Group export and various bug fixes

This commit is contained in:
Jack Andersen 2018-08-18 14:28:52 -10:00
parent fec074ad30
commit 08988fe3ec
35 changed files with 1528 additions and 291 deletions

View File

@ -1,8 +1,8 @@
#include "MIDIReader.hpp" #include "MIDIReader.hpp"
#include "MainWindow.hpp" #include "MainWindow.hpp"
MIDIReader::MIDIReader(amuse::Engine& engine, const char* name, bool useLock) MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock)
: amuse::BooBackendMIDIReader(engine, name, useLock) {} : amuse::BooBackendMIDIReader(engine, useLock) {}
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{ {
@ -46,7 +46,9 @@ void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
m_chanVoxs.erase(keySearch); m_chanVoxs.erase(keySearch);
} }
m_chanVoxs[key] = g_MainWindow->startEditorVoice(key, velocity); amuse::ObjToken<amuse::Voice> newVox = g_MainWindow->startEditorVoice(key, velocity);
if (newVox)
m_chanVoxs[key] = newVox;
} }
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {} void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
@ -120,10 +122,7 @@ VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: amuse::BooBackendVoiceAllocator(booEngine) {} : amuse::BooBackendVoiceAllocator(booEngine) {}
std::unique_ptr<amuse::IMIDIReader> std::unique_ptr<amuse::IMIDIReader>
VoiceAllocator::allocateMIDIReader(amuse::Engine& engine, const char* name) VoiceAllocator::allocateMIDIReader(amuse::Engine& engine)
{ {
std::unique_ptr<amuse::IMIDIReader> ret = std::make_unique<MIDIReader>(engine, name, m_booEngine.useMIDILock()); return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
if (!static_cast<MIDIReader&>(*ret).getMidiIn())
return {};
return ret;
} }

View File

@ -12,8 +12,7 @@ class MIDIReader : public amuse::BooBackendMIDIReader
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs; std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
amuse::ObjToken<amuse::Voice> m_lastVoice; amuse::ObjToken<amuse::Voice> m_lastVoice;
public: public:
MIDIReader(amuse::Engine& engine, const char* name, bool useLock); MIDIReader(amuse::Engine& engine, bool useLock);
boo::IMIDIIn* getMidiIn() const { return m_midiIn.get(); }
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity); void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity); void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
@ -47,7 +46,7 @@ class VoiceAllocator : public amuse::BooBackendVoiceAllocator
{ {
public: public:
VoiceAllocator(boo::IAudioVoiceEngine& booEngine); VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine, const char* name = nullptr); std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine);
}; };
#endif // AMUSE_MIDI_READER_HPP #endif // AMUSE_MIDI_READER_HPP

View File

