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 "MainWindow.hpp"
MIDIReader::MIDIReader(amuse::Engine& engine, const char* name, bool useLock)
: amuse::BooBackendMIDIReader(engine, name, useLock) {}
MIDIReader::MIDIReader(amuse::Engine& engine, bool useLock)
: amuse::BooBackendMIDIReader(engine, useLock) {}
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[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*/) {}
@ -120,10 +122,7 @@ VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: amuse::BooBackendVoiceAllocator(booEngine) {}
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());
if (!static_cast<MIDIReader&>(*ret).getMidiIn())
return {};
return ret;
return std::make_unique<MIDIReader>(engine, m_booEngine.useMIDILock());
}

View File

@ -12,8 +12,7 @@ class MIDIReader : public amuse::BooBackendMIDIReader
std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
amuse::ObjToken<amuse::Voice> m_lastVoice;
public:
MIDIReader(amuse::Engine& engine, const char* name, bool useLock);
boo::IMIDIIn* getMidiIn() const { return m_midiIn.get(); }
MIDIReader(amuse::Engine& engine, bool useLock);
void noteOff(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:
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

View File

@ -47,6 +47,8 @@ MainWindow::MainWindow(QWidget* parent)
m_ui.statusbar->connectFXPressed(this, SLOT(fxPressed()));
m_ui.statusbar->setVolumeValue(70);
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.velocitySlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
@ -299,12 +301,25 @@ bool MainWindow::setProjectPath(const QString& path)
void MainWindow::refreshAudioIO()
{
QList<QAction*> audioActions = m_ui.menuAudio->actions();
if (audioActions.size() > 3)
for (auto it = audioActions.begin() + 3 ; it != audioActions.end() ; ++it)
if (audioActions.size() > 2)
for (auto it = audioActions.begin() + 2 ; it != audioActions.end() ; ++it)
m_ui.menuAudio->removeAction(*it);
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)
m_ui.menuAudio->addAction(tr("No Audio Devices Found"))->setEnabled(false);
@ -320,11 +335,22 @@ void MainWindow::refreshMIDIIO()
bool addedDev = false;
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));
act->setCheckable(true);
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;
}
}
@ -414,13 +440,13 @@ void MainWindow::keyReleaseEvent(QKeyEvent* ev)
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)
{
assert(m_backgroundTask == nullptr && "existing background process");
setEnabled(false);
m_backgroundTask = new BackgroundTask(std::move(task));
m_backgroundTask = new BackgroundTask(id, std::move(task));
m_backgroundTask->moveToThread(&m_backgroundThread);
m_backgroundDialog = new QProgressDialog(this);
@ -442,8 +468,8 @@ void MainWindow::startBackgroundTask(const QString& windowTitle, const QString&
m_backgroundDialog, SLOT(setValue(int)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(setLabelText(const QString&)),
m_backgroundDialog, SLOT(setLabelText(const QString&)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(finished()),
this, SLOT(onBackgroundTaskFinished()), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(finished(int)),
this, SLOT(onBackgroundTaskFinished(int)), Qt::QueuedConnection);
m_backgroundDialog->open(m_backgroundTask, SLOT(cancel()));
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->setPitchWheel(m_pitch);
vox->installCtrlValues(m_ctrlVals);
vox->setReverbVol(m_auxAVol);
vox->setAuxBVol(m_auxBVol);
}
}
return vox;
@ -589,7 +617,10 @@ amuse::ObjToken<amuse::Voice> MainWindow::startSFX(amuse::GroupId groupId, amuse
if (ProjectModel::INode* node = getEditorNode())
{
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 {};
}
@ -600,7 +631,13 @@ amuse::ObjToken<amuse::Sequencer> MainWindow::startSong(amuse::GroupId groupId,
if (ProjectModel::INode* node = getEditorNode())
{
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 {};
}
@ -702,7 +739,7 @@ bool MainWindow::openProject(const QString& path)
return false;
ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Opening"), tr("Scanning Project"),
startBackgroundTask(TaskOpen, tr("Opening"), tr("Scanning Project"),
[dir, model](BackgroundTask& task)
{
QStringList childDirs = dir.entryList(QDir::Dirs);
@ -710,6 +747,8 @@ bool MainWindow::openProject(const QString& path)
{
if (task.isCanceled())
return;
if (chDir == QStringLiteral("out"))
continue;
QString chPath = dir.filePath(chDir);
if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() &&
QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists())
@ -785,7 +824,7 @@ void MainWindow::reloadSampleDataAction()
if (!dir.exists())
return;
startBackgroundTask(tr("Reloading Samples"), tr("Scanning Project"),
startBackgroundTask(TaskReloadSamples, tr("Reloading Samples"), tr("Scanning Project"),
[dir, model](BackgroundTask& task)
{
QStringList childDirs = dir.entryList(QDir::Dirs);
@ -793,6 +832,8 @@ void MainWindow::reloadSampleDataAction()
{
if (task.isCanceled())
return;
if (chDir == QStringLiteral("out"))
continue;
QString chPath = dir.filePath(chDir);
if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() &&
QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists())
@ -869,7 +910,7 @@ void MainWindow::importAction()
}
ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Importing"), tr("Scanning Project"),
startBackgroundTask(TaskImport, tr("Importing"), tr("Scanning Project"),
[model, path, importMode](BackgroundTask& task)
{
QDir dir = QFileInfo(path).dir();
@ -914,7 +955,7 @@ void MainWindow::importAction()
}
ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Importing"), tr("Scanning Project"),
startBackgroundTask(TaskImport, tr("Importing"), tr("Scanning Project"),
[model, path, importMode](BackgroundTask& task)
{
/* Handle single container */
@ -949,7 +990,32 @@ void MainWindow::importSongsAction()
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,
@ -1177,13 +1243,32 @@ void MainWindow::aboutToShowMIDIIOMenu()
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
//qobject_cast<QAction*>(sender())->data().toString().toUtf8().constData();
QAction* action = qobject_cast<QAction*>(sender());
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)
@ -1243,6 +1328,26 @@ void MainWindow::volumeChanged(int vol)
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()
{
@ -1399,16 +1504,32 @@ void MainWindow::studioSetupShown()
m_ui.statusbar->setFXDown(true);
}
void MainWindow::onBackgroundTaskFinished()
void MainWindow::onBackgroundTaskFinished(int id)
{
m_backgroundDialog->reset();
m_backgroundDialog->deleteLater();
m_backgroundDialog = nullptr;
m_backgroundTask->deleteLater();
m_backgroundTask = nullptr;
bool hasGroups = m_projectModel->ensureModelData();
m_ui.actionImport_Groups->setDisabled(hasGroups);
m_ui.actionImport_Songs->setEnabled(hasGroups);
if (id == TaskExport)
{
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);
}

View File

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

View File

@ -581,6 +581,48 @@ bool ProjectModel::saveToFile(UIMessenger& messenger)
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)
{
amuse::AudioGroup& group = gn.m_it->second;

View File

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

View File

@ -1150,7 +1150,7 @@ SetupTableView::SetupTableView(QWidget* parent)
void ColoredTabBarStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option,
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);
switch (optionTab.position)
@ -1171,10 +1171,9 @@ void ColoredTabBarStyle::drawControl(QStyle::ControlElement element, const QStyl
}
ColoredTabBar::ColoredTabBar(QWidget* parent)
: QTabBar(parent), m_style(new ColoredTabBarStyle(style()))
: QTabBar(parent)
{
setDrawBase(false);
setStyle(m_style);
}
ColoredTabWidget::ColoredTabWidget(QWidget* parent)

View File

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

View File

@ -8,7 +8,8 @@ FXButton::FXButton(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);
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[2] = QIcon(QStringLiteral(":/icons/IconVolume2"));
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.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)));
m_volumeSlider.setRange(0, 100);
m_volumeSlider.setFixedWidth(100);
m_volumeSlider.setToolTip(volTip);
addPermanentWidget(&m_voiceCount);
addPermanentWidget(&m_killButton);
addPermanentWidget(&m_fxButton);
addPermanentWidget(&m_aIcon);
addPermanentWidget(&m_aSlider);
addPermanentWidget(&m_bIcon);
addPermanentWidget(&m_bSlider);
addPermanentWidget(&m_volumeIcon);
addPermanentWidget(&m_volumeSlider);
}

View File

@ -31,6 +31,10 @@ class StatusBarWidget : public QStatusBar
QIcon m_volumeIcons[4];
QLabel m_volumeIcon;
QSlider m_volumeSlider;
QLabel m_aIcon;
QSlider m_aSlider;
QLabel m_bIcon;
QSlider m_bSlider;
int m_lastVolIdx = 0;
QLabel m_voiceCount;
int m_cachedVoiceCount = -1;
@ -47,6 +51,10 @@ public:
void setFXDown(bool down) { m_fxButton.setDown(down); }
void connectVolumeSlider(const QObject* receiver, const char* 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); }
private slots:

View File

@ -4,6 +4,7 @@
#include "amuse/EffectReverb.hpp"
#include <QPainter>
#include <QScrollBar>
#include <QStylePainter>
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)
: QWidget(nullptr), m_effect(effect), m_introspection(GetEffectIntrospection(type))
{
@ -258,8 +343,6 @@ EffectWidget::EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType
titleFont.setWeight(QFont::Bold);
m_titleLabel.setFont(titleFont);
m_titleLabel.setForegroundRole(QPalette::Background);
//m_titleLabel.setAutoFillBackground(true);
//m_titleLabel.setBackgroundRole(QPalette::Text);
m_titleLabel.setContentsMargins(46, 0, 0, 0);
m_titleLabel.setFixedHeight(20);
m_numberText.setTextOption(QTextOption(Qt::AlignRight));
@ -315,6 +398,16 @@ EffectWidget::EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType
layout->addWidget(sb, 1, f);
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:
{
FieldDoubleSlider* sb = new FieldDoubleSlider;
@ -397,6 +490,11 @@ void EffectWidget::numChanged(double 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()
{
if (m_index != -1)

View File

@ -13,6 +13,8 @@
#include <QSplitter>
#include "amuse/Studio.hpp"
class EffectListing;
struct EffectIntrospection
{
struct Field
@ -34,7 +36,31 @@ struct EffectIntrospection
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
{
@ -51,13 +77,14 @@ class EffectWidget : public QWidget
private slots:
void numChanged(int);
void numChanged(double);
void chanNumChanged(int, int);
void deleteClicked();
private:
EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType type);
explicit EffectWidget(amuse::EffectBaseTypeless* effect, amuse::EffectType type);
public:
EffectListing* getParent() const;
EffectWidget(amuse::EffectBaseTypeless* effect);
EffectWidget(amuse::EffectType op);
explicit EffectWidget(amuse::EffectBaseTypeless* effect);
explicit EffectWidget(amuse::EffectType op);
void paintEvent(QPaintEvent* event);
QString getText() const { return m_titleLabel.text(); }
};

View File

@ -3,6 +3,7 @@
#include <QStyleFactory>
#include <QTranslator>
#include "MainWindow.hpp"
#include "SongGroupEditor.hpp"
#include "boo/IApplication.hpp"
#include <QResource>
#include <QCommandLineParser>
@ -61,7 +62,7 @@ int main(int argc, char* argv[])
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication::setStyle(QStyleFactory::create("Fusion"));
QApplication::setStyle(new ColoredTabBarStyle(QStyleFactory::create("Fusion")));
QApplication a(argc, argv);
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,10 +63,10 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
transform="translate(0,-291.70832)">
<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"
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"
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.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841"
inkscape:connector-curvature="0"
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,16 +63,16 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
transform="translate(0,-291.70832)">
<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"
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"
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.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;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"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.2411458,293.36197 v 1.98437 h 0.3307293 v -1.98437 z"
id="path825"
inkscape:connector-curvature="0" />
</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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,21 +63,21 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
transform="translate(0,-291.70832)">
<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"
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"
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.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;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"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.2411458,293.36197 v 1.98437 h 0.3307293 v -1.98437 z"
id="path825"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;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"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.9026042,292.7005 v 3.30729 h 0.3307292 v -3.30729 z"
id="path827"
inkscape:connector-curvature="0"
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
width="20"
height="20"
viewBox="0 0 5.2916665 5.2916669"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
@ -63,27 +63,27 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
transform="translate(0,-291.70832)">
<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"
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"
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.03123 0.0122188,2.64584 H 1.2567708 l 1.6536459,1.32291 v -5.29166 l -1.6536459,1.32291 z"
id="path841"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;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"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.2411458,293.36197 v 1.98437 h 0.3307293 v -1.98437 z"
id="path825"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;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"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.9026042,292.7005 v 3.30729 h 0.3307292 v -3.30729 z"
id="path827"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;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"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.33072916px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4.5640625,292.03904 v 4.63021 h 0.3307292 v -4.63021 z"
id="path829"
inkscape:connector-curvature="0" />
</g>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -149,26 +149,26 @@
<context>
<name>EffectCatalogue</name>
<message>
<location filename="../StudioSetupWidget.cpp" line="779"/>
<location filename="../StudioSetupWidget.cpp" line="787"/>
<location filename="../StudioSetupWidget.cpp" line="877"/>
<location filename="../StudioSetupWidget.cpp" line="885"/>
<source>Reverb Standard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="780"/>
<location filename="../StudioSetupWidget.cpp" line="788"/>
<location filename="../StudioSetupWidget.cpp" line="878"/>
<location filename="../StudioSetupWidget.cpp" line="886"/>
<source>Reverb High</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="781"/>
<location filename="../StudioSetupWidget.cpp" line="789"/>
<location filename="../StudioSetupWidget.cpp" line="879"/>
<location filename="../StudioSetupWidget.cpp" line="887"/>
<source>Delay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="782"/>
<location filename="../StudioSetupWidget.cpp" line="790"/>
<location filename="../StudioSetupWidget.cpp" line="880"/>
<location filename="../StudioSetupWidget.cpp" line="888"/>
<source>Chorus</source>
<translation type="unfinished"></translation>
</message>
@ -350,13 +350,13 @@
<context>
<name>MIDIPlayerWidget</name>
<message>
<location filename="../SongGroupEditor.cpp" line="1193"/>
<location filename="../SongGroupEditor.cpp" line="1192"/>
<source>Stop</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1207"/>
<location filename="../SongGroupEditor.cpp" line="1230"/>
<location filename="../SongGroupEditor.cpp" line="1206"/>
<location filename="../SongGroupEditor.cpp" line="1229"/>
<source>Play</source>
<translation type="unfinished"></translation>
</message>
@ -559,284 +559,310 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="81"/>
<location filename="../MainWindow.cpp" line="83"/>
<source>Clear Recent Projects</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="87"/>
<location filename="../MainWindow.cpp" line="89"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="203"/>
<location filename="../MainWindow.cpp" line="205"/>
<source>Amuse[*]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="211"/>
<location filename="../MainWindow.cpp" line="213"/>
<source>%1/%2/%3[*] - Amuse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="216"/>
<location filename="../MainWindow.cpp" line="218"/>
<source>%1[*] - Amuse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="244"/>
<location filename="../MainWindow.cpp" line="683"/>
<location filename="../MainWindow.cpp" line="246"/>
<location filename="../MainWindow.cpp" line="720"/>
<source>The directory at &apos;%1&apos; must not be empty.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="245"/>
<location filename="../MainWindow.cpp" line="684"/>
<location filename="../MainWindow.cpp" line="247"/>
<location filename="../MainWindow.cpp" line="721"/>
<source>Directory empty</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="251"/>
<location filename="../MainWindow.cpp" line="253"/>
<source>Directory does not exist</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="254"/>
<location filename="../MainWindow.cpp" line="256"/>
<source>__amuse_test__</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="261"/>
<location filename="../MainWindow.cpp" line="263"/>
<source>Unable to write to directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="310"/>
<location filename="../MainWindow.cpp" line="325"/>
<source>No Audio Devices Found</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="389"/>
<location filename="../MainWindow.cpp" line="415"/>
<source>SUSTAIN</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="635"/>
<location filename="../MainWindow.cpp" line="672"/>
<source>Unsaved Changes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="635"/>
<location filename="../MainWindow.cpp" line="672"/>
<source>Save Changes in %1?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="664"/>
<location filename="../MainWindow.cpp" line="701"/>
<source>New Project</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="690"/>
<location filename="../MainWindow.cpp" line="727"/>
<source>Bad Directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="705"/>
<location filename="../MainWindow.cpp" line="742"/>
<source>Opening</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="705"/>
<location filename="../MainWindow.cpp" line="788"/>
<location filename="../MainWindow.cpp" line="872"/>
<location filename="../MainWindow.cpp" line="917"/>
<location filename="../MainWindow.cpp" line="742"/>
<location filename="../MainWindow.cpp" line="827"/>
<location filename="../MainWindow.cpp" line="913"/>
<location filename="../MainWindow.cpp" line="958"/>
<location filename="../MainWindow.cpp" line="1003"/>
<source>Scanning Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="717"/>
<location filename="../MainWindow.cpp" line="756"/>
<source>Opening %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="730"/>
<location filename="../MainWindow.cpp" line="769"/>
<source>Open Project</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="788"/>
<location filename="../MainWindow.cpp" line="827"/>
<source>Reloading Samples</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="800"/>
<location filename="../MainWindow.cpp" line="841"/>
<source>Scanning %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="810"/>
<location filename="../MainWindow.cpp" line="851"/>
<source>Import Project</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="821"/>
<location filename="../MainWindow.cpp" line="862"/>
<source>Unsupported MusyX Container</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="826"/>
<location filename="../MainWindow.cpp" line="867"/>
<source>Sample Import Mode</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="831"/>
<location filename="../MainWindow.cpp" line="872"/>
<source>Import Compressed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="831"/>
<location filename="../MainWindow.cpp" line="872"/>
<source>Import WAVs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="831"/>
<location filename="../MainWindow.cpp" line="872"/>
<source>Import Both</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="847"/>
<location filename="../MainWindow.cpp" line="888"/>
<source>Raw Import Mode</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="858"/>
<location filename="../MainWindow.cpp" line="899"/>
<source>Project Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="858"/>
<location filename="../MainWindow.cpp" line="899"/>
<source>What should this project be named?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="872"/>
<location filename="../MainWindow.cpp" line="917"/>
<location filename="../MainWindow.cpp" line="913"/>
<location filename="../MainWindow.cpp" line="958"/>
<source>Importing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="884"/>
<location filename="../MainWindow.cpp" line="926"/>
<location filename="../MainWindow.cpp" line="925"/>
<location filename="../MainWindow.cpp" line="967"/>
<source>Importing %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="942"/>
<location filename="../MainWindow.cpp" line="983"/>
<source>Import Songs</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1039"/>
<location filename="../MainWindow.cpp" line="1105"/>
<source>New SFX Group</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1058"/>
<location filename="../MainWindow.cpp" line="1124"/>
<source>New Song Group</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1100"/>
<location filename="../MainWindow.cpp" line="1166"/>
<source>New ADSR</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1119"/>
<location filename="../MainWindow.cpp" line="1185"/>
<source>New Curve</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1138"/>
<location filename="../MainWindow.cpp" line="1204"/>
<source>New Keymap</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1157"/>
<location filename="../MainWindow.cpp" line="1223"/>
<source>New Layers</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="1024"/>
<location filename="../MainWindow.cpp" line="1090"/>
<source>What should this subproject be named?</source>
<translation type="unfinished"></translation>
</message>
@ -1055,187 +1081,215 @@
<name>ProjectModel</name>
<message>
<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>
</message>
<message>
<location filename="../ProjectModel.cpp" line="620"/>
<source>ADSRs</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="631"/>
<source>Curves</source>
<source>Unable to export %1.sdir</source>
<translation type="unfinished"></translation>
</message>
<message>
<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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="650"/>
<location filename="../ProjectModel.cpp" line="692"/>
<source>Layers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="657"/>
<location filename="../ProjectModel.cpp" line="699"/>
<source>Samples</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="922"/>
<location filename="../ProjectModel.cpp" line="964"/>
<source>Subproject Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="922"/>
<location filename="../ProjectModel.cpp" line="964"/>
<source>The subproject %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="927"/>
<location filename="../ProjectModel.cpp" line="969"/>
<source>Add Subproject %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="977"/>
<location filename="../ProjectModel.cpp" line="1019"/>
<source>Sound Group Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="977"/>
<location filename="../ProjectModel.cpp" line="1002"/>
<location filename="../ProjectModel.cpp" line="1019"/>
<location filename="../ProjectModel.cpp" line="1044"/>
<source>The group %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="981"/>
<location filename="../ProjectModel.cpp" line="1023"/>
<source>Add Sound Group %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1002"/>
<location filename="../ProjectModel.cpp" line="1044"/>
<source>Song Group Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1006"/>
<location filename="../ProjectModel.cpp" line="1048"/>
<source>Add Song Group %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1087"/>
<location filename="../ProjectModel.cpp" line="1129"/>
<source>Sound Macro Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1087"/>
<location filename="../ProjectModel.cpp" line="1129"/>
<source>The macro %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1097"/>
<location filename="../ProjectModel.cpp" line="1139"/>
<source>Add Sound Macro %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1118"/>
<location filename="../ProjectModel.cpp" line="1160"/>
<source>ADSR Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1118"/>
<location filename="../ProjectModel.cpp" line="1160"/>
<source>The ADSR %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1124"/>
<location filename="../ProjectModel.cpp" line="1166"/>
<source>Add ADSR %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1145"/>
<location filename="../ProjectModel.cpp" line="1187"/>
<source>Curve Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1145"/>
<location filename="../ProjectModel.cpp" line="1187"/>
<source>The Curve %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1151"/>
<location filename="../ProjectModel.cpp" line="1193"/>
<source>Add Curve %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1172"/>
<location filename="../ProjectModel.cpp" line="1214"/>
<source>Keymap Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1172"/>
<location filename="../ProjectModel.cpp" line="1214"/>
<source>The Keymap %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1177"/>
<location filename="../ProjectModel.cpp" line="1219"/>
<source>Add Keymap %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1198"/>
<location filename="../ProjectModel.cpp" line="1240"/>
<source>Layers Conflict</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1198"/>
<location filename="../ProjectModel.cpp" line="1240"/>
<source>Layers %1 is already defined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1203"/>
<location filename="../ProjectModel.cpp" line="1245"/>
<source>Add Layers %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1217"/>
<location filename="../ProjectModel.cpp" line="1259"/>
<source>Delete Subproject %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1220"/>
<location filename="../ProjectModel.cpp" line="1262"/>
<source>Delete SongGroup %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1223"/>
<location filename="../ProjectModel.cpp" line="1265"/>
<source>Delete SFXGroup %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1226"/>
<location filename="../ProjectModel.cpp" line="1268"/>
<source>Delete SoundMacro %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1229"/>
<location filename="../ProjectModel.cpp" line="1271"/>
<source>Delete ADSR %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1232"/>
<location filename="../ProjectModel.cpp" line="1274"/>
<source>Delete Curve %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1235"/>
<location filename="../ProjectModel.cpp" line="1277"/>
<source>Delete Keymap %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="1238"/>
<location filename="../ProjectModel.cpp" line="1280"/>
<source>Delete Layers %1</source>
<translation type="unfinished"></translation>
</message>
@ -1481,37 +1535,37 @@
<context>
<name>SongGroupEditor</name>
<message>
<location filename="../SongGroupEditor.cpp" line="1502"/>
<location filename="../SongGroupEditor.cpp" line="1501"/>
<source>Add new page entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1504"/>
<location filename="../SongGroupEditor.cpp" line="1503"/>
<source>Remove selected page entries</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1471"/>
<location filename="../SongGroupEditor.cpp" line="1470"/>
<source>Normal Pages</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1301"/>
<location filename="../SongGroupEditor.cpp" line="1300"/>
<source>Add Page Entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1313"/>
<location filename="../SongGroupEditor.cpp" line="1312"/>
<source>Add Setup Entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1472"/>
<location filename="../SongGroupEditor.cpp" line="1471"/>
<source>Drum Pages</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SongGroupEditor.cpp" line="1473"/>
<location filename="../SongGroupEditor.cpp" line="1472"/>
<source>MIDI Setups</source>
<translation type="unfinished"></translation>
</message>
@ -1636,25 +1690,40 @@
<context>
<name>StatusBarWidget</name>
<message>
<location filename="../StatusBarWidget.cpp" line="16"/>
<location filename="../StatusBarWidget.cpp" line="17"/>
<source>Immediately kill active voices</source>
<translation type="unfinished"></translation>
</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>
<name>StudioSetupWidget</name>
<message>
<location filename="../StudioSetupWidget.cpp" line="1017"/>
<location filename="../StudioSetupWidget.cpp" line="1115"/>
<source>Studio Setup</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="1035"/>
<location filename="../StudioSetupWidget.cpp" line="1133"/>
<source>Aux A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../StudioSetupWidget.cpp" line="1036"/>
<location filename="../StudioSetupWidget.cpp" line="1134"/>
<source>Aux B</source>
<translation type="unfinished"></translation>
</message>
@ -1667,6 +1736,57 @@
<translation type="unfinished"></translation>
</message>
</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>
<name>VelocitySlider</name>
<message>

View File

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

View File

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

View File

@ -1132,6 +1132,8 @@ struct SoundMacro
template <athena::Endian DNAE>
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)
{
@ -1423,6 +1425,8 @@ public:
const Curve* tableAsCurves(ObjectId id) const;
bool toYAML(SystemStringView groupPath) const;
template <athena::Endian DNAE>
bool toData(SystemStringView groupPath) const;
AudioGroupPool(const AudioGroupPool&) = delete;
AudioGroupPool& operator=(const AudioGroupPool&) = delete;

View File

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

View File

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

View File

@ -73,7 +73,8 @@ class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
friend class BooBackendVoiceAllocator;
protected:
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;
bool m_useLock;
@ -83,9 +84,14 @@ protected:
public:
~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 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<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut, int busId);
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);
AudioChannelSet getAvailableSet();
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; });
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

View File

@ -75,6 +75,9 @@ public:
/** Access voice backend of engine */
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! */
const AudioGroup* addAudioGroup(const AudioGroupData& data);

View File

@ -28,7 +28,6 @@ class IMIDIReader
{
public:
virtual ~IMIDIReader() = default;
virtual std::string description() = 0;
virtual void pumpReader(double dt) = 0;
};
@ -48,7 +47,7 @@ public:
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() = 0;
/** 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 */
virtual AudioChannelSet getAvailableSet() = 0;

View File

@ -1,3 +1,4 @@
#include <athena/MemoryWriter.hpp>
#include "amuse/AudioGroupPool.hpp"
#include "amuse/Common.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::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)
{
m_cmds.reserve(other.m_cmds.size());
@ -1063,6 +1079,126 @@ bool AudioGroupPool::toYAML(SystemStringView groupPath) const
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 <>
void amuse::Curve::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& r)
{

View File

@ -1,4 +1,6 @@
#include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/AudioGroupData.hpp"
#include "athena/MemoryReader.hpp"
#include "athena/FileWriter.hpp"
@ -7,6 +9,13 @@
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)
{
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)
{
while (!AtEnd32(r))
@ -91,7 +131,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
/* Normal pages */
r.seek(header.pageTableOff, athena::Begin);
while (!AtEnd16(r))
while (!AtEnd64(r))
{
SongGroupIndex::PageEntryDNA<athena::Big> entry;
entry.read(r);
@ -100,7 +140,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
/* Drum pages */
r.seek(header.drumTableOff, athena::Begin);
while (!AtEnd16(r))
while (!AtEnd64(r))
{
SongGroupIndex::PageEntryDNA<athena::Big> entry;
entry.read(r);
@ -698,4 +738,243 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
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/N64MusyXCodec.hpp"
#include "amuse/DirectoryEnumerator.hpp"
#include "amuse/AudioGroup.hpp"
#include "athena/MemoryReader.hpp"
#include "athena/FileWriter.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()); }
std::string BooBackendMIDIReader::description() { return m_midiIn->description(); }
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)
{
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();
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(
auto midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(),
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
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)
@ -273,15 +304,12 @@ std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix&
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());
if (!static_cast<BooBackendMIDIReader&>(*ret).m_midiIn)
return {};
return ret;
return std::make_unique<BooBackendMIDIReader>(engine, m_booEngine.useMIDILock());
}
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;
while (true)
{
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;
continue;
}
@ -594,10 +620,11 @@ static uint32_t DecodeTimeRLE(const unsigned char*& data)
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)
{
// Automatically emit no-op command as continued time
vecOut.push_back(0xff);
vecOut.push_back(0xff);
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)});
if (trk.m_pitchWheelData[0] != 0x80 || trk.m_pitchWheelData[1] != 0x00)
{
trk.m_nextPitchTick += DecodeUnsignedValue(trk.m_pitchWheelData);
trk.m_nextPitchDelta = DecodeSignedValue(trk.m_pitchWheelData);
auto delta = DecodeDelta(trk.m_pitchWheelData);
trk.m_nextPitchTick += delta.first;
trk.m_nextPitchDelta = delta.second;
}
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});
if (trk.m_modWheelData[0] != 0x80 || trk.m_modWheelData[1] != 0x00)
{
trk.m_nextModTick += DecodeUnsignedValue(trk.m_modWheelData);
trk.m_nextModDelta = DecodeSignedValue(trk.m_modWheelData);
auto delta = DecodeDelta(trk.m_modWheelData);
trk.m_nextModTick += delta.first;
trk.m_nextModDelta = delta.second;
}
else
{
@ -797,7 +826,7 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, int& v
}
/* Set next delta-time */
trk.m_eventWaitCountdown += int32_t(DecodeTimeRLE(trk.m_data));
trk.m_eventWaitCountdown += int32_t(DecodeTime(trk.m_data));
}
}
else
@ -1047,12 +1076,12 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
for (auto& prog : results)
{
bool didInit = false;
int startTick;
int lastEventTick;
int lastPitchTick;
int lastPitchVal;
int lastModTick;
int lastModVal;
int startTick = 0;
int lastEventTick = 0;
int lastPitchTick = 0;
int lastPitchVal = 0;
int lastModTick = 0;
int lastModVal = 0;
Region region;
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)
{
EncodeUnsignedValue(region.modBuf, uint32_t(eventTick - lastModTick));
lastModTick = eventTick;
int newMod = event.second.velOrVal * 128;
EncodeSignedValue(region.modBuf, newMod - lastModVal);
EncodeDelta(region.modBuf, eventTick - lastModTick, newMod - lastModVal);
lastModTick = eventTick;
lastModVal = newMod;
}
else
{
if (version == 1)
{
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick;
region.eventBuf.push_back(0x80 | event.second.velOrVal);
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)
{
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick;
region.eventBuf.push_back(0x80 | event.second.program);
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:
{
EncodeUnsignedValue(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
lastPitchTick = eventTick;
int newPitch = event.second.pitchBend - 0x2000;
EncodeSignedValue(region.pitchBuf, newPitch - lastPitchVal);
EncodeDelta(region.modBuf, eventTick - lastPitchTick, newPitch - lastPitchVal);
lastPitchTick = eventTick;
lastPitchVal = newPitch;
break;
}
@ -1159,7 +1186,7 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
{
if (version == 1)
{
EncodeTimeRLE(region.eventBuf, uint32_t(eventTick - lastEventTick));
EncodeTime(region.eventBuf, uint32_t(eventTick - lastEventTick));
lastEventTick = eventTick;
region.eventBuf.push_back(event.second.noteOrCtrl);
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)
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);
}

View File

@ -39,16 +39,30 @@ static int16_t DecodeSignedValue(const unsigned char*& data)
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;
while (true)
{
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;
continue;
}
@ -118,8 +132,9 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
m_pitchWheelData = m_parent->m_songData + header.m_pitchOff;
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
{
m_nextPitchTick = m_parent->m_curTick + DecodeUnsignedValue(m_pitchWheelData);
m_nextPitchDelta = DecodeSignedValue(m_pitchWheelData);
auto delta = DecodeDelta(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;
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
{
m_nextModTick = m_parent->m_curTick + DecodeUnsignedValue(m_modWheelData);
m_nextModDelta = DecodeSignedValue(m_modWheelData);
auto delta = DecodeDelta(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));
}
if (m_parent->m_sngVersion == 1)
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data));
m_eventWaitCountdown = int32_t(DecodeTime(m_data));
else
{
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;
while (dptr[0] != 0x80 || dptr[1] != 0x00)
{
DecodeUnsignedValue(dptr);
DecodeSignedValue(dptr);
}
DecodeDelta(dptr);
dptr += 2;
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
continue;
@ -245,10 +258,7 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
{
const unsigned char* dptr = ptr + header.m_modOff;
while (dptr[0] != 0x80 || dptr[1] != 0x00)
{
DecodeUnsignedValue(dptr);
DecodeSignedValue(dptr);
}
DecodeDelta(dptr);
dptr += 2;
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
continue;
@ -261,7 +271,7 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
while (true)
{
/* Delta time */
DecodeTimeRLE(data);
DecodeTime(data);
/* Load next command */
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));
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
{
m_nextPitchTick += DecodeUnsignedValue(m_pitchWheelData);
m_nextPitchDelta = DecodeSignedValue(m_pitchWheelData);
auto delta = DecodeDelta(m_pitchWheelData);
m_nextPitchTick += delta.first;
m_nextPitchDelta = delta.second;
}
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));
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
{
m_nextModTick += DecodeUnsignedValue(m_modWheelData);
m_nextModDelta = DecodeSignedValue(m_modWheelData);
auto delta = DecodeDelta(m_modWheelData);
m_nextModTick += delta.first;
m_nextModDelta = delta.second;
}
else
{
@ -519,7 +531,7 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
}
/* Set next delta-time */
m_eventWaitCountdown += int32_t(DecodeTimeRLE(m_data));
m_eventWaitCountdown += int32_t(DecodeTime(m_data));
}
}
else