@ -47,6 +47,8 @@ MainWindow::MainWindow(QWidget* parent)
m_ui.statusbar->connectFXPressed(this, SLOT(fxPressed())); m_ui.statusbar->connectFXPressed(this, SLOT(fxPressed()));
m_ui.statusbar->setVolumeValue(70); m_ui.statusbar->setVolumeValue(70);
m_ui.statusbar->connectVolumeSlider(this, SLOT(volumeChanged(int))); m_ui.statusbar->connectVolumeSlider(this, SLOT(volumeChanged(int)));
m_ui.statusbar->connectASlider(this, SLOT(auxAChanged(int)));
m_ui.statusbar->connectBSlider(this, SLOT(auxBChanged(int)));
m_ui.keyboardContents->setStatusFocus(new StatusBarFocus(m_ui.statusbar)); m_ui.keyboardContents->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
m_ui.velocitySlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar)); m_ui.velocitySlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
@ -299,12 +301,25 @@ bool MainWindow::setProjectPath(const QString& path)
void MainWindow::refreshAudioIO() void MainWindow::refreshAudioIO()
{ {
QList<QAction*> audioActions = m_ui.menuAudio->actions(); QList<QAction*> audioActions = m_ui.menuAudio->actions();
if (audioActions.size() > 3) if (audioActions.size() > 2)
for (auto it = audioActions.begin() + 3 ; it != audioActions.end() ; ++it) for (auto it = audioActions.begin() + 2 ; it != audioActions.end() ; ++it)
m_ui.menuAudio->removeAction(*it); m_ui.menuAudio->removeAction(*it);
bool addedDev = false; bool addedDev = false;
// TODO: Do if (m_voxEngine)
{
std::string curOut = m_voxEngine->getCurrentAudioOutput();
for (const auto& dev : m_voxEngine->enumerateAudioOutputs())
{
QAction* act = m_ui.menuAudio->addAction(QString::fromStdString(dev.second));
act->setCheckable(true);
act->setData(QString::fromStdString(dev.first));
if (curOut == dev.first)
act->setChecked(true);
connect(act, SIGNAL(triggered()), this, SLOT(setAudioIO()));
addedDev = true;
}
}
if (!addedDev) if (!addedDev)
m_ui.menuAudio->addAction(tr("No Audio Devices Found"))->setEnabled(false); m_ui.menuAudio->addAction(tr("No Audio Devices Found"))->setEnabled(false);
@ -320,11 +335,22 @@ void MainWindow::refreshMIDIIO()
bool addedDev = false; bool addedDev = false;
if (m_voxEngine) if (m_voxEngine)
{ {
for (const auto& dev : m_voxEngine->enumerateMIDIDevices()) if (m_voxEngine->supportsVirtualMIDIIn())
{
QAction* act = m_ui.menuMIDI->addAction(tr("Virtual MIDI-In"));
act->setCheckable(true);
act->setData(QStringLiteral("<amuse-virtual-in>"));
act->setChecked(static_cast<MIDIReader*>(m_engine->getMIDIReader())->hasVirtualIn());
connect(act, SIGNAL(triggered(bool)), this, SLOT(setMIDIIO(bool)));
addedDev = true;
}
for (const auto& dev : m_voxEngine->enumerateMIDIInputs())
{ {
QAction* act = m_ui.menuMIDI->addAction(QString::fromStdString(dev.second)); QAction* act = m_ui.menuMIDI->addAction(QString::fromStdString(dev.second));
act->setCheckable(true);
act->setData(QString::fromStdString(dev.first)); act->setData(QString::fromStdString(dev.first));
connect(act, SIGNAL(triggered()), this, SLOT(setMIDIIO())); act->setChecked(static_cast<MIDIReader*>(m_engine->getMIDIReader())->hasMIDIIn(dev.first.c_str()));
connect(act, SIGNAL(triggered(bool)), this, SLOT(setMIDIIO(bool)));
addedDev = true; addedDev = true;
} }
} }
@ -414,13 +440,13 @@ void MainWindow::keyReleaseEvent(QKeyEvent* ev)
setSustain(false); setSustain(false);
} }
void MainWindow::startBackgroundTask(const QString& windowTitle, const QString& label, void MainWindow::startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task) std::function<void(BackgroundTask&)>&& task)
{ {
assert(m_backgroundTask == nullptr && "existing background process"); assert(m_backgroundTask == nullptr && "existing background process");
setEnabled(false); setEnabled(false);
m_backgroundTask = new BackgroundTask(std::move(task)); m_backgroundTask = new BackgroundTask(id, std::move(task));
m_backgroundTask->moveToThread(&m_backgroundThread); m_backgroundTask->moveToThread(&m_backgroundThread);
m_backgroundDialog = new QProgressDialog(this); m_backgroundDialog = new QProgressDialog(this);
@ -442,8 +468,8 @@ void MainWindow::startBackgroundTask(const QString& windowTitle, const QString&
m_backgroundDialog, SLOT(setValue(int)), Qt::QueuedConnection); m_backgroundDialog, SLOT(setValue(int)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(setLabelText(const QString&)), connect(m_backgroundTask, SIGNAL(setLabelText(const QString&)),
m_backgroundDialog, SLOT(setLabelText(const QString&)), Qt::QueuedConnection); m_backgroundDialog, SLOT(setLabelText(const QString&)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(finished()), connect(m_backgroundTask, SIGNAL(finished(int)),
this, SLOT(onBackgroundTaskFinished()), Qt::QueuedConnection); this, SLOT(onBackgroundTaskFinished(int)), Qt::QueuedConnection);
m_backgroundDialog->open(m_backgroundTask, SLOT(cancel())); m_backgroundDialog->open(m_backgroundTask, SLOT(cancel()));
connectMessenger(&m_backgroundTask->uiMessenger(), Qt::BlockingQueuedConnection); connectMessenger(&m_backgroundTask->uiMessenger(), Qt::BlockingQueuedConnection);
@ -579,6 +605,8 @@ amuse::ObjToken<amuse::Voice> MainWindow::startEditorVoice(uint8_t key, uint8_t
vox->setPedal(m_ctrlVals[64] >= 0x40); vox->setPedal(m_ctrlVals[64] >= 0x40);
vox->setPitchWheel(m_pitch); vox->setPitchWheel(m_pitch);
vox->installCtrlValues(m_ctrlVals); vox->installCtrlValues(m_ctrlVals);
vox->setReverbVol(m_auxAVol);
vox->setAuxBVol(m_auxBVol);
} }
} }
return vox; return vox;
@ -589,7 +617,10 @@ amuse::ObjToken<amuse::Voice> MainWindow::startSFX(amuse::GroupId groupId, amuse
if (ProjectModel::INode* node = getEditorNode()) if (ProjectModel::INode* node = getEditorNode())
{ {
amuse::AudioGroupDatabase* group = projectModel()->getGroupNode(node)->getAudioGroup(); amuse::AudioGroupDatabase* group = projectModel()->getGroupNode(node)->getAudioGroup();
return m_engine->fxStart(group, groupId, sfxId, 1.f, 0.f); auto ret = m_engine->fxStart(group, groupId, sfxId, 1.f, 0.f);
ret->setReverbVol(m_auxAVol);
ret->setAuxBVol(m_auxBVol);
return ret;
} }
return {}; return {};
} }
@ -600,7 +631,13 @@ amuse::ObjToken<amuse::Sequencer> MainWindow::startSong(amuse::GroupId groupId,
if (ProjectModel::INode* node = getEditorNode()) if (ProjectModel::INode* node = getEditorNode())
{ {
amuse::AudioGroupDatabase* group = projectModel()->getGroupNode(node)->getAudioGroup(); amuse::AudioGroupDatabase* group = projectModel()->getGroupNode(node)->getAudioGroup();
return m_engine->seqPlay(group, groupId, songId, arrData); auto ret = m_engine->seqPlay(group, groupId, songId, arrData);
for (uint8_t i = 0; i < 16; ++i)
{
ret->setCtrlValue(i, 0x5b, int8_t(m_auxAVol * 127.f));
ret->setCtrlValue(i, 0x5d, int8_t(m_auxBVol * 127.f));
}
return ret;
} }
return {}; return {};
} }
@ -702,7 +739,7 @@ bool MainWindow::openProject(const QString& path)
return false; return false;
ProjectModel* model = m_projectModel; ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Opening"), tr("Scanning Project"), startBackgroundTask(TaskOpen, tr("Opening"), tr("Scanning Project"),
[dir, model](BackgroundTask& task) [dir, model](BackgroundTask& task)
{ {
QStringList childDirs = dir.entryList(QDir::Dirs); QStringList childDirs = dir.entryList(QDir::Dirs);
@ -710,6 +747,8 @@ bool MainWindow::openProject(const QString& path)
{ {
if (task.isCanceled()) if (task.isCanceled())
return; return;
if (chDir == QStringLiteral("out"))
continue;
QString chPath = dir.filePath(chDir); QString chPath = dir.filePath(chDir);
if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() && if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() &&
QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists()) QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists())
@ -785,7 +824,7 @@ void MainWindow::reloadSampleDataAction()
if (!dir.exists()) if (!dir.exists())
return; return;
startBackgroundTask(tr("Reloading Samples"), tr("Scanning Project"), startBackgroundTask(TaskReloadSamples, tr("Reloading Samples"), tr("Scanning Project"),
[dir, model](BackgroundTask& task) [dir, model](BackgroundTask& task)
{ {
QStringList childDirs = dir.entryList(QDir::Dirs); QStringList childDirs = dir.entryList(QDir::Dirs);
@ -793,6 +832,8 @@ void MainWindow::reloadSampleDataAction()
{ {
if (task.isCanceled()) if (task.isCanceled())
return; return;
if (chDir == QStringLiteral("out"))
continue;
QString chPath = dir.filePath(chDir); QString chPath = dir.filePath(chDir);
if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() && if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() &&
QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists()) QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists())
@ -869,7 +910,7 @@ void MainWindow::importAction()
} }
ProjectModel* model = m_projectModel; ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Importing"), tr("Scanning Project"), startBackgroundTask(TaskImport, tr("Importing"), tr("Scanning Project"),
[model, path, importMode](BackgroundTask& task) [model, path, importMode](BackgroundTask& task)
{ {
QDir dir = QFileInfo(path).dir(); QDir dir = QFileInfo(path).dir();
@ -914,7 +955,7 @@ void MainWindow::importAction()
} }
ProjectModel* model = m_projectModel; ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Importing"), tr("Scanning Project"), startBackgroundTask(TaskImport, tr("Importing"), tr("Scanning Project"),
[model, path, importMode](BackgroundTask& task) [model, path, importMode](BackgroundTask& task)
{ {
/* Handle single container */ /* Handle single container */
@ -949,7 +990,32 @@ void MainWindow::importSongsAction()
void MainWindow::exportAction() void MainWindow::exportAction()
{ {
if (!m_projectModel)
return;
QFileInfo dirInfo(m_projectModel->dir(), QStringLiteral("out"));
if (!MkPath(dirInfo.filePath(), m_mainMessenger))
return;
QDir dir(dirInfo.filePath());
ProjectModel* model = m_projectModel;
startBackgroundTask(BackgroundTaskId::TaskExport, tr("Exporting"), tr("Scanning Project"),
[model, dir](BackgroundTask& task)
{
QStringList groupList = model->getGroupList();
task.setMaximum(groupList.size());
int curVal = 0;
for (QString group : groupList)
{
task.setLabelText(tr("Exporting %1").arg(group));
if (task.isCanceled())
return;
if (!model->exportGroup(dir.path(), group, task.uiMessenger()))
return;
task.setValue(++curVal);
}
});
} }
bool TreeDelegate::editorEvent(QEvent* event, bool TreeDelegate::editorEvent(QEvent* event,
@ -1177,13 +1243,32 @@ void MainWindow::aboutToShowMIDIIOMenu()
void MainWindow::setAudioIO() void MainWindow::setAudioIO()
{ {
// TODO: Do QByteArray devName = qobject_cast<QAction*>(sender())->data().toString().toUtf8();
if (m_voxEngine)
m_voxEngine->setCurrentAudioOutput(devName.data());
} }
void MainWindow::setMIDIIO() void MainWindow::setMIDIIO(bool checked)
{ {
// TODO: Do QAction* action = qobject_cast<QAction*>(sender());
//qobject_cast<QAction*>(sender())->data().toString().toUtf8().constData(); QByteArray devName = action->data().toString().toUtf8();
if (m_voxEngine)
{
MIDIReader* mr = static_cast<MIDIReader*>(m_engine->getMIDIReader());
if (devName == "<amuse-virtual-in>")
{
mr->setVirtualIn(checked);
action->setChecked(mr->hasVirtualIn());
}
else
{
if (checked)
mr->addMIDIIn(devName.data());
else
mr->removeMIDIIn(devName.data());
action->setChecked(mr->hasMIDIIn(devName.data()));
}
}
} }
void MainWindow::notePressed(int key) void MainWindow::notePressed(int key)
@ -1243,6 +1328,26 @@ void MainWindow::volumeChanged(int vol)
m_engine->setVolume(vol / 100.f); m_engine->setVolume(vol / 100.f);
} }
void MainWindow::auxAChanged(int vol)
{
m_auxAVol = vol / 100.f;
for (auto& vox : m_engine->getActiveVoices())
vox->setReverbVol(m_auxAVol);
for (auto& seq : m_engine->getActiveSequencers())
for (uint8_t i = 0; i < 16; ++i)
seq->setCtrlValue(i, 0x5b, int8_t(m_auxAVol * 127.f));
}
void MainWindow::auxBChanged(int vol)
{
m_auxBVol = vol / 100.f;
for (auto& vox : m_engine->getActiveVoices())
vox->setAuxBVol(m_auxBVol);
for (auto& seq : m_engine->getActiveSequencers())
for (uint8_t i = 0; i < 16; ++i)
seq->setCtrlValue(i, 0x5d, int8_t(m_auxBVol * 127.f));
}
void MainWindow::outlineCutAction() void MainWindow::outlineCutAction()
{ {
@ -1399,16 +1504,32 @@ void MainWindow::studioSetupShown()
m_ui.statusbar->setFXDown(true); m_ui.statusbar->setFXDown(true);
} }
void MainWindow::onBackgroundTaskFinished() void MainWindow::onBackgroundTaskFinished(int id)
{ {
m_backgroundDialog->reset(); m_backgroundDialog->reset();
m_backgroundDialog->deleteLater(); m_backgroundDialog->deleteLater();
m_backgroundDialog = nullptr; m_backgroundDialog = nullptr;
m_backgroundTask->deleteLater(); m_backgroundTask->deleteLater();
m_backgroundTask = nullptr; m_backgroundTask = nullptr;
bool hasGroups = m_projectModel->ensureModelData();
m_ui.actionImport_Groups->setDisabled(hasGroups); if (id == TaskExport)
m_ui.actionImport_Songs->setEnabled(hasGroups); {
if (m_mainMessenger.question(tr("Export Complete"), tr("%1?").
arg(ShowInGraphicalShellString())) == QMessageBox::Yes)
{
QFileInfo dirInfo(m_projectModel->dir(), QStringLiteral("out"));
QDir dir(dirInfo.filePath());
QStringList entryList = dir.entryList(QDir::Files);
ShowInGraphicalShell(this, entryList.empty() ? dirInfo.filePath() : QFileInfo(dir, entryList.first()).filePath());
}
}
else
{
bool hasGroups = m_projectModel->ensureModelData();
m_ui.actionImport_Groups->setDisabled(hasGroups);
m_ui.actionImport_Songs->setEnabled(hasGroups);
}
setEnabled(true); setEnabled(true);
} }

View File

@ -32,15 +32,24 @@ class KeymapEditor;
class LayersEditor; class LayersEditor;
class SampleEditor; class SampleEditor;
enum BackgroundTaskId
{
TaskOpen,
TaskImport,
TaskExport,
TaskReloadSamples
};
class BackgroundTask : public QObject class BackgroundTask : public QObject
{ {
Q_OBJECT Q_OBJECT
int m_id;
std::function<void(BackgroundTask&)> m_task; std::function<void(BackgroundTask&)> m_task;
UIMessenger m_threadMessenger; UIMessenger m_threadMessenger;
bool m_cancelled = false; bool m_cancelled = false;
public: public:
explicit BackgroundTask(std::function<void(BackgroundTask&)>&& task) explicit BackgroundTask(int id, std::function<void(BackgroundTask&)>&& task)
: m_task(std::move(task)), m_threadMessenger(this) {} : m_id(id), m_task(std::move(task)), m_threadMessenger(this) {}
bool isCanceled() const { QCoreApplication::processEvents(); return m_cancelled; } bool isCanceled() const { QCoreApplication::processEvents(); return m_cancelled; }
UIMessenger& uiMessenger() { return m_threadMessenger; } UIMessenger& uiMessenger() { return m_threadMessenger; }
@ -49,10 +58,10 @@ signals:
void setMaximum(int maximum); void setMaximum(int maximum);
void setValue(int value); void setValue(int value);
void setLabelText(const QString& text); void setLabelText(const QString& text);
void finished(); void finished(int id);
public slots: public slots:
void run() { m_task(*this); emit finished(); } void run() { m_task(*this); emit finished(m_id); }
void cancel() { m_cancelled = true; } void cancel() { m_cancelled = true; }
}; };
@ -99,6 +108,8 @@ class MainWindow : public QMainWindow
int m_velocity = 90; int m_velocity = 90;
float m_pitch = 0.f; float m_pitch = 0.f;
int8_t m_ctrlVals[128] = {}; int8_t m_ctrlVals[128] = {};
float m_auxAVol = 0.f;
float m_auxBVol = 0.f;
bool m_uiDisabled = false; bool m_uiDisabled = false;
QUndoStack* m_undoStack; QUndoStack* m_undoStack;
@ -125,7 +136,7 @@ class MainWindow : public QMainWindow
void keyPressEvent(QKeyEvent* ev); void keyPressEvent(QKeyEvent* ev);
void keyReleaseEvent(QKeyEvent* ev); void keyReleaseEvent(QKeyEvent* ev);
void startBackgroundTask(const QString& windowTitle, const QString& label, void startBackgroundTask(int id, const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task); std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget); bool _setEditor(EditorWidget* widget);
@ -193,7 +204,7 @@ public slots:
void aboutToShowMIDIIOMenu(); void aboutToShowMIDIIOMenu();
void setAudioIO(); void setAudioIO();
void setMIDIIO(); void setMIDIIO(bool checked);
void notePressed(int key); void notePressed(int key);
void noteReleased(); void noteReleased();
@ -203,6 +214,8 @@ public slots:
void killSounds(); void killSounds();
void fxPressed(); void fxPressed();
void volumeChanged(int vol); void volumeChanged(int vol);
void auxAChanged(int vol);
void auxBChanged(int vol);
void outlineCutAction(); void outlineCutAction();
void outlineCopyAction(); void outlineCopyAction();
@ -222,7 +235,7 @@ public slots:
void studioSetupHidden(); void studioSetupHidden();
void studioSetupShown(); void studioSetupShown();
void onBackgroundTaskFinished(); void onBackgroundTaskFinished(int id);
QMessageBox::StandardButton msgInformation(const QString &title, QMessageBox::StandardButton msgInformation(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,

View File

@ -581,6 +581,48 @@ bool ProjectModel::saveToFile(UIMessenger& messenger)
return true; return true;
} }
QStringList ProjectModel::getGroupList() const
{
QStringList list;
list.reserve(m_root->childCount());
m_root->oneLevelTraverse([&list](INode* node)
{
list.push_back(node->name());
return true;
});
return list;
}
bool ProjectModel::exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const
{
auto search = m_groups.find(groupName);
if (search == m_groups.cend())
{
messenger.critical(tr("Export Error"), tr("Unable to find group %1").arg(groupName));
return false;
}
const amuse::AudioGroupDatabase& group = search->second;
m_projectDatabase.setIdDatabases();
auto basePath = QStringToSysString(QFileInfo(QDir(path), groupName).filePath());
group.setIdDatabases();
if (!group.getProj().toGCNData(basePath, group.getPool(), group.getSdir()))
{
messenger.critical(tr("Export Error"), tr("Unable to export %1.proj").arg(groupName));
return false;
}
if (!group.getPool().toData<athena::Big>(basePath))
{
messenger.critical(tr("Export Error"), tr("Unable to export %1.pool").arg(groupName));
return false;
}
if (!group.getSdir().toGCNData(basePath, group))
{
messenger.critical(tr("Export Error"), tr("Unable to export %1.sdir").arg(groupName));
return false;
}
return true;
}
void ProjectModel::_buildGroupNode(GroupNode& gn) void ProjectModel::_buildGroupNode(GroupNode& gn)
{ {
amuse::AudioGroup& group = gn.m_it->second; amuse::AudioGroup& group = gn.m_it->second;

View File

@ -363,6 +363,8 @@ public:
ImportMode mode, UIMessenger& messenger); ImportMode mode, UIMessenger& messenger);
void saveSongsIndex(); void saveSongsIndex();
bool saveToFile(UIMessenger& messenger); bool saveToFile(UIMessenger& messenger);
QStringList getGroupList() const;
bool exportGroup(const QString& path, const QString& groupName, UIMessenger& messenger) const;
bool ensureModelData(); bool ensureModelData();

View File

@ -1150,7 +1150,7 @@ SetupTableView::SetupTableView(QWidget* parent)
void ColoredTabBarStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, void ColoredTabBarStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const QPainter *painter, const QWidget *widget) const
{ {
if (element == QStyle::CE_TabBarTab) if (qobject_cast<const ColoredTabBar*>(widget) && element == QStyle::CE_TabBarTab)
{ {
QStyleOptionTab optionTab = *static_cast<const QStyleOptionTab*>(option); QStyleOptionTab optionTab = *static_cast<const QStyleOptionTab*>(option);
switch (optionTab.position) switch (optionTab.position)
@ -1171,10 +1171,9 @@ void ColoredTabBarStyle::drawControl(QStyle::ControlElement element, const QStyl
} }
ColoredTabBar::ColoredTabBar(QWidget* parent) ColoredTabBar::ColoredTabBar(QWidget* parent)
: QTabBar(parent), m_style(new ColoredTabBarStyle(style())) : QTabBar(parent)
{ {
setDrawBase(false); setDrawBase(false);
setStyle(m_style);
} }
ColoredTabWidget::ColoredTabWidget(QWidget* parent) ColoredTabWidget::ColoredTabWidget(QWidget* parent)

View File

@ -206,7 +206,6 @@ public:
class ColoredTabBar : public QTabBar class ColoredTabBar : public QTabBar
{ {
Q_OBJECT Q_OBJECT
ColoredTabBarStyle* m_style;
public: public:
explicit ColoredTabBar(QWidget* parent = Q_NULLPTR); explicit ColoredTabBar(QWidget* parent = Q_NULLPTR);
}; };

View File

@ -8,7 +8,8 @@ FXButton::FXButton(QWidget* parent)
} }
StatusBarWidget::StatusBarWidget(QWidget* parent) StatusBarWidget::StatusBarWidget(QWidget* parent)
: QStatusBar(parent), m_volumeSlider(Qt::Horizontal) : QStatusBar(parent), m_volumeSlider(Qt::Horizontal),
m_aSlider(Qt::Horizontal), m_bSlider(Qt::Horizontal)
{ {
addWidget(&m_normalMessage); addWidget(&m_normalMessage);
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg"))); m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
@ -19,14 +20,35 @@ StatusBarWidget::StatusBarWidget(QWidget* parent)
m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1")); m_volumeIcons[1] = QIcon(QStringLiteral(":/icons/IconVolume1"));
m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2")); m_volumeIcons[2] = QIcon(QStringLiteral(":/icons/IconVolume2"));
m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3")); m_volumeIcons[3] = QIcon(QStringLiteral(":/icons/IconVolume3"));
m_aIcon.setFixedSize(16, 16);
m_aIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconA.svg")).pixmap(16, 16));
QString aTip = tr("Aux A send level for all voices");
m_aIcon.setToolTip(aTip);
m_aSlider.setRange(0, 100);
m_aSlider.setFixedWidth(100);
m_aSlider.setToolTip(aTip);
m_bIcon.setFixedSize(16, 16);
m_bIcon.setPixmap(QIcon(QStringLiteral(":/icons/IconB.svg")).pixmap(16, 16));
QString bTip = tr("Aux B send level for all voices");
m_bIcon.setToolTip(bTip);
m_bSlider.setRange(0, 100);
m_bSlider.setFixedWidth(100);
m_bSlider.setToolTip(bTip);
m_volumeIcon.setFixedSize(16, 16); m_volumeIcon.setFixedSize(16, 16);
m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16)); m_volumeIcon.setPixmap(m_volumeIcons[0].pixmap(16, 16));
QString volTip = tr("Master volume level");
m_volumeIcon.setToolTip(volTip);
connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); connect(&m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
m_volumeSlider.setRange(0, 100); m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100); m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount); addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton); addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton); addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon); addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider); addPermanentWidget(&m_volumeSlider);
} }

View File

@ -31,6 +31,10 @@ class StatusBarWidget : public QStatusBar
QIcon m_volumeIcons[4]; QIcon m_volumeIcons[4];
QLabel m_volumeIcon; QLabel m_volumeIcon;
QSlider m_volumeSlider; QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0; int m_lastVolIdx = 0;
QLabel m_voiceCount; QLabel m_voiceCount;
int m_cachedVoiceCount = -1; int m_cachedVoiceCount = -1;
@ -47,6 +51,10 @@ public:
void setFXDown(bool down) { m_fxButton.setDown(down); } void setFXDown(bool down) { m_fxButton.setDown(down); }
void connectVolumeSlider(const QObject* receiver, const char* method) void connectVolumeSlider(const QObject* receiver, const char* method)
{ connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method); } { connect(&m_volumeSlider, SIGNAL(valueChanged(int)), receiver, method); }
void connectASlider(const QObject* receiver, const char* method)
{ connect(&m_aSlider, SIGNAL(valueChanged(int)), receiver, method); }
void connectBSlider(const QObject* receiver, const char* method)
{ connect(&m_bSlider, SIGNAL(valueChanged(int)), receiver, method); }
void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); } void setVolumeValue(int vol) { m_volumeSlider.setValue(vol); }
private slots: private slots:

View File

@ -4,6 +4,7 @@
#include "amuse/EffectReverb.hpp" #include "amuse/EffectReverb.hpp"
#include <QPainter> #include <QPainter>
#include <QScrollBar> #include <QScrollBar>
#include <QStylePainter>
using namespace std::literals; using namespace std::literals;
@ -251,6 +252,90 @@ static void SetEffectParm(amuse::EffectBaseTypeless* effect, int idx, int chanId
} }
} }
static const char* ChanNames[] =
{
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Left"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Right"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Left"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Rear Right"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Front Center"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "LFE"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Left"),
QT_TRANSLATE_NOOP("Uint32X8Popup", "Side Right")
};
Uint32X8Popup::Uint32X8Popup(int min, int max, QWidget* parent)
: QFrame(parent, Qt::Popup)
{
setAttribute(Qt::WA_WindowPropagation);
setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
Uint32X8Button* combo = static_cast<Uint32X8Button*>(parent);
QStyleOptionComboBox opt = combo->comboStyleOption();
setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
QGridLayout* layout = new QGridLayout;
for (int i = 0; i < 8; ++i)
{
layout->addWidget(new QLabel(tr(ChanNames[i])), i, 0);
FieldSlider* slider = new FieldSlider;
m_sliders[i] = slider;
slider->setToolTip(QStringLiteral("[%1,%2]").arg(min).arg(max));
slider->setProperty("chanIdx", i);
slider->setRange(min, max);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(doValueChanged(int)));
layout->addWidget(slider, i, 1);
}
setLayout(layout);
}
void Uint32X8Popup::setValue(int chanIdx, int val)
{
m_sliders[chanIdx]->setValue(val);
}
void Uint32X8Popup::doValueChanged(int val)
{
FieldSlider* slider = static_cast<FieldSlider*>(sender());
int chanIdx = slider->property("chanIdx").toInt();
emit valueChanged(chanIdx, val);
}
Uint32X8Button::Uint32X8Button(int min, int max, QWidget* parent)
: QPushButton(parent), m_popup(new Uint32X8Popup(min, max, this))
{
connect(this, SIGNAL(pressed()), this, SLOT(onPressed()));
}
void Uint32X8Button::paintEvent(QPaintEvent*)
{
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt = comboStyleOption();
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
QStyleOptionComboBox Uint32X8Button::comboStyleOption() const
{
QStyleOptionComboBox opt;
opt.initFrom(this);
opt.editable = false;
opt.frame = true;
opt.currentText = tr("Channels");
return opt;
}
void Uint32X8Button::onPressed()
{
QPoint pt = parentWidget()->mapToGlobal(pos());
m_popup->move(pt.x(), pt.y());
m_popup->show();
}
EffectWidget::EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType type) EffectWidget::EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType type)
: QWidget(nullptr), m_effect(effect), m_introspection(GetEffectIntrospection(type)) : QWidget(nullptr), m_effect(effect), m_introspection(GetEffectIntrospection(type))
{ {
@ -258,8 +343,6 @@ EffectWidget::EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType
titleFont.setWeight(QFont::Bold); titleFont.setWeight(QFont::Bold);
m_titleLabel.setFont(titleFont); m_titleLabel.setFont(titleFont);
m_titleLabel.setForegroundRole(QPalette::Background); m_titleLabel.setForegroundRole(QPalette::Background);
//m_titleLabel.setAutoFillBackground(true);
//m_titleLabel.setBackgroundRole(QPalette::Text);
m_titleLabel.setContentsMargins(46, 0, 0, 0); m_titleLabel.setContentsMargins(46, 0, 0, 0);
m_titleLabel.setFixedHeight(20); m_titleLabel.setFixedHeight(20);
m_numberText.setTextOption(QTextOption(Qt::AlignRight)); m_numberText.setTextOption(QTextOption(Qt::AlignRight));
@ -315,6 +398,16 @@ EffectWidget::EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType
layout->addWidget(sb, 1, f); layout->addWidget(sb, 1, f);
break; break;
} }
case EffectIntrospection::Field::Type::UInt32x8:
{
Uint32X8Button* sb = new Uint32X8Button(int(field.m_min), int(field.m_max));
sb->popup()->setProperty("fieldIndex", f);
for (int i = 0; i < 8; ++i)
sb->popup()->setValue(i, GetEffectParm<uint32_t>(m_effect, f, i));
connect(sb->popup(), SIGNAL(valueChanged(int, int)), this, SLOT(chanNumChanged(int, int)));
layout->addWidget(sb, 1, f);
break;
}
case EffectIntrospection::Field::Type::Float: case EffectIntrospection::Field::Type::Float:
{ {
FieldDoubleSlider* sb = new FieldDoubleSlider; FieldDoubleSlider* sb = new FieldDoubleSlider;
@ -397,6 +490,11 @@ void EffectWidget::numChanged(double value)
SetEffectParm<float>(m_effect, sender()->property("fieldIndex").toInt(), 0, value); SetEffectParm<float>(m_effect, sender()->property("fieldIndex").toInt(), 0, value);
} }
void EffectWidget::chanNumChanged(int chanIdx, int value)
{
SetEffectParm<uint32_t>(m_effect, sender()->property("fieldIndex").toInt(), chanIdx, value);
}
void EffectWidget::deleteClicked() void EffectWidget::deleteClicked()
{ {
if (m_index != -1) if (m_index != -1)

View File

@ -13,6 +13,8 @@
#include <QSplitter> #include <QSplitter>
#include "amuse/Studio.hpp" #include "amuse/Studio.hpp"
class EffectListing;
struct EffectIntrospection struct EffectIntrospection
{ {
struct Field struct Field
@ -34,7 +36,31 @@ struct EffectIntrospection
Field m_fields[7]; Field m_fields[7];
}; };
class EffectListing; class Uint32X8Popup : public QFrame
{
Q_OBJECT
FieldSlider* m_sliders[8];
public:
explicit Uint32X8Popup(int min, int max, QWidget* parent = Q_NULLPTR);
void setValue(int chanIdx, int val);
private slots:
void doValueChanged(int val);
signals:
void valueChanged(int chanIdx, int val);
};
class Uint32X8Button : public QPushButton
{
Q_OBJECT
Uint32X8Popup* m_popup;
public:
explicit Uint32X8Button(int min, int max, QWidget* parent = Q_NULLPTR);
void paintEvent(QPaintEvent* event);
Uint32X8Popup* popup() const { return m_popup; }
QStyleOptionComboBox comboStyleOption() const;
private slots:
void onPressed();
};
class EffectWidget : public QWidget class EffectWidget : public QWidget
{ {
@ -51,13 +77,14 @@ class EffectWidget : public QWidget
private slots: private slots:
void numChanged(int); void numChanged(int);
void numChanged(double); void numChanged(double);
void chanNumChanged(int, int);
void deleteClicked(); void deleteClicked();
private: private:
EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType type); explicit EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType type);
public: public:
EffectListing* getParent() const; EffectListing* getParent() const;
EffectWidget(amuse::EffectBaseTypeless* effect); explicit EffectWidget(amuse::EffectBaseTypeless* effect);
EffectWidget(amuse::EffectType op); explicit EffectWidget(amuse::EffectType op);
void paintEvent(QPaintEvent* event); void paintEvent(QPaintEvent* event);
QString getText() const { return m_titleLabel.text(); } QString getText() const { return m_titleLabel.text(); }
}; };

View File

@ -3,6 +3,7 @@
#include <QStyleFactory> #include <QStyleFactory>
#include <QTranslator> #include <QTranslator>
#include "MainWindow.hpp" #include "MainWindow.hpp"
#include "SongGroupEditor.hpp"
#include "boo/IApplication.hpp" #include "boo/IApplication.hpp"
#include <QResource> #include <QResource>
#include <QCommandLineParser> #include <QCommandLineParser>
@ -61,7 +62,7 @@ int main(int argc, char* argv[])
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif #endif
QApplication::setStyle(QStyleFactory::create("Fusion")); QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
QApplication a(argc, argv); QApplication a(argc, argv);
QApplication::setWindowIcon(MakeAppIcon()); QApplication::setWindowIcon(MakeAppIcon());

View File

@ -0,0 +1,79 @@
<?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="20"
height="20"
viewBox="0 0 5.2916665 5.2916668"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconA.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="42.15"
inkscape:cx="10.765381"
inkscape:cy="12.799167"
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"
gridtolerance="10"
showguides="false"
objecttolerance="17">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<g
aria-label="A"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.33670712px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.37563241"
id="text1395">
<path
d="m 0.04443603,296.98746 0.05869366,-0.30814 h 0.13939743 q 0.13939744,0 0.24211134,-0.044 0.1027139,-0.044 0.19809109,-0.16875 0.1027139,-0.12472 0.24211133,-0.3595 l 2.55317412,-4.358 h 0.5722631 l 0.6969872,4.54142 q 0.036684,0.22744 0.124724,0.30814 0.095377,0.0807 0.3081417,0.0807 h 0.095377 l -0.058694,0.30814 H 3.2212302 l 0.058694,-0.30814 h 0.1687442 q 0.2347747,0 0.3815088,-0.0954 0.1467341,-0.10272 0.1467341,-0.2788 0,-0.0587 -0.00734,-0.12472 0,-0.0734 -0.00734,-0.12472 l -0.117381,-0.82171 H 1.9226331 l -0.4475392,0.77036 q -0.1393974,0.25678 -0.1393974,0.41085 0,0.26413 0.4108556,0.26413 h 0.1687442 l -0.058694,0.30814 z m 2.87598917,-3.47027 -0.7850276,1.34996 h 1.6580958 l -0.1834177,-1.28392 q -0.04402,-0.32282 -0.080704,-0.60895 -0.029347,-0.28613 -0.036684,-0.55025 -0.1173873,0.26412 -0.2494481,0.50623 -0.124724,0.24211 -0.3228144,0.58693 z"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Italic';fill:#ffffff;fill-opacity:1;stroke-width:1.37563241"
id="path1397"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,79 @@
<?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="20"
height="20"
viewBox="0 0 5.2916665 5.2916668"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconB.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="42.15"
inkscape:cx="10.765381"
inkscape:cy="12.799167"
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"
gridtolerance="10"
showguides="false"
objecttolerance="17">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<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
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-291.70832)">
<g
aria-label="B"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.34962893px;line-height:1.25;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.37805545"
id="text1421">
<path
d="m 0.31920594,296.97952 0.058797,-0.30868 h 0.0955452 q 0.24988739,0 0.44832737,-0.0882 0.19843999,-0.0955 0.27193629,-0.44097 l 0.7643614,-3.60132 q 0.029398,-0.1176 0.029398,-0.20579 0,-0.19109 -0.1616918,-0.24254 -0.1543422,-0.0514 -0.3674815,-0.0514 h -0.095545 l 0.066147,-0.30868 h 1.9035539 q 0.8452073,0 1.2788354,0.27928 0.4409778,0.27929 0.4409778,0.82316 0,0.40423 -0.1616919,0.68352 -0.1543422,0.27193 -0.4262784,0.44098 -0.2719363,0.16904 -0.6100193,0.24253 l -0.00735,0.0294 q 0.39688,0.0735 0.6467674,0.34543 0.2498873,0.26459 0.2498873,0.72762 0,0.80846 -0.5365229,1.24208 -0.5365229,0.43363 -1.5801702,0.43363 z m 2.05789606,-2.9325 h 0.5659215 q 0.6908651,0 0.9995495,-0.28663 0.3086844,-0.29399 0.3086844,-0.83051 0,-0.83051 -1.0142488,-0.83051 H 2.7960309 Z m -0.5438725,2.56502 h 0.712914 q 0.7570118,0 1.0803955,-0.34543 0.3233836,-0.35278 0.3233836,-1.01425 0,-0.83786 -1.028948,-0.83786 H 2.3036057 Z"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif Italic';fill:#ffffff;fill-opacity:1;stroke-width:1.37805545"
id="path1423"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -9,9 +9,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16" width="20"
height="16" height="20"
viewBox="0 0 4.2333332 4.2333335" viewBox="0 0 5.2916665 5.2916669"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11" inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,10 +63,10 @@
inkscape:label="Layer 1" inkscape:label="Layer 1"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-292.76665)"> transform="translate(0,-291.70832)">
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.3028571;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.37857139;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.26458333,293.82498 0.009775,2.11667 H 1.0583333 l 1.3229167,1.05833 v -4.23333 l -1.3229167,1.05833 z" d="m 0.26458333,293.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841" id="path841"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" /> sodipodi:nodetypes="ccccccc" />

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -9,9 +9,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16" width="20"
height="16" height="20"
viewBox="0 0 4.2333332 4.2333335" viewBox="0 0 5.2916665 5.2916669"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11" inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,16 +63,16 @@
inkscape:label="Layer 1" inkscape:label="Layer 1"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-292.76665)"> transform="translate(0,-291.70832)">
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.3028571;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.37857139;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.26458333,293.82498 0.009775,2.11667 H 1.0583333 l 1.3229167,1.05833 v -4.23333 l -1.3229167,1.05833 z" d="m 0.26458333,293.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841" id="path841"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" /> sodipodi:nodetypes="ccccccc" />
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2.6458333,294.08957 v 1.5875 h 0.2645834 v -1.5875 z" d="m 3.2411458,293.36197 v 1.98437 h 0.3307293 v -1.98437 z"
id="path825" id="path825"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
</g> </g>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -9,9 +9,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16" width="20"
height="16" height="20"
viewBox="0 0 4.2333332 4.2333335" viewBox="0 0 5.2916665 5.2916669"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11" inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,21 +63,21 @@
inkscape:label="Layer 1" inkscape:label="Layer 1"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-292.76665)"> transform="translate(0,-291.70832)">
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.3028571;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.37857139;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.26458333,293.82498 0.009775,2.11667 H 1.0583333 l 1.3229167,1.05833 v -4.23333 l -1.3229167,1.05833 z" d="m 0.26458333,293.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841" id="path841"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" /> sodipodi:nodetypes="ccccccc" />
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2.6458333,294.08957 v 1.5875 h 0.2645834 v -1.5875 z" d="m 3.2411458,293.36197 v 1.98437 h 0.3307293 v -1.98437 z"
id="path825" id="path825"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.175,293.5604 v 2.64583 h 0.2645833 v -2.64583 z" d="m 3.9026042,292.7005 v 3.30729 h 0.3307292 v -3.30729 z"
id="path827" id="path827"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> sodipodi:nodetypes="ccccc" />

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -9,9 +9,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16" width="20"
height="16" height="20"
viewBox="0 0 4.2333332 4.2333335" viewBox="0 0 5.2916665 5.2916669"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11" inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,27 +63,27 @@
inkscape:label="Layer 1" inkscape:label="Layer 1"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-292.76665)"> transform="translate(0,-291.70832)">
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.3028571;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.37857139;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0.26458333,293.82498 0.009775,2.11667 H 1.0583333 l 1.3229167,1.05833 v -4.23333 l -1.3229167,1.05833 z" d="m 0.26458333,293.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841" id="path841"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" /> sodipodi:nodetypes="ccccccc" />
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2.6458333,294.08957 v 1.5875 h 0.2645834 v -1.5875 z" d="m 3.2411458,293.36197 v 1.98437 h 0.3307293 v -1.98437 z"
id="path825" id="path825"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.175,293.5604 v 2.64583 h 0.2645833 v -2.64583 z" d="m 3.9026042,292.7005 v 3.30729 h 0.3307292 v -3.30729 z"
id="path827" id="path827"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" /> sodipodi:nodetypes="ccccc" />
<path <path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.7041666,293.03123 v 3.70417 H 3.96875 v -3.70417 z" d="m 4.5640625,292.03904 v 4.63021 h 0.3307292 v -4.63021 z"
id="path829" id="path829"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
</g> </g>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -149,26 +149,26 @@
<context> <context>
<name>EffectCatalogue</name> <name>EffectCatalogue</name>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="779"/> <location filename="../StudioSetupWidget.cpp" line="877"/>
<location filename="../StudioSetupWidget.cpp" line="787"/> <location filename="../StudioSetupWidget.cpp" line="885"/>
<source>Reverb Standard</source> <source>Reverb Standard</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="780"/> <location filename="../StudioSetupWidget.cpp" line="878"/>
<location filename="../StudioSetupWidget.cpp" line="788"/> <location filename="../StudioSetupWidget.cpp" line="886"/>
<source>Reverb High</source> <source>Reverb High</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="781"/> <location filename="../StudioSetupWidget.cpp" line="879"/>
<location filename="../StudioSetupWidget.cpp" line="789"/> <location filename="../StudioSetupWidget.cpp" line="887"/>
<source>Delay</source> <source>Delay</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="782"/> <location filename="../StudioSetupWidget.cpp" line="880"/>
<location filename="../StudioSetupWidget.cpp" line="790"/> <location filename="../StudioSetupWidget.cpp" line="888"/>
<source>Chorus</source> <source>Chorus</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -350,13 +350,13 @@
<context> <context>
<name>MIDIPlayerWidget</name> <name>MIDIPlayerWidget</name>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1193"/> <location filename="../SongGroupEditor.cpp" line="1192"/>
<source>Stop</source> <source>Stop</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1207"/> <location filename="../SongGroupEditor.cpp" line="1206"/>
<location filename="../SongGroupEditor.cpp" line="1230"/> <location filename="../SongGroupEditor.cpp" line="1229"/>
<source>Play</source> <source>Play</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -559,284 +559,310 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="81"/> <location filename="../MainWindow.cpp" line="83"/>
<source>Clear Recent Projects</source> <source>Clear Recent Projects</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="87"/> <location filename="../MainWindow.cpp" line="89"/>
<source>Quit</source> <source>Quit</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="203"/> <location filename="../MainWindow.cpp" line="205"/>
<source>Amuse[*]</source> <source>Amuse[*]</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="211"/> <location filename="../MainWindow.cpp" line="213"/>
<source>%1/%2/%3[*] - Amuse</source> <source>%1/%2/%3[*] - Amuse</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="216"/> <location filename="../MainWindow.cpp" line="218"/>
<source>%1[*] - Amuse</source> <source>%1[*] - Amuse</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="244"/> <location filename="../MainWindow.cpp" line="246"/>
<location filename="../MainWindow.cpp" line="683"/> <location filename="../MainWindow.cpp" line="720"/>
<source>The directory at &apos;%1&apos; must not be empty.</source> <source>The directory at &apos;%1&apos; must not be empty.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="245"/> <location filename="../MainWindow.cpp" line="247"/>
<location filename="../MainWindow.cpp" line="684"/> <location filename="../MainWindow.cpp" line="721"/>
<source>Directory empty</source> <source>Directory empty</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="250"/> <location filename="../MainWindow.cpp" line="252"/>
<source>The directory at &apos;%1&apos; must exist for the Amuse editor.</source> <source>The directory at &apos;%1&apos; must exist for the Amuse editor.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="251"/> <location filename="../MainWindow.cpp" line="253"/>
<source>Directory does not exist</source> <source>Directory does not exist</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="254"/> <location filename="../MainWindow.cpp" line="256"/>
<source>__amuse_test__</source> <source>__amuse_test__</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="259"/> <location filename="../MainWindow.cpp" line="261"/>
<source>The directory at &apos;%1&apos; must be writable for the Amuse editor: %2</source> <source>The directory at &apos;%1&apos; must be writable for the Amuse editor: %2</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="261"/> <location filename="../MainWindow.cpp" line="263"/>
<source>Unable to write to directory</source> <source>Unable to write to directory</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="310"/> <location filename="../MainWindow.cpp" line="325"/>
<source>No Audio Devices Found</source> <source>No Audio Devices Found</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="333"/> <location filename="../MainWindow.cpp" line="340"/>
<source>Virtual MIDI-In</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="359"/>
<source>No MIDI Devices Found</source> <source>No MIDI Devices Found</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="389"/> <location filename="../MainWindow.cpp" line="415"/>
<source>SUSTAIN</source> <source>SUSTAIN</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="635"/> <location filename="../MainWindow.cpp" line="672"/>
<source>Unsaved Changes</source> <source>Unsaved Changes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="635"/> <location filename="../MainWindow.cpp" line="672"/>
<source>Save Changes in %1?</source> <source>Save Changes in %1?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="664"/> <location filename="../MainWindow.cpp" line="701"/>
<source>New Project</source> <source>New Project</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="689"/> <location filename="../MainWindow.cpp" line="726"/>
<source>The directory at &apos;%1&apos; does not exist.</source> <source>The directory at &apos;%1&apos; does not exist.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="690"/> <location filename="../MainWindow.cpp" line="727"/>
<source>Bad Directory</source> <source>Bad Directory</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="705"/> <location filename="../MainWindow.cpp" line="742"/>
<source>Opening</source> <source>Opening</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="705"/> <location filename="../MainWindow.cpp" line="742"/>
<location filename="../MainWindow.cpp" line="788"/> <location filename="../MainWindow.cpp" line="827"/>
<location filename="../MainWindow.cpp" line="872"/> <location filename="../MainWindow.cpp" line="913"/>
<location filename="../MainWindow.cpp" line="917"/> <location filename="../MainWindow.cpp" line="958"/>
<location filename="../MainWindow.cpp" line="1003"/>
<source>Scanning Project</source> <source>Scanning Project</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="717"/> <location filename="../MainWindow.cpp" line="756"/>
<source>Opening %1</source> <source>Opening %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="730"/> <location filename="../MainWindow.cpp" line="769"/>
<source>Open Project</source> <source>Open Project</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="788"/> <location filename="../MainWindow.cpp" line="827"/>
<source>Reloading Samples</source> <source>Reloading Samples</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="800"/> <location filename="../MainWindow.cpp" line="841"/>
<source>Scanning %1</source> <source>Scanning %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="810"/> <location filename="../MainWindow.cpp" line="851"/>
<source>Import Project</source> <source>Import Project</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="820"/> <location filename="../MainWindow.cpp" line="861"/>
<source>The file at &apos;%1&apos; could not be interpreted as a MusyX container.</source> <source>The file at &apos;%1&apos; could not be interpreted as a MusyX container.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="821"/> <location filename="../MainWindow.cpp" line="862"/>
<source>Unsupported MusyX Container</source> <source>Unsupported MusyX Container</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="826"/> <location filename="../MainWindow.cpp" line="867"/>
<source>Sample Import Mode</source> <source>Sample Import Mode</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="827"/> <location filename="../MainWindow.cpp" line="868"/>
<source>Amuse can import samples as WAV files for ease of editing, import original compressed data for lossless repacking, or both. Exporting the project will prefer whichever version was modified most recently.</source> <source>Amuse can import samples as WAV files for ease of editing, import original compressed data for lossless repacking, or both. Exporting the project will prefer whichever version was modified most recently.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="831"/> <location filename="../MainWindow.cpp" line="872"/>
<source>Import Compressed</source> <source>Import Compressed</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="831"/> <location filename="../MainWindow.cpp" line="872"/>
<source>Import WAVs</source> <source>Import WAVs</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="831"/> <location filename="../MainWindow.cpp" line="872"/>
<source>Import Both</source> <source>Import Both</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="847"/> <location filename="../MainWindow.cpp" line="888"/>
<source>Raw Import Mode</source> <source>Raw Import Mode</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="848"/> <location filename="../MainWindow.cpp" line="889"/>
<source>Would you like to scan for all MusyX group files in this directory?</source> <source>Would you like to scan for all MusyX group files in this directory?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="858"/> <location filename="../MainWindow.cpp" line="899"/>
<source>Project Name</source> <source>Project Name</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="858"/> <location filename="../MainWindow.cpp" line="899"/>
<source>What should this project be named?</source> <source>What should this project be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="872"/> <location filename="../MainWindow.cpp" line="913"/>
<location filename="../MainWindow.cpp" line="917"/> <location filename="../MainWindow.cpp" line="958"/>
<source>Importing</source> <source>Importing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="884"/> <location filename="../MainWindow.cpp" line="925"/>
<location filename="../MainWindow.cpp" line="926"/> <location filename="../MainWindow.cpp" line="967"/>
<source>Importing %1</source> <source>Importing %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="942"/> <location filename="../MainWindow.cpp" line="983"/>
<source>Import Songs</source> <source>Import Songs</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1023"/> <location filename="../MainWindow.cpp" line="1003"/>
<source>Exporting</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1011"/>
<source>Exporting %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1517"/>
<source>Export Complete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1517"/>
<source>%1?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1089"/>
<source>New Subproject</source> <source>New Subproject</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1039"/> <location filename="../MainWindow.cpp" line="1105"/>
<source>New SFX Group</source> <source>New SFX Group</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1040"/> <location filename="../MainWindow.cpp" line="1106"/>
<source>What should the new SFX group in %1 be named?</source> <source>What should the new SFX group in %1 be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1058"/> <location filename="../MainWindow.cpp" line="1124"/>
<source>New Song Group</source> <source>New Song Group</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1059"/> <location filename="../MainWindow.cpp" line="1125"/>
<source>What should the new Song group in %1 be named?</source> <source>What should the new Song group in %1 be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1100"/> <location filename="../MainWindow.cpp" line="1166"/>
<source>New ADSR</source> <source>New ADSR</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1101"/> <location filename="../MainWindow.cpp" line="1167"/>
<source>What should the new ADSR in %1 be named?</source> <source>What should the new ADSR in %1 be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1119"/> <location filename="../MainWindow.cpp" line="1185"/>
<source>New Curve</source> <source>New Curve</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1120"/> <location filename="../MainWindow.cpp" line="1186"/>
<source>What should the new Curve in %1 be named?</source> <source>What should the new Curve in %1 be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1138"/> <location filename="../MainWindow.cpp" line="1204"/>
<source>New Keymap</source> <source>New Keymap</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1139"/> <location filename="../MainWindow.cpp" line="1205"/>
<source>What should the new Keymap in %1 be named?</source> <source>What should the new Keymap in %1 be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1157"/> <location filename="../MainWindow.cpp" line="1223"/>
<source>New Layers</source> <source>New Layers</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1158"/> <location filename="../MainWindow.cpp" line="1224"/>
<source>What should the new Layers in %1 be named?</source> <source>What should the new Layers in %1 be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="1024"/> <location filename="../MainWindow.cpp" line="1090"/>
<source>What should this subproject be named?</source> <source>What should this subproject be named?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1055,187 +1081,215 @@
<name>ProjectModel</name> <name>ProjectModel</name>
<message> <message>
<location filename="../ProjectModel.cpp" line="601"/> <location filename="../ProjectModel.cpp" line="601"/>
<source>Sound Macros</source> <location filename="../ProjectModel.cpp" line="610"/>
<location filename="../ProjectModel.cpp" line="615"/>
<location filename="../ProjectModel.cpp" line="620"/>
<source>Export Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="601"/>
<source>Unable to find group %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="610"/>
<source>Unable to export %1.proj</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="615"/>
<source>Unable to export %1.pool</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="620"/> <location filename="../ProjectModel.cpp" line="620"/>
<source>ADSRs</source> <source>Unable to export %1.sdir</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="631"/>
<source>Curves</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="643"/> <location filename="../ProjectModel.cpp" line="643"/>
<source>Sound Macros</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="662"/>
<source>ADSRs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="673"/>
<source>Curves</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="685"/>
<source>Keymaps</source> <source>Keymaps</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="650"/> <location filename="../ProjectModel.cpp" line="692"/>
<source>Layers</source> <source>Layers</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="657"/> <location filename="../ProjectModel.cpp" line="699"/>
<source>Samples</source> <source>Samples</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="922"/> <location filename="../ProjectModel.cpp" line="964"/>
<source>Subproject Conflict</source> <source>Subproject Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="922"/> <location filename="../ProjectModel.cpp" line="964"/>
<source>The subproject %1 is already defined</source> <source>The subproject %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="927"/> <location filename="../ProjectModel.cpp" line="969"/>
<source>Add Subproject %1</source> <source>Add Subproject %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="977"/> <location filename="../ProjectModel.cpp" line="1019"/>
<source>Sound Group Conflict</source> <source>Sound Group Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="977"/> <location filename="../ProjectModel.cpp" line="1019"/>
<location filename="../ProjectModel.cpp" line="1002"/> <location filename="../ProjectModel.cpp" line="1044"/>
<source>The group %1 is already defined</source> <source>The group %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="981"/> <location filename="../ProjectModel.cpp" line="1023"/>
<source>Add Sound Group %1</source> <source>Add Sound Group %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1002"/> <location filename="../ProjectModel.cpp" line="1044"/>
<source>Song Group Conflict</source> <source>Song Group Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1006"/> <location filename="../ProjectModel.cpp" line="1048"/>
<source>Add Song Group %1</source> <source>Add Song Group %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1087"/> <location filename="../ProjectModel.cpp" line="1129"/>
<source>Sound Macro Conflict</source> <source>Sound Macro Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1087"/> <location filename="../ProjectModel.cpp" line="1129"/>
<source>The macro %1 is already defined</source> <source>The macro %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1097"/> <location filename="../ProjectModel.cpp" line="1139"/>
<source>Add Sound Macro %1</source> <source>Add Sound Macro %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1118"/> <location filename="../ProjectModel.cpp" line="1160"/>
<source>ADSR Conflict</source> <source>ADSR Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1118"/> <location filename="../ProjectModel.cpp" line="1160"/>
<source>The ADSR %1 is already defined</source> <source>The ADSR %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1124"/> <location filename="../ProjectModel.cpp" line="1166"/>
<source>Add ADSR %1</source> <source>Add ADSR %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1145"/> <location filename="../ProjectModel.cpp" line="1187"/>
<source>Curve Conflict</source> <source>Curve Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1145"/> <location filename="../ProjectModel.cpp" line="1187"/>
<source>The Curve %1 is already defined</source> <source>The Curve %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1151"/> <location filename="../ProjectModel.cpp" line="1193"/>
<source>Add Curve %1</source> <source>Add Curve %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1172"/> <location filename="../ProjectModel.cpp" line="1214"/>
<source>Keymap Conflict</source> <source>Keymap Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1172"/> <location filename="../ProjectModel.cpp" line="1214"/>
<source>The Keymap %1 is already defined</source> <source>The Keymap %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1177"/> <location filename="../ProjectModel.cpp" line="1219"/>
<source>Add Keymap %1</source> <source>Add Keymap %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1198"/> <location filename="../ProjectModel.cpp" line="1240"/>
<source>Layers Conflict</source> <source>Layers Conflict</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1198"/> <location filename="../ProjectModel.cpp" line="1240"/>
<source>Layers %1 is already defined</source> <source>Layers %1 is already defined</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1203"/> <location filename="../ProjectModel.cpp" line="1245"/>
<source>Add Layers %1</source> <source>Add Layers %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1217"/> <location filename="../ProjectModel.cpp" line="1259"/>
<source>Delete Subproject %1</source> <source>Delete Subproject %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1220"/> <location filename="../ProjectModel.cpp" line="1262"/>
<source>Delete SongGroup %1</source> <source>Delete SongGroup %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1223"/> <location filename="../ProjectModel.cpp" line="1265"/>
<source>Delete SFXGroup %1</source> <source>Delete SFXGroup %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1226"/> <location filename="../ProjectModel.cpp" line="1268"/>
<source>Delete SoundMacro %1</source> <source>Delete SoundMacro %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1229"/> <location filename="../ProjectModel.cpp" line="1271"/>
<source>Delete ADSR %1</source> <source>Delete ADSR %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1232"/> <location filename="../ProjectModel.cpp" line="1274"/>
<source>Delete Curve %1</source> <source>Delete Curve %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1235"/> <location filename="../ProjectModel.cpp" line="1277"/>
<source>Delete Keymap %1</source> <source>Delete Keymap %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="1238"/> <location filename="../ProjectModel.cpp" line="1280"/>
<source>Delete Layers %1</source> <source>Delete Layers %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1481,37 +1535,37 @@
<context> <context>
<name>SongGroupEditor</name> <name>SongGroupEditor</name>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1502"/> <location filename="../SongGroupEditor.cpp" line="1501"/>
<source>Add new page entry</source> <source>Add new page entry</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1504"/> <location filename="../SongGroupEditor.cpp" line="1503"/>
<source>Remove selected page entries</source> <source>Remove selected page entries</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1471"/> <location filename="../SongGroupEditor.cpp" line="1470"/>
<source>Normal Pages</source> <source>Normal Pages</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1301"/> <location filename="../SongGroupEditor.cpp" line="1300"/>
<source>Add Page Entry</source> <source>Add Page Entry</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1313"/> <location filename="../SongGroupEditor.cpp" line="1312"/>
<source>Add Setup Entry</source> <source>Add Setup Entry</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1472"/> <location filename="../SongGroupEditor.cpp" line="1471"/>
<source>Drum Pages</source> <source>Drum Pages</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SongGroupEditor.cpp" line="1473"/> <location filename="../SongGroupEditor.cpp" line="1472"/>
<source>MIDI Setups</source> <source>MIDI Setups</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1636,25 +1690,40 @@
<context> <context>
<name>StatusBarWidget</name> <name>StatusBarWidget</name>
<message> <message>
<location filename="../StatusBarWidget.cpp" line="16"/> <location filename="../StatusBarWidget.cpp" line="17"/>
<source>Immediately kill active voices</source> <source>Immediately kill active voices</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../StatusBarWidget.cpp" line="25"/>
<source>Aux A send level for all voices</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StatusBarWidget.cpp" line="32"/>
<source>Aux B send level for all voices</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StatusBarWidget.cpp" line="39"/>
<source>Master volume level</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>StudioSetupWidget</name> <name>StudioSetupWidget</name>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="1017"/> <location filename="../StudioSetupWidget.cpp" line="1115"/>
<source>Studio Setup</source> <source>Studio Setup</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="1035"/> <location filename="../StudioSetupWidget.cpp" line="1133"/>
<source>Aux A</source> <source>Aux A</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../StudioSetupWidget.cpp" line="1036"/> <location filename="../StudioSetupWidget.cpp" line="1134"/>
<source>Aux B</source> <source>Aux B</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -1667,6 +1736,57 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>Uint32X8Button</name>
<message>
<location filename="../StudioSetupWidget.cpp" line="328"/>
<source>Channels</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Uint32X8Popup</name>
<message>
<location filename="../StudioSetupWidget.cpp" line="257"/>
<source>Front Left</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="258"/>
<source>Front Right</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="259"/>
<source>Rear Left</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="260"/>
<source>Rear Right</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="261"/>
<source>Front Center</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="262"/>
<source>LFE</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="263"/>
<source>Side Left</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="264"/>
<source>Side Right</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>VelocitySlider</name> <name>VelocitySlider</name>
<message> <message>

View File

@ -34,6 +34,8 @@
<file>IconVolume2.svg</file> <file>IconVolume2.svg</file>
<file>IconVolume3.svg</file> <file>IconVolume3.svg</file>
<file>IconFX.svg</file> <file>IconFX.svg</file>
<file>IconB.svg</file>
<file>IconA.svg</file>
</qresource> </qresource>
<qresource prefix="/bg"> <qresource prefix="/bg">
<file>FaceGrey.svg</file> <file>FaceGrey.svg</file>

View File

@ -12,6 +12,7 @@ class AudioGroupData;
/** Runtime audio group index container */ /** Runtime audio group index container */
class AudioGroup class AudioGroup
{ {
friend class AudioGroupSampleDirectory;
AudioGroupProject m_proj; AudioGroupProject m_proj;
AudioGroupPool m_pool; AudioGroupPool m_pool;
AudioGroupSampleDirectory m_sdir; AudioGroupSampleDirectory m_sdir;

View File

@ -1132,6 +1132,8 @@ struct SoundMacro
template <athena::Endian DNAE> template <athena::Endian DNAE>
void readCmds(athena::io::IStreamReader& r, uint32_t size); void readCmds(athena::io::IStreamReader& r, uint32_t size);
template <athena::Endian DNAE>
void writeCmds(athena::io::IStreamWriter& w) const;
ICmd* insertNewCmd(int idx, CmdOp op) ICmd* insertNewCmd(int idx, CmdOp op)
{ {
@ -1423,6 +1425,8 @@ public:
const Curve* tableAsCurves(ObjectId id) const; const Curve* tableAsCurves(ObjectId id) const;
bool toYAML(SystemStringView groupPath) const; bool toYAML(SystemStringView groupPath) const;
template <athena::Endian DNAE>
bool toData(SystemStringView groupPath) const;
AudioGroupPool(const AudioGroupPool&) = delete; AudioGroupPool(const AudioGroupPool&) = delete;
AudioGroupPool& operator=(const AudioGroupPool&) = delete; AudioGroupPool& operator=(const AudioGroupPool&) = delete;

View File

@ -11,6 +11,8 @@
namespace amuse namespace amuse
{ {
class AudioGroupData; class AudioGroupData;
class AudioGroupPool;
class AudioGroupSampleDirectory;
enum class GroupType : atUint16 enum class GroupType : atUint16
{ {
@ -205,6 +207,7 @@ public:
std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; } std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
bool toYAML(SystemStringView groupPath) const; bool toYAML(SystemStringView groupPath) const;
bool toGCNData(SystemStringView groupPath, const AudioGroupPool& pool, const AudioGroupSampleDirectory& sdir) const;
AudioGroupProject(const AudioGroupProject&) = delete; AudioGroupProject(const AudioGroupProject&) = delete;
AudioGroupProject& operator=(const AudioGroupProject&) = delete; AudioGroupProject& operator=(const AudioGroupProject&) = delete;

View File

@ -8,6 +8,7 @@
namespace amuse namespace amuse
{ {
class AudioGroupData; class AudioGroupData;
class AudioGroupDatabase;
struct DSPADPCMHeader : BigDNA struct DSPADPCMHeader : BigDNA
{ {
@ -167,6 +168,16 @@ public:
Value<atUint32, DNAEn> m_loopStartSample; Value<atUint32, DNAEn> m_loopStartSample;
Value<atUint32, DNAEn> m_loopLengthSamples; Value<atUint32, DNAEn> m_loopLengthSamples;
Value<atUint32, DNAEn> m_adpcmParmOffset; Value<atUint32, DNAEn> m_adpcmParmOffset;
void _setLoopStartSample(atUint32 sample)
{
m_loopLengthSamples += m_loopStartSample - sample;
m_loopStartSample = sample;
}
void setLoopEndSample(atUint32 sample)
{
m_loopLengthSamples = sample + 1 - m_loopStartSample;
}
}; };
template <athena::Endian DNAEn> template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
@ -346,6 +357,8 @@ public:
void reloadSampleData(SystemStringView groupPath); void reloadSampleData(SystemStringView groupPath);
bool toGCNData(SystemStringView groupPath, const AudioGroupDatabase& group) const;
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete; AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete; AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default; AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;

View File

@ -73,7 +73,8 @@ class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
friend class BooBackendVoiceAllocator; friend class BooBackendVoiceAllocator;
protected: protected:
Engine& m_engine; Engine& m_engine;
std::unique_ptr<boo::IMIDIIn> m_midiIn; std::unordered_map<std::string, std::unique_ptr<boo::IMIDIIn>> m_midiIns;
std::unique_ptr<boo::IMIDIIn> m_virtualIn;
boo::MIDIDecoder m_decoder; boo::MIDIDecoder m_decoder;
bool m_useLock; bool m_useLock;
@ -83,9 +84,14 @@ protected:
public: public:
~BooBackendMIDIReader(); ~BooBackendMIDIReader();
BooBackendMIDIReader(Engine& engine, const char* name, bool useLock); BooBackendMIDIReader(Engine& engine, bool useLock);
void addMIDIIn(const char* name);
void removeMIDIIn(const char* name);
bool hasMIDIIn(const char* name) const;
void setVirtualIn(bool v);
bool hasVirtualIn() const;
std::string description();
void pumpReader(double dt); void pumpReader(double dt);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity); void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
@ -129,7 +135,7 @@ public:
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch); std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId); std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices(); std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine, const char* name = nullptr); std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine);
void setCallbackInterface(Engine* engine); void setCallbackInterface(Engine* engine);
AudioChannelSet getAvailableSet(); AudioChannelSet getAvailableSet();
void setVolume(float vol); void setVolume(float vol);

View File

@ -569,6 +569,28 @@ static std::vector<std::pair<typename T::key_type,
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret; return ret;
} }
template <class T>
static std::vector<typename T::key_type> SortUnorderedSet(T& us)
{
std::vector<typename T::key_type> ret;
ret.reserve(us.size());
for (auto& p : us)
ret.emplace_back(p);
std::sort(ret.begin(), ret.end());
return ret;
}
template <class T>
static std::vector<typename T::key_type> SortUnorderedSet(const T& us)
{
std::vector<typename T::key_type> ret;
ret.reserve(us.size());
for (const auto& p : us)
ret.emplace_back(p);
std::sort(ret.begin(), ret.end());
return ret;
}
} }
namespace std namespace std

View File

@ -75,6 +75,9 @@ public:
/** Access voice backend of engine */ /** Access voice backend of engine */
IBackendVoiceAllocator& getBackend() { return m_backend; } IBackendVoiceAllocator& getBackend() { return m_backend; }
/** Access MIDI reader */
IMIDIReader* getMIDIReader() const { return m_midiReader.get(); }
/** Add audio group data pointers to engine; must remain resident! */ /** Add audio group data pointers to engine; must remain resident! */
const AudioGroup* addAudioGroup(const AudioGroupData& data); const AudioGroup* addAudioGroup(const AudioGroupData& data);

View File

@ -28,7 +28,6 @@ class IMIDIReader
{ {
public: public:
virtual ~IMIDIReader() = default; virtual ~IMIDIReader() = default;
virtual std::string description() = 0;
virtual void pumpReader(double dt) = 0; virtual void pumpReader(double dt) = 0;
}; };
@ -48,7 +47,7 @@ public:
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0; virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
/** Amuse obtains an interactive MIDI-in connection from the OS this way */ /** Amuse obtains an interactive MIDI-in connection from the OS this way */
virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine, const char* name = nullptr) = 0; virtual std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine) = 0;
/** Amuse obtains speaker-configuration from the platform this way */ /** Amuse obtains speaker-configuration from the platform this way */
virtual AudioChannelSet getAvailableSet() = 0; virtual AudioChannelSet getAvailableSet() = 0;

View File

@ -1,3 +1,4 @@
#include <athena/MemoryWriter.hpp>
#include "amuse/AudioGroupPool.hpp" #include "amuse/AudioGroupPool.hpp"
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include "amuse/Entity.hpp" #include "amuse/Entity.hpp"
@ -393,6 +394,21 @@ void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size)
template void SoundMacro::readCmds<athena::Big>(athena::io::IStreamReader& r, uint32_t size); template void SoundMacro::readCmds<athena::Big>(athena::io::IStreamReader& r, uint32_t size);
template void SoundMacro::readCmds<athena::Little>(athena::io::IStreamReader& r, uint32_t size); template void SoundMacro::readCmds<athena::Little>(athena::io::IStreamReader& r, uint32_t size);
template <athena::Endian DNAE>
void SoundMacro::writeCmds(athena::io::IStreamWriter& w) const
{
for (const auto& cmd : m_cmds)
{
uint32_t data[2];
athena::io::MemoryWriter mw((uint8_t*)data, 8);
mw.writeUByte(uint8_t(cmd->Isa()));
cmd->write(mw);
athena::io::Write<athena::io::PropType::None>::Do<decltype(data), DNAE>({}, data, w);
}
}
template void SoundMacro::writeCmds<athena::Big>(athena::io::IStreamWriter& w) const;
template void SoundMacro::writeCmds<athena::Little>(athena::io::IStreamWriter& w) const;
void SoundMacro::buildFromPrototype(const SoundMacro& other) void SoundMacro::buildFromPrototype(const SoundMacro& other)
{ {
m_cmds.reserve(other.m_cmds.size()); m_cmds.reserve(other.m_cmds.size());
@ -1063,6 +1079,126 @@ bool AudioGroupPool::toYAML(SystemStringView groupPath) const
return w.finish(&fo); return w.finish(&fo);
} }
template <athena::Endian DNAE>
bool AudioGroupPool::toData(SystemStringView groupPath) const
{
SystemString poolPath(groupPath);
poolPath += _S(".pool");
athena::io::FileWriter fo(poolPath);
if (fo.hasError())
return false;
PoolHeader<DNAE> head = {};
head.write(fo);
const uint32_t term = 0xffffffff;
if (!m_soundMacros.empty())
{
head.soundMacrosOffset = fo.position();
for (const auto& p : m_soundMacros)
{
auto startPos = fo.position();
ObjectHeader<DNAE> objHead = {};
objHead.write(fo);
p.second->template writeCmds<DNAE>(fo);
objHead.size = fo.position() - startPos;
objHead.objectId = p.first;
fo.seek(startPos, athena::Begin);
objHead.write(fo);
fo.seek(startPos + objHead.size, athena::Begin);
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(term), DNAE>({}, term, fo);
}
if (!m_tables.empty())
{
head.tablesOffset = fo.position();
for (const auto& p : m_tables)
{
auto startPos = fo.position();
ObjectHeader<DNAE> objHead = {};
objHead.write(fo);
switch ((*p.second)->Isa())
{
case ITable::Type::ADSR:
static_cast<ADSR*>(p.second->get())->write(fo);
break;
case ITable::Type::ADSRDLS:
static_cast<ADSRDLS*>(p.second->get())->write(fo);
break;
case ITable::Type::Curve:
{
const auto& data = static_cast<Curve*>(p.second->get())->data;
fo.writeUBytes(data.data(), data.size());
break;
}
default:
break;
}
objHead.size = fo.position() - startPos;
objHead.objectId = p.first;
fo.seek(startPos, athena::Begin);
objHead.write(fo);
fo.seek(startPos + objHead.size, athena::Begin);
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(term), DNAE>({}, term, fo);
}
if (!m_keymaps.empty())
{
head.keymapsOffset = fo.position();
for (const auto& p : m_keymaps)
{
auto startPos = fo.position();
ObjectHeader<DNAE> objHead = {};
objHead.write(fo);
for (const auto& km : *p.second)
{
KeymapDNA<DNAE> kmData = km.toDNA<DNAE>();
kmData.write(fo);
}
objHead.size = fo.position() - startPos;
objHead.objectId = p.first;
fo.seek(startPos, athena::Begin);
objHead.write(fo);
fo.seek(startPos + objHead.size, athena::Begin);
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(term), DNAE>({}, term, fo);
}
if (!m_layers.empty())
{
head.layersOffset = fo.position();
for (const auto& p : m_layers)
{
auto startPos = fo.position();
ObjectHeader<DNAE> objHead = {};
objHead.write(fo);
uint32_t count = p.second->size();
athena::io::Write<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, fo);
for (const auto& lm : *p.second)
{
LayerMappingDNA<DNAE> lmData = lm.toDNA<DNAE>();
lmData.write(fo);
}
objHead.size = fo.position() - startPos;
objHead.objectId = p.first;
fo.seek(startPos, athena::Begin);
objHead.write(fo);
fo.seek(startPos + objHead.size, athena::Begin);
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(term), DNAE>({}, term, fo);
}
fo.seek(0, athena::Begin);
head.write(fo);
return true;
}
template bool AudioGroupPool::toData<athena::Big>(SystemStringView groupPath) const;
template bool AudioGroupPool::toData<athena::Little>(SystemStringView groupPath) const;
template <> template <>
void amuse::Curve::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& r) void amuse::Curve::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& r)
{ {

View File

@ -1,4 +1,6 @@
#include "amuse/AudioGroupProject.hpp" #include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/AudioGroupData.hpp" #include "amuse/AudioGroupData.hpp"
#include "athena/MemoryReader.hpp" #include "athena/MemoryReader.hpp"
#include "athena/FileWriter.hpp" #include "athena/FileWriter.hpp"
@ -7,6 +9,13 @@
namespace amuse namespace amuse
{ {
static bool AtEnd64(athena::io::IStreamReader& r)
{
uint64_t v = r.readUint64Big();
r.seek(-8, athena::Current);
return v == 0xffffffffffffffff;
}
static bool AtEnd32(athena::io::IStreamReader& r) static bool AtEnd32(athena::io::IStreamReader& r)
{ {
uint32_t v = r.readUint32Big(); uint32_t v = r.readUint32Big();
@ -50,6 +59,37 @@ static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB
} }
} }
template <athena::Endian DNAE, class T>
static void WriteRangedObjectIds(athena::io::IStreamWriter& w, const T& list)
{
if (list.cbegin() == list.cend())
return;
bool inRange = false;
uint16_t lastId = list.cbegin()->first & 0x3fff;
for (auto it = list.cbegin() + 1; it != list.cend(); ++it)
{
uint16_t thisId = it->first & 0x3fff;
if (thisId == lastId + 1)
{
if (!inRange)
{
inRange = true;
lastId |= 0x8000;
athena::io::Write<athena::io::PropType::None>::Do<decltype(lastId), DNAE>({}, lastId, w);
}
}
else
{
inRange = false;
athena::io::Write<athena::io::PropType::None>::Do<decltype(lastId), DNAE>({}, lastId, w);
}
lastId = thisId;
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(lastId), DNAE>({}, lastId, w);
uint16_t term = 0xffff;
athena::io::Write<athena::io::PropType::None>::Do<decltype(term), DNAE>({}, term, w);
}
AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag) AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
{ {
while (!AtEnd32(r)) while (!AtEnd32(r))
@ -91,7 +131,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
/* Normal pages */ /* Normal pages */
r.seek(header.pageTableOff, athena::Begin); r.seek(header.pageTableOff, athena::Begin);
while (!AtEnd16(r)) while (!AtEnd64(r))
{ {
SongGroupIndex::PageEntryDNA<athena::Big> entry; SongGroupIndex::PageEntryDNA<athena::Big> entry;
entry.read(r); entry.read(r);
@ -100,7 +140,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
/* Drum pages */ /* Drum pages */
r.seek(header.drumTableOff, athena::Begin); r.seek(header.drumTableOff, athena::Begin);
while (!AtEnd16(r)) while (!AtEnd64(r))
{ {
SongGroupIndex::PageEntryDNA<athena::Big> entry; SongGroupIndex::PageEntryDNA<athena::Big> entry;
entry.read(r); entry.read(r);
@ -698,4 +738,243 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
return w.finish(&fo); return w.finish(&fo);
} }
#if 0
struct ObjectIdPool
{
std::unordered_set<SoundMacroId> soundMacros;
std::unordered_set<SampleId> samples;
std::unordered_set<TableId> tables;
std::unordered_set<KeymapId> keymaps;
std::unordered_set<LayersId> layers;
void _recursiveAddSoundMacro(SoundMacroId id, const AudioGroupPool& pool)
{
if (soundMacros.find(id) != soundMacros.cend())
return;
const SoundMacro* macro = pool.soundMacro(id);
if (!macro)
return;
soundMacros.insert(id);
for (const auto& cmd : macro->m_cmds)
{
switch (cmd->Isa())
{
case SoundMacro::CmdOp::StartSample:
samples.insert(static_cast<SoundMacro::CmdStartSample*>(cmd.get())->sample);
break;
case SoundMacro::CmdOp::SetAdsr:
tables.insert(static_cast<SoundMacro::CmdSetAdsr*>(cmd.get())->table);
break;
case SoundMacro::CmdOp::ScaleVolume:
tables.insert(static_cast<SoundMacro::CmdScaleVolume*>(cmd.get())->table);
break;
case SoundMacro::CmdOp::Envelope:
tables.insert(static_cast<SoundMacro::CmdEnvelope*>(cmd.get())->table);
break;
case SoundMacro::CmdOp::FadeIn:
tables.insert(static_cast<SoundMacro::CmdFadeIn*>(cmd.get())->table);
break;
case SoundMacro::CmdOp::SetPitchAdsr:
tables.insert(static_cast<SoundMacro::CmdSetPitchAdsr*>(cmd.get())->table);
break;
case SoundMacro::CmdOp::SplitKey:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdSplitKey*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::SplitVel:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdSplitVel*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::Goto:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdGoto*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::PlayMacro:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdPlayMacro*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::SplitMod:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdSplitMod*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::SplitRnd:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdSplitRnd*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::GoSub:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdGoSub*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::TrapEvent:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdTrapEvent*>(cmd.get())->macro, pool);
break;
case SoundMacro::CmdOp::SendMessage:
_recursiveAddSoundMacro(static_cast<SoundMacro::CmdSendMessage*>(cmd.get())->macro, pool);
break;
default:
break;
}
}
}
void addRootId(ObjectId id, const AudioGroupPool& pool)
{
if (id & 0x8000)
{
if (const std::vector<LayerMapping>* lms = pool.layer(id))
{
layers.insert(id);
for (const auto& lm : *lms)
_recursiveAddSoundMacro(lm.macro, pool);
}
}
else if (id & 0x4000)
{
if (const auto* kms = pool.keymap(id))
{
keymaps.insert(id);
for (int i = 0; i < 128; ++i)
_recursiveAddSoundMacro(kms[i].macro, pool);
}
}
else
{
_recursiveAddSoundMacro(id, pool);
}
}
void cleanup()
{
soundMacros.erase(SoundMacroId{});
samples.erase(SampleId{});
tables.erase(TableId{});
keymaps.erase(KeymapId{});
layers.erase(LayersId{});
}
};
#endif
bool AudioGroupProject::toGCNData(SystemStringView groupPath, const AudioGroupPool& pool,
const AudioGroupSampleDirectory& sdir) const
{
constexpr athena::Endian DNAE = athena::Big;
SystemString projPath(groupPath);
projPath += _S(".proj");
athena::io::FileWriter fo(projPath);
if (fo.hasError())
return false;
std::vector<GroupId> groupIds;
groupIds.reserve(m_songGroups.size() + m_sfxGroups.size());
for (auto& p : m_songGroups)
groupIds.push_back(p.first);
for (auto& p : m_sfxGroups)
groupIds.push_back(p.first);
std::sort(groupIds.begin(), groupIds.end());
const uint64_t term64 = 0xffffffffffffffff;
const uint16_t padding = 0;
for (GroupId id : groupIds)
{
auto search = m_songGroups.find(id);
if (search != m_songGroups.end())
{
const SongGroupIndex& index = *search->second;
auto groupStart = fo.position();
GroupHeader<DNAE> header = {};
header.write(fo);
header.groupId = id;
header.type = GroupType::Song;
header.soundMacroIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.soundMacros()));
header.samplIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(sdir.sampleEntries()));
header.tableIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.tables()));
header.keymapIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.keymaps()));
header.layerIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.layers()));
header.pageTableOff = fo.position();
for (auto& p : SortUnorderedMap(index.m_normPages))
{
SongGroupIndex::PageEntryDNA<DNAE> entry = p.second.get().toDNA<DNAE>(p.first);
entry.write(fo);
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(term64), DNAE>({}, term64, fo);
header.drumTableOff = fo.position();
for (auto& p : SortUnorderedMap(index.m_drumPages))
{
SongGroupIndex::PageEntryDNA<DNAE> entry = p.second.get().toDNA<DNAE>(p.first);
entry.write(fo);
}
athena::io::Write<athena::io::PropType::None>::Do<decltype(term64), DNAE>({}, term64, fo);
header.midiSetupsOff = fo.position();
for (auto& p : SortUnorderedMap(index.m_midiSetups))
{
uint16_t songId = p.first.id;
athena::io::Write<athena::io::PropType::None>::Do<decltype(songId), DNAE>({}, songId, fo);
athena::io::Write<athena::io::PropType::None>::Do<decltype(padding), DNAE>({}, padding, fo);
const std::array<SongGroupIndex::MIDISetup, 16>& setup = p.second.get();
for (int i = 0; i < 16 ; ++i)
setup[i].write(fo);
}
header.groupEndOff = fo.position();
fo.seek(groupStart, athena::Begin);
header.write(fo);
fo.seek(header.groupEndOff, athena::Begin);
}
else
{
auto search2 = m_sfxGroups.find(id);
if (search2 != m_sfxGroups.end())
{
const SFXGroupIndex& index = *search2->second;
auto groupStart = fo.position();
GroupHeader<DNAE> header = {};
header.write(fo);
header.groupId = id;
header.type = GroupType::SFX;
header.soundMacroIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.soundMacros()));
header.samplIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(sdir.sampleEntries()));
header.tableIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.tables()));
header.keymapIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.keymaps()));
header.layerIdsOff = fo.position();
WriteRangedObjectIds<DNAE>(fo, SortUnorderedMap(pool.layers()));
header.pageTableOff = fo.position();
uint16_t count = index.m_sfxEntries.size();
athena::io::Write<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, fo);
athena::io::Write<athena::io::PropType::None>::Do<decltype(padding), DNAE>({}, padding, fo);
for (auto& p : SortUnorderedMap(index.m_sfxEntries))
{
SFXGroupIndex::SFXEntryDNA<DNAE> entry = p.second.get().toDNA<DNAE>(p.first);
entry.write(fo);
}
header.groupEndOff = fo.position();
fo.seek(groupStart, athena::Begin);
header.write(fo);
fo.seek(header.groupEndOff, athena::Begin);
}
}
}
const uint32_t finalTerm = 0xffffffff;
athena::io::Write<athena::io::PropType::None>::Do<decltype(finalTerm), DNAE>({}, finalTerm, fo);
return true;
}
} }

View File

@ -4,6 +4,7 @@
#include "amuse/DSPCodec.hpp" #include "amuse/DSPCodec.hpp"
#include "amuse/N64MusyXCodec.hpp" #include "amuse/N64MusyXCodec.hpp"
#include "amuse/DirectoryEnumerator.hpp" #include "amuse/DirectoryEnumerator.hpp"
#include "amuse/AudioGroup.hpp"
#include "athena/MemoryReader.hpp" #include "athena/MemoryReader.hpp"
#include "athena/FileWriter.hpp" #include "athena/FileWriter.hpp"
#include "athena/FileReader.hpp" #include "athena/FileReader.hpp"
@ -872,4 +873,97 @@ void AudioGroupSampleDirectory::reloadSampleData(SystemStringView groupPath)
} }
} }
} }
bool AudioGroupSampleDirectory::toGCNData(SystemStringView groupPath, const AudioGroupDatabase& group) const
{
constexpr athena::Endian DNAE = athena::Big;
group.setIdDatabases();
SystemString sdirPath(groupPath);
SystemString sampPath(groupPath);
sdirPath += _S(".sdir");
sampPath += _S(".samp");
athena::io::FileWriter fo(sdirPath);
if (fo.hasError())
return false;
athena::io::FileWriter sfo(sampPath);
if (sfo.hasError())
return false;
std::vector<std::pair<EntryDNA<DNAE>, ADPCMParms>> entries;
entries.reserve(m_entries.size());
size_t sampleOffset = 0;
size_t adpcmOffset = 0;
for (const auto& ent : SortUnorderedMap(m_entries))
{
amuse::SystemString path = group.getSampleBasePath(ent.first);
path += _S(".dsp");
SampleFileState state = group.getSampleFileState(ent.first, ent.second.get().get(), &path);
switch (state)
{
case SampleFileState::MemoryOnlyWAV:
case SampleFileState::MemoryOnlyCompressed:
case SampleFileState::WAVRecent:
case SampleFileState::WAVNoCompressed:
group.makeCompressedVersion(ent.first, ent.second.get().get());
default:
break;
}
athena::io::FileReader r(path);
if (!r.hasError())
{
EntryDNA<DNAE> entryDNA = ent.second.get()->toDNA<DNAE>(ent.first);
DSPADPCMHeader header;
header.read(r);
entryDNA.m_pitch = header.m_pitch;
entryDNA.m_sampleRate = atUint16(header.x8_sample_rate);
entryDNA.m_numSamples = header.x0_num_samples;
if (header.xc_loop_flag)
{
entryDNA._setLoopStartSample(DSPNibbleToSample(header.x10_loop_start_nibble));
entryDNA.setLoopEndSample(DSPNibbleToSample(header.x14_loop_end_nibble));
}
ADPCMParms adpcmParms;
adpcmParms.dsp.m_bytesPerFrame = 8;
adpcmParms.dsp.m_ps = uint8_t(header.x3e_ps);
adpcmParms.dsp.m_lps = uint8_t(header.x44_loop_ps);
adpcmParms.dsp.m_hist1 = header.x40_hist1;
adpcmParms.dsp.m_hist2 = header.x42_hist2;
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 2; ++j)
adpcmParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j];
uint32_t dataLen = (header.x4_num_nibbles + 1) / 2;
auto dspData = r.readUBytes(dataLen);
sfo.writeUBytes(dspData.get(), dataLen);
sfo.seekAlign32();
entryDNA.m_sampleOff = sampleOffset;
sampleOffset += ROUND_UP_32(dataLen);
entryDNA.binarySize(adpcmOffset);
entries.push_back(std::make_pair(entryDNA, adpcmParms));
}
}
adpcmOffset += 4;
for (auto& p : entries)
{
p.first.m_adpcmParmOffset = adpcmOffset;
p.first.write(fo);
adpcmOffset += sizeof(ADPCMParms::DSPParms);
}
const uint32_t term = 0xffffffff;
athena::io::Write<athena::io::PropType::None>::Do<decltype(term), DNAE>({}, term, fo);
for (auto& p : entries)
{
p.second.swapBigDSP();
fo.writeUBytes((uint8_t*)&p.second, sizeof(ADPCMParms::DSPParms));
}
return true;
}
} }

View File

@ -92,31 +92,62 @@ double BooBackendSubmix::getSampleRate() const { return m_booSubmix->getSampleRa
SubmixFormat BooBackendSubmix::getSampleFormat() const { return SubmixFormat(m_booSubmix->getSampleFormat()); } SubmixFormat BooBackendSubmix::getSampleFormat() const { return SubmixFormat(m_booSubmix->getSampleFormat()); }
std::string BooBackendMIDIReader::description() { return m_midiIn->description(); }
BooBackendMIDIReader::~BooBackendMIDIReader() {} BooBackendMIDIReader::~BooBackendMIDIReader() {}
BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name, bool useLock) BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, bool useLock)
: m_engine(engine), m_decoder(*this), m_useLock(useLock) : m_engine(engine), m_decoder(*this), m_useLock(useLock)
{ {
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend()); BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend());
if (!name) auto devices = voxAlloc.m_booEngine.enumerateMIDIInputs();
for (const auto& dev : devices)
{ {
auto devices = voxAlloc.m_booEngine.enumerateMIDIDevices(); auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(),
for (const auto& dev : devices)
{
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(
dev.first.c_str(),
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
if (m_midiIn)
return;
}
m_midiIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2)); std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
if (midiIn)
m_midiIns[dev.first] = std::move(midiIn);
}
if (voxAlloc.m_booEngine.supportsVirtualMIDIIn())
m_virtualIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
}
void BooBackendMIDIReader::addMIDIIn(const char* name)
{
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(m_engine.getBackend());
auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(name,
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
if (midiIn)
m_midiIns[name] = std::move(midiIn);
}
void BooBackendMIDIReader::removeMIDIIn(const char* name)
{
m_midiIns.erase(name);
}
bool BooBackendMIDIReader::hasMIDIIn(const char* name) const
{
return m_midiIns.find(name) != m_midiIns.cend();
}
void BooBackendMIDIReader::setVirtualIn(bool v)
{
if (v)
{
BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(m_engine.getBackend());
if (voxAlloc.m_booEngine.supportsVirtualMIDIIn())
m_virtualIn = voxAlloc.m_booEngine.newVirtualMIDIIn(
std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2));
} }
else else
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn( {
name, std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1, std::placeholders::_2)); m_virtualIn.reset();
}
}
bool BooBackendMIDIReader::hasVirtualIn() const
{
return m_virtualIn.operator bool();
} }
void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes, double time) void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes, double time)
@ -273,15 +304,12 @@ std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix&
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices() std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices()
{ {
return m_booEngine.enumerateMIDIDevices(); return m_booEngine.enumerateMIDIInputs();
} }
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine, const char* name) std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine)
{ {
std::unique_ptr<IMIDIReader> ret = std::make_unique<BooBackendMIDIReader>(engine, name, m_booEngine.useMIDILock()); return std::make_unique<BooBackendMIDIReader>(engine, m_booEngine.useMIDILock());
if (!static_cast<BooBackendMIDIReader&>(*ret).m_midiIn)
return {};
return ret;
} }
void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine) void BooBackendVoiceAllocator::setCallbackInterface(Engine* engine)

View File

@ -572,16 +572,42 @@ static void EncodeSignedValue(std::vector<uint8_t>& vecOut, int16_t val)
} }
} }
static uint32_t DecodeTimeRLE(const unsigned char*& data) static std::pair<uint32_t, int32_t> DecodeDelta(const unsigned char*& data)
{
std::pair<uint32_t, int32_t> ret = {};
do {
if (data[0] == 0x80 && data[1] == 0x00)
break;
ret.first += DecodeUnsignedValue(data);
ret.second = DecodeSignedValue(data);
} while (ret.second == 0);
return ret;
}
static void EncodeDelta(std::vector<uint8_t>& vecOut, uint32_t deltaTime, int32_t val)
{
while (deltaTime > 32767)
{
EncodeUnsignedValue(vecOut, 32767);
EncodeSignedValue(vecOut, 0);
deltaTime -= 32767;
}
EncodeUnsignedValue(vecOut, deltaTime);
EncodeSignedValue(vecOut, val);
}
static uint32_t DecodeTime(const unsigned char*& data)
{ {
uint32_t ret = 0; uint32_t ret = 0;
while (true) while (true)
{ {
uint16_t thisPart = SBig(*reinterpret_cast<const uint16_t*>(data)); uint16_t thisPart = SBig(*reinterpret_cast<const uint16_t*>(data));
if (thisPart == 0xffff) uint16_t nextPart = *reinterpret_cast<const uint16_t*>(data + 2);
if (nextPart == 0)
{ {
ret += 65535; // Automatically consume no-op command as continued time
ret += thisPart;
data += 4; data += 4;
continue; continue;
} }
@ -594,10 +620,11 @@ static uint32_t DecodeTimeRLE(const unsigned char*& data)
return ret; return ret;
} }
static void EncodeTimeRLE(std::vector<uint8_t>& vecOut, uint32_t val) static void EncodeTime(std::vector<uint8_t>& vecOut, uint32_t val)
{ {
while (val >= 65535) while (val >= 65535)
{ {
// Automatically emit no-op command as continued time
vecOut.push_back(0xff); vecOut.push_back(0xff);
vecOut.push_back(0xff); vecOut.push_back(0xff);
vecOut.push_back(0); vecOut.push_back(0);
@ -721,8 +748,9 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, int& v
clamp(0, trk.m_pitchVal + 0x2000, 0x4000)}); clamp(0, trk.m_pitchVal + 0x2000, 0x4000)});
if (trk.m_pitchWheelData[0] != 0x80 || trk.m_pitchWheelData[1] != 0x00) if (trk.m_pitchWheelData[0] != 0x80 || trk.m_pitchWheelData[1] != 0x00)
{ {
trk.m_nextPitchTick += DecodeUnsignedValue(trk.m_pitchWheelData); auto delta = DecodeDelta(trk.m_pitchWheelData);
trk.m_nextPitchDelta = DecodeSignedValue(trk.m_pitchWheelData); trk.m_nextPitchTick += delta.first;
trk.m_nextPitchDelta = delta.second;
} }
else else
{ {
@ -743,8 +771,9 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, int& v
uint8_t(clamp(0, trk.m_modVal / 128, 127)), 0}); uint8_t(clamp(0, trk.m_modVal / 128, 127)), 0});
if (trk.m_modWheelData[0] != 0x80 || trk.m_modWheelData[1] != 0x00) if (trk.m_modWheelData[0] != 0x80 || trk.m_modWheelData[1] != 0x00)
{ {
trk.m_nextModTick += DecodeUnsignedValue(trk.m_modWheelData); auto delta = DecodeDelta(trk.m_modWheelData);
trk.m_nextModDelta = DecodeSignedValue(trk.m_modWheelData); trk.m_nextModTick += delta.first;
trk.m_nextModDelta = delta.second;
} }
else else
{ {
@ -797,7 +826,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, int& v
} }
/* Set next delta-time */ /* Set next delta-time */
trk.m_eventWaitCountdown += int32_t(DecodeTimeRLE(trk.m_data)); trk.m_eventWaitCountdown += int32_t(DecodeTime(trk.m_data));
} }
} }
else else
@ -1047,12 +1076,12 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
for (auto& prog : results) for (auto& prog : results)
{ {
bool didInit = false; bool didInit = false;
int startTick; int startTick = 0;
int lastEventTick; int lastEventTick = 0;
int lastPitchTick; int lastPitchTick = 0;
int lastPitchVal; int lastPitchVal = 0;
int lastModTick; int lastModTick = 0;
int lastModVal; int lastModVal = 0;
Region region; Region region;
for (auto& event : prog.second) for (auto& event : prog.second)
@ -1079,17 +1108,16 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
{ {
if (event.second.noteOrCtrl == 1) if (event.second.noteOrCtrl == 1)
{ {
EncodeUnsignedValue(region.modBuf, uint32_t(eventTick - lastModTick));
lastModTick = eventTick;
int newMod = event.second.velOrVal * 128; int newMod = event.second.velOrVal * 128;
EncodeSignedValue(region.modBuf, newMod - lastModVal); EncodeDelta(region.modBuf, eventTick - lastModTick, newMod - lastModVal);
lastModTick = eventTick;
lastModVal = newMod; lastModVal = newMod;
} }
else else
{ {
if (version == 1) if (version == 1)
{ {
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick)); EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick; lastEventTick = eventTick;
region.eventBuf.push_back(0x80 | event.second.velOrVal); region.eventBuf.push_back(0x80 | event.second.velOrVal);
region.eventBuf.push_back(0x80 | event.second.noteOrCtrl); region.eventBuf.push_back(0x80 | event.second.noteOrCtrl);
@ -1120,7 +1148,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
{ {
if (version == 1) if (version == 1)
{ {
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick)); EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick; lastEventTick = eventTick;
region.eventBuf.push_back(0x80 | event.second.program); region.eventBuf.push_back(0x80 | event.second.program);
region.eventBuf.push_back(0); region.eventBuf.push_back(0);
@ -1148,10 +1176,9 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
} }
case Event::Type::Pitch: case Event::Type::Pitch:
{ {
EncodeUnsignedValue(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
lastPitchTick = eventTick;
int newPitch = event.second.pitchBend - 0x2000; int newPitch = event.second.pitchBend - 0x2000;
EncodeSignedValue(region.pitchBuf, newPitch - lastPitchVal); EncodeDelta(region.modBuf, eventTick - lastPitchTick, newPitch - lastPitchVal);
lastPitchTick = eventTick;
lastPitchVal = newPitch; lastPitchVal = newPitch;
break; break;
} }
@ -1159,7 +1186,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
{ {
if (version == 1) if (version == 1)
{ {
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick)); EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick; lastEventTick = eventTick;
region.eventBuf.push_back(event.second.noteOrCtrl); region.eventBuf.push_back(event.second.noteOrCtrl);
region.eventBuf.push_back(event.second.velOrVal); region.eventBuf.push_back(event.second.velOrVal);
@ -1219,7 +1246,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
if (lastModTick > lastEventTick) if (lastModTick > lastEventTick)
modDelta = lastModTick - lastEventTick; modDelta = lastModTick - lastEventTick;
EncodeTimeRLE(region.eventBuf, std::max(pitchDelta, modDelta)); EncodeTime(region.eventBuf, std::max(pitchDelta, modDelta));
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0xff);
region.eventBuf.push_back(0xff); region.eventBuf.push_back(0xff);
} }

View File

@ -39,16 +39,30 @@ static int16_t DecodeSignedValue(const unsigned char*& data)
return ret; return ret;
} }
static uint32_t DecodeTimeRLE(const unsigned char*& data) static std::pair<uint32_t, int32_t> DecodeDelta(const unsigned char*& data)
{
std::pair<uint32_t, int32_t> ret = {};
do {
if (data[0] == 0x80 && data[1] == 0x00)
break;
ret.first += DecodeUnsignedValue(data);
ret.second = DecodeSignedValue(data);
} while (ret.second == 0);
return ret;
}
static uint32_t DecodeTime(const unsigned char*& data)
{ {
uint32_t ret = 0; uint32_t ret = 0;
while (true) while (true)
{ {
uint16_t thisPart = SBig(*reinterpret_cast<const uint16_t*>(data)); uint16_t thisPart = SBig(*reinterpret_cast<const uint16_t*>(data));
if (thisPart == 0xffff) uint16_t nextPart = *reinterpret_cast<const uint16_t*>(data + 2);
if (nextPart == 0)
{ {
ret += 65535; // Automatically consume no-op command as continued time
ret += thisPart;
data += 4; data += 4;
continue; continue;
} }
@ -118,8 +132,9 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
m_pitchWheelData = m_parent->m_songData + header.m_pitchOff; m_pitchWheelData = m_parent->m_songData + header.m_pitchOff;
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00) if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
{ {
m_nextPitchTick = m_parent->m_curTick + DecodeUnsignedValue(m_pitchWheelData); auto delta = DecodeDelta(m_pitchWheelData);
m_nextPitchDelta = DecodeSignedValue(m_pitchWheelData); m_nextPitchTick = m_parent->m_curTick + delta.first;
m_nextPitchDelta = delta.second;
} }
} }
@ -131,8 +146,9 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
m_modWheelData = m_parent->m_songData + header.m_modOff; m_modWheelData = m_parent->m_songData + header.m_modOff;
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00) if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
{ {
m_nextModTick = m_parent->m_curTick + DecodeUnsignedValue(m_modWheelData); auto delta = DecodeDelta(m_modWheelData);
m_nextModDelta = DecodeSignedValue(m_modWheelData); m_nextModTick = m_parent->m_curTick + delta.first;
m_nextModDelta = delta.second;
} }
} }
@ -145,7 +161,7 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_modVal * 128 / 16384, 127)); seq->setCtrlValue(m_midiChan, 1, clamp(0, m_modVal * 128 / 16384, 127));
} }
if (m_parent->m_sngVersion == 1) if (m_parent->m_sngVersion == 1)
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data)); m_eventWaitCountdown = int32_t(DecodeTime(m_data));
else else
{ {
int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data)) int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data))
@ -231,10 +247,7 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
{ {
const unsigned char* dptr = ptr + header.m_pitchOff; const unsigned char* dptr = ptr + header.m_pitchOff;
while (dptr[0] != 0x80 || dptr[1] != 0x00) while (dptr[0] != 0x80 || dptr[1] != 0x00)
{ DecodeDelta(dptr);
DecodeUnsignedValue(dptr);
DecodeSignedValue(dptr);
}
dptr += 2; dptr += 2;
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd)) if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
continue; continue;
@ -245,10 +258,7 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
{ {
const unsigned char* dptr = ptr + header.m_modOff; const unsigned char* dptr = ptr + header.m_modOff;
while (dptr[0] != 0x80 || dptr[1] != 0x00) while (dptr[0] != 0x80 || dptr[1] != 0x00)
{ DecodeDelta(dptr);
DecodeUnsignedValue(dptr);
DecodeSignedValue(dptr);
}
dptr += 2; dptr += 2;
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd)) if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
continue; continue;
@ -261,7 +271,7 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
while (true) while (true)
{ {
/* Delta time */ /* Delta time */
DecodeTimeRLE(data); DecodeTime(data);
/* Load next command */ /* Load next command */
if (*reinterpret_cast<const uint16_t*>(data) == 0xffff) if (*reinterpret_cast<const uint16_t*>(data) == 0xffff)
@ -427,8 +437,9 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 8191.f, 1.f)); seq.setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 8191.f, 1.f));
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00) if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
{ {
m_nextPitchTick += DecodeUnsignedValue(m_pitchWheelData); auto delta = DecodeDelta(m_pitchWheelData);
m_nextPitchDelta = DecodeSignedValue(m_pitchWheelData); m_nextPitchTick += delta.first;
m_nextPitchDelta = delta.second;
} }
else else
{ {
@ -456,8 +467,9 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_modVal / 128, 127)); seq.setCtrlValue(m_midiChan, 1, clamp(0, m_modVal / 128, 127));
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00) if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
{ {
m_nextModTick += DecodeUnsignedValue(m_modWheelData); auto delta = DecodeDelta(m_modWheelData);
m_nextModDelta = DecodeSignedValue(m_modWheelData); m_nextModTick += delta.first;
m_nextModDelta = delta.second;
} }
else else
{ {
@ -519,7 +531,7 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
} }
/* Set next delta-time */ /* Set next delta-time */
m_eventWaitCountdown += int32_t(DecodeTimeRLE(m_data)); m_eventWaitCountdown += int32_t(DecodeTime(m_data));
} }
} }
else else