mirror of https://github.com/AxioDL/amuse.git
Implement amuse playback
This commit is contained in:
parent
cb24322fc1
commit
f5984141fd
|
@ -1 +0,0 @@
|
||||||
#include "AudioGroupModel.hpp"
|
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef AMUSE_AUDIO_GROUP_MODEL_HPP
|
|
||||||
#define AMUSE_AUDIO_GROUP_MODEL_HPP
|
|
||||||
|
|
||||||
#include "amuse/AudioGroup.hpp"
|
|
||||||
|
|
||||||
class AudioGroupModel
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class SFXGroupModel : public AudioGroupModel
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class SongGroupModel : public AudioGroupModel
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AMUSE_AUDIO_GROUP_MODEL_HPP
|
|
|
@ -47,7 +47,7 @@ add_executable(amuse-gui WIN32 MACOSX_BUNDLE
|
||||||
SampleEditor.hpp SampleEditor.cpp
|
SampleEditor.hpp SampleEditor.cpp
|
||||||
SoundGroupEditor.hpp SoundGroupEditor.cpp
|
SoundGroupEditor.hpp SoundGroupEditor.cpp
|
||||||
SongGroupEditor.hpp SongGroupEditor.cpp
|
SongGroupEditor.hpp SongGroupEditor.cpp
|
||||||
AudioGroupModel.hpp AudioGroupModel.cpp
|
MIDIReader.hpp MIDIReader.cpp
|
||||||
resources/resources.qrc qrc_resources.cpp
|
resources/resources.qrc qrc_resources.cpp
|
||||||
${QM_FILES} qrc_translation_res.cpp
|
${QM_FILES} qrc_translation_res.cpp
|
||||||
${PLAT_SRCS}
|
${PLAT_SRCS}
|
||||||
|
|
|
@ -13,6 +13,7 @@ public:
|
||||||
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
|
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
|
||||||
virtual bool valid() const { return true; }
|
virtual bool valid() const { return true; }
|
||||||
virtual void unloadData() {}
|
virtual void unloadData() {}
|
||||||
|
virtual ProjectModel::INode* currentNode() const { return nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditorUndoCommand : public QUndoCommand
|
class EditorUndoCommand : public QUndoCommand
|
||||||
|
|
|
@ -135,12 +135,12 @@ std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const
|
||||||
|
|
||||||
void KeyboardWidget::_startKey(int octave, int key)
|
void KeyboardWidget::_startKey(int octave, int key)
|
||||||
{
|
{
|
||||||
printf("START %d %d\n", octave, key);
|
emit notePressed(octave * 12 + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardWidget::_stopKey()
|
void KeyboardWidget::_stopKey()
|
||||||
{
|
{
|
||||||
printf("STOP\n");
|
emit noteReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardWidget::_moveOnKey(int octave, int key)
|
void KeyboardWidget::_moveOnKey(int octave, int key)
|
||||||
|
@ -209,7 +209,75 @@ void KeyboardWidget::showEvent(QShowEvent* event)
|
||||||
{
|
{
|
||||||
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
|
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
|
||||||
{
|
{
|
||||||
/* Scroll to C2 */
|
/* Scroll to C1 */
|
||||||
scroll->ensureVisible(141 * 3 + scroll->width(), 0, 0, 0);
|
scroll->ensureVisible(141 * 2 + scroll->width(), 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyboardSlider::KeyboardSlider(QWidget* parent)
|
||||||
|
: QSlider(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void KeyboardSlider::enterEvent(QEvent* event)
|
||||||
|
{
|
||||||
|
if (m_statusFocus)
|
||||||
|
m_statusFocus->enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSlider::leaveEvent(QEvent* event)
|
||||||
|
{
|
||||||
|
if (m_statusFocus)
|
||||||
|
m_statusFocus->exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSlider::setStatusFocus(StatusBarFocus* statusFocus)
|
||||||
|
{
|
||||||
|
m_statusFocus = statusFocus;
|
||||||
|
QString str = stringOfValue(value());
|
||||||
|
m_statusFocus->setMessage(str);
|
||||||
|
setToolTip(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardSlider::sliderChange(SliderChange change)
|
||||||
|
{
|
||||||
|
QSlider::sliderChange(change);
|
||||||
|
if (m_statusFocus && change == QAbstractSlider::SliderValueChange)
|
||||||
|
{
|
||||||
|
QString str = stringOfValue(value());
|
||||||
|
m_statusFocus->setMessage(str);
|
||||||
|
setToolTip(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VelocitySlider::VelocitySlider(QWidget* parent)
|
||||||
|
: KeyboardSlider(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString VelocitySlider::stringOfValue(int value) const
|
||||||
|
{
|
||||||
|
return tr("Velocity: %1").arg(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModulationSlider::ModulationSlider(QWidget* parent)
|
||||||
|
: KeyboardSlider(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString ModulationSlider::stringOfValue(int value) const
|
||||||
|
{
|
||||||
|
return tr("Modulation: %1").arg(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
PitchSlider::PitchSlider(QWidget* parent)
|
||||||
|
: KeyboardSlider(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString PitchSlider::stringOfValue(int value) const
|
||||||
|
{
|
||||||
|
return tr("Pitch: %1").arg(value / 2048.0, 0, 'g', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PitchSlider::mouseReleaseEvent(QMouseEvent *ev)
|
||||||
|
{
|
||||||
|
KeyboardSlider::mouseReleaseEvent(ev);
|
||||||
|
setValue(0);
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QSvgWidget>
|
#include <QSvgWidget>
|
||||||
|
#include <QSlider>
|
||||||
|
#include <QWheelEvent>
|
||||||
#include "StatusBarWidget.hpp"
|
#include "StatusBarWidget.hpp"
|
||||||
|
|
||||||
class KeyboardWidget;
|
class KeyboardWidget;
|
||||||
|
@ -47,7 +49,51 @@ public:
|
||||||
void leaveEvent(QEvent* event);
|
void leaveEvent(QEvent* event);
|
||||||
void wheelEvent(QWheelEvent *event);
|
void wheelEvent(QWheelEvent *event);
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void notePressed(int key);
|
||||||
|
void noteReleased();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class KeyboardSlider : public QSlider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
protected:
|
||||||
|
StatusBarFocus* m_statusFocus = nullptr;
|
||||||
|
virtual QString stringOfValue(int value) const = 0;
|
||||||
|
public:
|
||||||
|
explicit KeyboardSlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
void enterEvent(QEvent* event);
|
||||||
|
void leaveEvent(QEvent* event);
|
||||||
|
void setStatusFocus(StatusBarFocus* statusFocus);
|
||||||
|
void sliderChange(SliderChange change);
|
||||||
|
};
|
||||||
|
|
||||||
|
class VelocitySlider : public KeyboardSlider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QString stringOfValue(int value) const;
|
||||||
|
public:
|
||||||
|
explicit VelocitySlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModulationSlider : public KeyboardSlider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QString stringOfValue(int value) const;
|
||||||
|
public:
|
||||||
|
explicit ModulationSlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PitchSlider : public KeyboardSlider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QString stringOfValue(int value) const;
|
||||||
|
public:
|
||||||
|
explicit PitchSlider(QWidget* parent = Q_NULLPTR);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *ev);
|
||||||
|
void wheelEvent(QWheelEvent* ev) { ev->ignore(); }
|
||||||
|
};
|
||||||
|
|
||||||
#endif //AMUSE_KEYBOARD_WIDGET_HPP
|
#endif //AMUSE_KEYBOARD_WIDGET_HPP
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "MIDIReader.hpp"
|
||||||
|
#include "MainWindow.hpp"
|
||||||
|
|
||||||
|
MIDIReader::MIDIReader(amuse::Engine& engine, const char* name, bool useLock)
|
||||||
|
: amuse::BooBackendMIDIReader(engine, name, useLock) {}
|
||||||
|
|
||||||
|
void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
|
||||||
|
{
|
||||||
|
auto keySearch = m_chanVoxs.find(key);
|
||||||
|
if (keySearch == m_chanVoxs.cend())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (keySearch->second == m_lastVoice.lock())
|
||||||
|
m_lastVoice.reset();
|
||||||
|
keySearch->second->keyOff();
|
||||||
|
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||||
|
m_chanVoxs.erase(keySearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
|
||||||
|
{
|
||||||
|
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
||||||
|
if (std::shared_ptr<amuse::Voice> lastVoice = m_lastVoice.lock())
|
||||||
|
{
|
||||||
|
uint8_t lastNote = lastVoice->getLastNote();
|
||||||
|
if (lastVoice->doPortamento(key))
|
||||||
|
{
|
||||||
|
m_chanVoxs.erase(lastNote);
|
||||||
|
m_chanVoxs[key] = lastVoice;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure keyoff sent first */
|
||||||
|
auto keySearch = m_chanVoxs.find(key);
|
||||||
|
if (keySearch != m_chanVoxs.cend())
|
||||||
|
{
|
||||||
|
if (keySearch->second == m_lastVoice.lock())
|
||||||
|
m_lastVoice.reset();
|
||||||
|
keySearch->second->keyOff();
|
||||||
|
keySearch->second->setPedal(false);
|
||||||
|
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||||
|
m_chanVoxs.erase(keySearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectModel::INode* node = g_MainWindow->getEditorNode();
|
||||||
|
if (node && node->type() == ProjectModel::INode::Type::SoundMacro)
|
||||||
|
{
|
||||||
|
ProjectModel::SoundMacroNode* cNode = static_cast<ProjectModel::SoundMacroNode*>(node);
|
||||||
|
amuse::AudioGroupDatabase* group = g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup();
|
||||||
|
std::shared_ptr<amuse::Voice>& vox = m_chanVoxs[key];
|
||||||
|
vox = m_engine.macroStart(group, cNode->id(), key, velocity, g_MainWindow->m_modulation);
|
||||||
|
vox->setPedal(g_MainWindow->m_sustain);
|
||||||
|
vox->setPitchWheel(g_MainWindow->m_pitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
|
||||||
|
{
|
||||||
|
if (control == 1)
|
||||||
|
{
|
||||||
|
g_MainWindow->m_ui.modulationSlider->setValue(int(value));
|
||||||
|
}
|
||||||
|
else if (control == 64)
|
||||||
|
{
|
||||||
|
g_MainWindow->setSustain(value >= 0x40);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto& v : m_engine.getActiveVoices())
|
||||||
|
v->setCtrlValue(control, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MIDIReader::programChange(uint8_t chan, uint8_t program) {}
|
||||||
|
|
||||||
|
void MIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::pitchBend(uint8_t chan, int16_t pitch)
|
||||||
|
{
|
||||||
|
g_MainWindow->m_ui.pitchSlider->setValue(int((pitch - 0x2000) / float(0x2000) * 2048.f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MIDIReader::allSoundOff(uint8_t chan)
|
||||||
|
{
|
||||||
|
for (auto& v : m_engine.getActiveVoices())
|
||||||
|
v->kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MIDIReader::resetAllControllers(uint8_t /*chan*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::allNotesOff(uint8_t chan)
|
||||||
|
{
|
||||||
|
for (auto& v : m_engine.getActiveVoices())
|
||||||
|
v->kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::polyMode(uint8_t /*chan*/, bool /*on*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::sysex(const void* /*data*/, size_t /*len*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::timeCodeQuarterFrame(uint8_t /*message*/, uint8_t /*value*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::songPositionPointer(uint16_t /*pointer*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::songSelect(uint8_t /*song*/) {}
|
||||||
|
|
||||||
|
void MIDIReader::tuneRequest() {}
|
||||||
|
|
||||||
|
void MIDIReader::startSeq() {}
|
||||||
|
|
||||||
|
void MIDIReader::continueSeq() {}
|
||||||
|
|
||||||
|
void MIDIReader::stopSeq() {}
|
||||||
|
|
||||||
|
void MIDIReader::reset() {}
|
||||||
|
|
||||||
|
VoiceAllocator::VoiceAllocator(boo::IAudioVoiceEngine& booEngine)
|
||||||
|
: amuse::BooBackendVoiceAllocator(booEngine) {}
|
||||||
|
|
||||||
|
std::unique_ptr<amuse::IMIDIReader>
|
||||||
|
VoiceAllocator::allocateMIDIReader(amuse::Engine& engine, const char* name)
|
||||||
|
{
|
||||||
|
std::unique_ptr<amuse::IMIDIReader> ret = std::make_unique<MIDIReader>(engine, name, m_booEngine.useMIDILock());
|
||||||
|
if (!static_cast<MIDIReader&>(*ret).getMidiIn())
|
||||||
|
return {};
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef AMUSE_MIDI_READER_HPP
|
||||||
|
#define AMUSE_MIDI_READER_HPP
|
||||||
|
|
||||||
|
#include "amuse/BooBackend.hpp"
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
class MIDIReader : public amuse::BooBackendMIDIReader
|
||||||
|
{
|
||||||
|
std::unordered_map<uint8_t, std::shared_ptr<amuse::Voice>> m_chanVoxs;
|
||||||
|
std::unordered_set<std::shared_ptr<amuse::Voice>> m_keyoffVoxs;
|
||||||
|
std::weak_ptr<amuse::Voice> m_lastVoice;
|
||||||
|
public:
|
||||||
|
MIDIReader(amuse::Engine& engine, const char* name, bool useLock);
|
||||||
|
boo::IMIDIIn* getMidiIn() const { return m_midiIn.get(); }
|
||||||
|
|
||||||
|
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
|
||||||
|
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
|
||||||
|
void notePressure(uint8_t chan, uint8_t key, uint8_t pressure);
|
||||||
|
void controlChange(uint8_t chan, uint8_t control, uint8_t value);
|
||||||
|
void programChange(uint8_t chan, uint8_t program);
|
||||||
|
void channelPressure(uint8_t chan, uint8_t pressure);
|
||||||
|
void pitchBend(uint8_t chan, int16_t pitch);
|
||||||
|
|
||||||
|
void allSoundOff(uint8_t chan);
|
||||||
|
void resetAllControllers(uint8_t chan);
|
||||||
|
void localControl(uint8_t chan, bool on);
|
||||||
|
void allNotesOff(uint8_t chan);
|
||||||
|
void omniMode(uint8_t chan, bool on);
|
||||||
|
void polyMode(uint8_t chan, bool on);
|
||||||
|
|
||||||
|
void sysex(const void* data, size_t len);
|
||||||
|
void timeCodeQuarterFrame(uint8_t message, uint8_t value);
|
||||||
|
void songPositionPointer(uint16_t pointer);
|
||||||
|
void songSelect(uint8_t song);
|
||||||
|
void tuneRequest();
|
||||||
|
|
||||||
|
void startSeq();
|
||||||
|
void continueSeq();
|
||||||
|
void stopSeq();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
class VoiceAllocator : public amuse::BooBackendVoiceAllocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VoiceAllocator(boo::IAudioVoiceEngine& booEngine);
|
||||||
|
std::unique_ptr<amuse::IMIDIReader> allocateMIDIReader(amuse::Engine& engine, const char* name = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AMUSE_MIDI_READER_HPP
|
|
@ -31,14 +31,40 @@ MainWindow::MainWindow(QWidget* parent)
|
||||||
m_ui.projectOutline->setItemDelegate(&m_treeDelegate);
|
m_ui.projectOutline->setItemDelegate(&m_treeDelegate);
|
||||||
connectMessenger(&m_mainMessenger, Qt::DirectConnection);
|
connectMessenger(&m_mainMessenger, Qt::DirectConnection);
|
||||||
|
|
||||||
|
m_ui.statusbar->connectKillClicked(this, SLOT(killSounds()));
|
||||||
|
|
||||||
m_ui.keyboardContents->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
|
m_ui.keyboardContents->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
|
||||||
|
m_ui.velocitySlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
|
||||||
|
m_ui.modulationSlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
|
||||||
|
m_ui.pitchSlider->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
|
||||||
|
connect(m_ui.keyboardContents, SIGNAL(notePressed(int)), this, SLOT(notePressed(int)));
|
||||||
|
connect(m_ui.keyboardContents, SIGNAL(noteReleased()), this, SLOT(noteReleased()));
|
||||||
|
connect(m_ui.velocitySlider, SIGNAL(valueChanged(int)), this, SLOT(velocityChanged(int)));
|
||||||
|
connect(m_ui.modulationSlider, SIGNAL(valueChanged(int)), this, SLOT(modulationChanged(int)));
|
||||||
|
connect(m_ui.pitchSlider, SIGNAL(valueChanged(int)), this, SLOT(pitchChanged(int)));
|
||||||
|
|
||||||
m_ui.actionNew_Project->setShortcut(QKeySequence::New);
|
m_ui.actionNew_Project->setShortcut(QKeySequence::New);
|
||||||
connect(m_ui.actionNew_Project, SIGNAL(triggered()), this, SLOT(newAction()));
|
connect(m_ui.actionNew_Project, SIGNAL(triggered()), this, SLOT(newAction()));
|
||||||
m_ui.actionOpen_Project->setShortcut(QKeySequence::Open);
|
m_ui.actionOpen_Project->setShortcut(QKeySequence::Open);
|
||||||
connect(m_ui.actionOpen_Project, SIGNAL(triggered()), this, SLOT(openAction()));
|
connect(m_ui.actionOpen_Project, SIGNAL(triggered()), this, SLOT(openAction()));
|
||||||
|
m_ui.actionSave_Project->setShortcut(QKeySequence::Save);
|
||||||
|
connect(m_ui.actionSave_Project, SIGNAL(triggered()), this, SLOT(saveAction()));
|
||||||
|
connect(m_ui.actionRevert_Project, SIGNAL(triggered()), this, SLOT(revertAction()));
|
||||||
connect(m_ui.actionImport, SIGNAL(triggered()), this, SLOT(importAction()));
|
connect(m_ui.actionImport, SIGNAL(triggered()), this, SLOT(importAction()));
|
||||||
connect(m_ui.actionExport_GameCube_Groups, SIGNAL(triggered()), this, SLOT(exportAction()));
|
connect(m_ui.actionExport_GameCube_Groups, SIGNAL(triggered()), this, SLOT(exportAction()));
|
||||||
|
|
||||||
|
for (int i = 0; i < MaxRecentFiles; ++i)
|
||||||
|
{
|
||||||
|
m_recentFileActs[i] = new QAction(this);
|
||||||
|
m_recentFileActs[i]->setVisible(false);
|
||||||
|
m_ui.menuRecent_Projects->addAction(m_recentFileActs[i]);
|
||||||
|
connect(m_recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFileAction()));
|
||||||
|
}
|
||||||
|
m_ui.menuRecent_Projects->addSeparator();
|
||||||
|
m_clearRecentFileAct = new QAction(tr("Clear Recent Projects"), this);
|
||||||
|
connect(m_clearRecentFileAct, SIGNAL(triggered()), this, SLOT(clearRecentFilesAction()));
|
||||||
|
m_ui.menuRecent_Projects->addAction(m_clearRecentFileAct);
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
m_ui.menuFile->addSeparator();
|
m_ui.menuFile->addSeparator();
|
||||||
QAction* quitAction = m_ui.menuFile->addAction(tr("Quit"));
|
QAction* quitAction = m_ui.menuFile->addAction(tr("Quit"));
|
||||||
|
@ -46,6 +72,8 @@ MainWindow::MainWindow(QWidget* parent)
|
||||||
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
|
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
updateRecentFileActions();
|
||||||
|
|
||||||
QAction* undoAction = m_undoStack->createUndoAction(this);
|
QAction* undoAction = m_undoStack->createUndoAction(this);
|
||||||
undoAction->setShortcut(QKeySequence::Undo);
|
undoAction->setShortcut(QKeySequence::Undo);
|
||||||
m_ui.menuEdit->insertAction(m_ui.actionCut, undoAction);
|
m_ui.menuEdit->insertAction(m_ui.actionCut, undoAction);
|
||||||
|
@ -97,11 +125,11 @@ MainWindow::MainWindow(QWidget* parent)
|
||||||
|
|
||||||
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(onFocusChanged(QWidget*,QWidget*)));
|
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(onFocusChanged(QWidget*,QWidget*)));
|
||||||
|
|
||||||
setFocusAudioGroup(nullptr);
|
|
||||||
|
|
||||||
m_voxEngine = boo::NewAudioVoiceEngine();
|
m_voxEngine = boo::NewAudioVoiceEngine();
|
||||||
m_voxAllocator = std::make_unique<amuse::BooBackendVoiceAllocator>(*m_voxEngine);
|
m_voxAllocator = std::make_unique<VoiceAllocator>(*m_voxEngine);
|
||||||
m_engine = std::make_unique<amuse::Engine>(*m_voxAllocator);
|
m_engine = std::make_unique<amuse::Engine>(*m_voxAllocator);
|
||||||
|
|
||||||
|
startTimer(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
@ -139,13 +167,39 @@ void MainWindow::connectMessenger(UIMessenger* messenger, Qt::ConnectionType typ
|
||||||
QMessageBox::StandardButton)), type);
|
QMessageBox::StandardButton)), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateRecentFileActions()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
QStringList files = settings.value("recentFileList").toStringList();
|
||||||
|
|
||||||
|
int numRecentFiles = std::min(files.size(), int(MaxRecentFiles));
|
||||||
|
|
||||||
|
for (int i = 0; i < numRecentFiles; ++i)
|
||||||
|
{
|
||||||
|
QString text = QStringLiteral("&%1 %2").arg(i + 1).arg(QDir(files[i]).dirName());
|
||||||
|
m_recentFileActs[i]->setText(text);
|
||||||
|
m_recentFileActs[i]->setData(files[i]);
|
||||||
|
m_recentFileActs[i]->setVisible(true);
|
||||||
|
}
|
||||||
|
for (int j = numRecentFiles; j < MaxRecentFiles; ++j)
|
||||||
|
m_recentFileActs[j]->setVisible(false);
|
||||||
|
|
||||||
|
m_ui.menuRecent_Projects->setEnabled(numRecentFiles > 0);
|
||||||
|
}
|
||||||
|
|
||||||
bool MainWindow::setProjectPath(const QString& path)
|
bool MainWindow::setProjectPath(const QString& path)
|
||||||
{
|
{
|
||||||
if (m_projectModel && m_projectModel->path() == path)
|
if (m_projectModel && m_projectModel->path() == path)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
if (!dir.exists())
|
if (dir.path().isEmpty() || dir.path() == QStringLiteral(".") || dir.path() == QStringLiteral(".."))
|
||||||
|
{
|
||||||
|
QString msg = QString(tr("The directory at '%1' must not be empty.")).arg(path);
|
||||||
|
QMessageBox::critical(this, tr("Directory empty"), msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!dir.exists())
|
||||||
{
|
{
|
||||||
QString msg = QString(tr("The directory at '%1' must exist for the Amuse editor.")).arg(path);
|
QString msg = QString(tr("The directory at '%1' must exist for the Amuse editor.")).arg(path);
|
||||||
QMessageBox::critical(this, tr("Directory does not exist"), msg);
|
QMessageBox::critical(this, tr("Directory does not exist"), msg);
|
||||||
|
@ -165,24 +219,30 @@ bool MainWindow::setProjectPath(const QString& path)
|
||||||
m_projectModel->deleteLater();
|
m_projectModel->deleteLater();
|
||||||
m_projectModel = new ProjectModel(path, this);
|
m_projectModel = new ProjectModel(path, this);
|
||||||
m_ui.projectOutline->setModel(m_projectModel);
|
m_ui.projectOutline->setModel(m_projectModel);
|
||||||
|
connect(m_ui.projectOutline->selectionModel(),
|
||||||
|
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||||
|
this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&)));
|
||||||
|
m_ui.actionSave_Project->setEnabled(true);
|
||||||
|
m_ui.actionRevert_Project->setEnabled(true);
|
||||||
m_ui.actionExport_GameCube_Groups->setEnabled(true);
|
m_ui.actionExport_GameCube_Groups->setEnabled(true);
|
||||||
setWindowFilePath(path);
|
setWindowFilePath(path);
|
||||||
setWindowTitle(QString("Amuse [%1]").arg(dir.dirName()));
|
setWindowTitle(QString("Amuse [%1]").arg(dir.dirName()));
|
||||||
setFocusAudioGroup(nullptr);
|
|
||||||
onFocusChanged(nullptr, focusWidget());
|
onFocusChanged(nullptr, focusWidget());
|
||||||
|
m_undoStack->clear();
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
QStringList files = settings.value("recentFileList").toStringList();
|
||||||
|
files.removeAll(dir.path());
|
||||||
|
files.prepend(dir.path());
|
||||||
|
while (files.size() > MaxRecentFiles)
|
||||||
|
files.removeLast();
|
||||||
|
settings.setValue("recentFileList", files);
|
||||||
|
|
||||||
|
updateRecentFileActions();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setFocusAudioGroup(AudioGroupModel* group)
|
|
||||||
{
|
|
||||||
m_focusAudioGroup = group;
|
|
||||||
bool active = m_focusAudioGroup != nullptr;
|
|
||||||
m_ui.actionNew_Sound_Macro->setEnabled(active);
|
|
||||||
m_ui.actionNew_Keymap->setEnabled(active);
|
|
||||||
m_ui.actionNew_Layers->setEnabled(active);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::refreshAudioIO()
|
void MainWindow::refreshAudioIO()
|
||||||
{
|
{
|
||||||
QList<QAction*> audioActions = m_ui.menuAudio->actions();
|
QList<QAction*> audioActions = m_ui.menuAudio->actions();
|
||||||
|
@ -220,6 +280,61 @@ void MainWindow::refreshMIDIIO()
|
||||||
m_ui.menuMIDI->addAction(tr("No MIDI Devices Found"))->setEnabled(false);
|
m_ui.menuMIDI->addAction(tr("No MIDI Devices Found"))->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::timerEvent(QTimerEvent* ev)
|
||||||
|
{
|
||||||
|
if (m_voxEngine && m_engine)
|
||||||
|
{
|
||||||
|
m_voxEngine->pumpAndMixVoices();
|
||||||
|
m_ui.statusbar->setVoiceCount(int(m_engine->getActiveVoices().size()));
|
||||||
|
if (m_engine->getActiveVoices().empty() && m_uiDisabled)
|
||||||
|
{
|
||||||
|
m_ui.projectOutline->setEnabled(true);
|
||||||
|
m_ui.editorContents->setEnabled(true);
|
||||||
|
m_ui.menubar->setEnabled(true);
|
||||||
|
m_uiDisabled = false;
|
||||||
|
}
|
||||||
|
else if (!m_engine->getActiveVoices().empty() && !m_uiDisabled)
|
||||||
|
{
|
||||||
|
m_ui.projectOutline->setEnabled(false);
|
||||||
|
m_ui.editorContents->setEnabled(false);
|
||||||
|
m_ui.menubar->setEnabled(false);
|
||||||
|
m_uiDisabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setSustain(bool sustain)
|
||||||
|
{
|
||||||
|
if (sustain && !m_sustain)
|
||||||
|
{
|
||||||
|
m_ui.statusbar->setNormalMessage(tr("SUSTAIN"));
|
||||||
|
for (auto& v : m_engine->getActiveVoices())
|
||||||
|
v->setPedal(true);
|
||||||
|
m_sustain = true;
|
||||||
|
}
|
||||||
|
else if (!sustain && m_sustain)
|
||||||
|
{
|
||||||
|
m_ui.statusbar->setNormalMessage({});
|
||||||
|
for (auto& v : m_engine->getActiveVoices())
|
||||||
|
v->setPedal(false);
|
||||||
|
m_sustain = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::keyPressEvent(QKeyEvent* ev)
|
||||||
|
{
|
||||||
|
if (ev->key() == Qt::Key_Shift)
|
||||||
|
setSustain(true);
|
||||||
|
else if (ev->key() == Qt::Key_Escape)
|
||||||
|
killSounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::keyReleaseEvent(QKeyEvent* ev)
|
||||||
|
{
|
||||||
|
if (ev->key() == Qt::Key_Shift)
|
||||||
|
setSustain(false);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::startBackgroundTask(const QString& windowTitle, const QString& label,
|
void MainWindow::startBackgroundTask(const QString& windowTitle, const QString& label,
|
||||||
std::function<void(BackgroundTask&)>&& task)
|
std::function<void(BackgroundTask&)>&& task)
|
||||||
{
|
{
|
||||||
|
@ -335,11 +450,24 @@ void MainWindow::closeEditor()
|
||||||
_setEditor(nullptr);
|
_setEditor(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProjectModel::INode* MainWindow::getEditorNode() const
|
||||||
|
{
|
||||||
|
if (m_ui.editorContents->currentWidget() != m_faceSvg)
|
||||||
|
return static_cast<EditorWidget*>(m_ui.editorContents->currentWidget())->currentNode();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::pushUndoCommand(QUndoCommand* cmd)
|
void MainWindow::pushUndoCommand(QUndoCommand* cmd)
|
||||||
{
|
{
|
||||||
m_undoStack->push(cmd);
|
m_undoStack->push(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::aboutToDeleteNode(ProjectModel::INode* node)
|
||||||
|
{
|
||||||
|
if (getEditorNode() == node)
|
||||||
|
closeEditor();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::newAction()
|
void MainWindow::newAction()
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getSaveFileName(this, tr("New Project"));
|
QString path = QFileDialog::getSaveFileName(this, tr("New Project"));
|
||||||
|
@ -354,18 +482,20 @@ void MainWindow::newAction()
|
||||||
m_projectModel->ensureModelData();
|
m_projectModel->ensureModelData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openAction()
|
bool MainWindow::openProject(const QString& path)
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getExistingDirectory(this, tr("Open Project"));
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
if (!dir.exists())
|
if (dir.path().isEmpty() || dir.path() == QStringLiteral(".") || dir.path() == QStringLiteral(".."))
|
||||||
|
{
|
||||||
|
QString msg = QString(tr("The directory at '%1' must not be empty.")).arg(path);
|
||||||
|
QMessageBox::critical(this, tr("Directory empty"), msg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!dir.exists())
|
||||||
{
|
{
|
||||||
QString msg = QString(tr("The directory at '%1' does not exist.")).arg(path);
|
QString msg = QString(tr("The directory at '%1' does not exist.")).arg(path);
|
||||||
QMessageBox::critical(this, tr("Bad Directory"), msg);
|
QMessageBox::critical(this, tr("Bad Directory"), msg);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QFileInfo(dir, QStringLiteral("!project.yaml")).exists() &&
|
if (QFileInfo(dir, QStringLiteral("!project.yaml")).exists() &&
|
||||||
|
@ -373,7 +503,7 @@ void MainWindow::openAction()
|
||||||
dir.cdUp();
|
dir.cdUp();
|
||||||
|
|
||||||
if (!setProjectPath(dir.path()))
|
if (!setProjectPath(dir.path()))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
ProjectModel* model = m_projectModel;
|
ProjectModel* model = m_projectModel;
|
||||||
startBackgroundTask(tr("Opening"), tr("Scanning Project"),
|
startBackgroundTask(tr("Opening"), tr("Scanning Project"),
|
||||||
|
@ -394,6 +524,50 @@ void MainWindow::openAction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::openAction()
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getExistingDirectory(this, tr("Open Project"));
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
openProject(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::openRecentFileAction()
|
||||||
|
{
|
||||||
|
if (QAction *action = qobject_cast<QAction *>(sender()))
|
||||||
|
if (!openProject(action->data().toString()))
|
||||||
|
{
|
||||||
|
QString path = action->data().toString();
|
||||||
|
QSettings settings;
|
||||||
|
QStringList files = settings.value("recentFileList").toStringList();
|
||||||
|
files.removeAll(path);
|
||||||
|
settings.setValue("recentFileList", files);
|
||||||
|
updateRecentFileActions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::clearRecentFilesAction()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("recentFileList", QStringList());
|
||||||
|
updateRecentFileActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::saveAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::revertAction()
|
||||||
|
{
|
||||||
|
QString path = m_projectModel->path();
|
||||||
|
delete m_projectModel;
|
||||||
|
m_projectModel = nullptr;
|
||||||
|
openProject(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::importAction()
|
void MainWindow::importAction()
|
||||||
|
@ -610,6 +784,83 @@ void MainWindow::setMIDIIO()
|
||||||
//qobject_cast<QAction*>(sender())->data().toString().toUtf8().constData();
|
//qobject_cast<QAction*>(sender())->data().toString().toUtf8().constData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::notePressed(int key)
|
||||||
|
{
|
||||||
|
if (m_engine)
|
||||||
|
{
|
||||||
|
ProjectModel::INode* node = getEditorNode();
|
||||||
|
if (node && node->type() == ProjectModel::INode::Type::SoundMacro)
|
||||||
|
{
|
||||||
|
ProjectModel::SoundMacroNode* cNode = static_cast<ProjectModel::SoundMacroNode*>(node);
|
||||||
|
amuse::AudioGroupDatabase* group = m_projectModel->getGroupNode(node)->getAudioGroup();
|
||||||
|
if (m_lastSound)
|
||||||
|
m_lastSound->keyOff();
|
||||||
|
m_lastSound = m_engine->macroStart(group, cNode->id(), key, m_velocity, m_modulation);
|
||||||
|
m_lastSound->setPedal(m_sustain);
|
||||||
|
m_lastSound->setPitchWheel(m_pitch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::noteReleased()
|
||||||
|
{
|
||||||
|
if (m_lastSound)
|
||||||
|
{
|
||||||
|
m_lastSound->keyOff();
|
||||||
|
m_lastSound.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::velocityChanged(int vel)
|
||||||
|
{
|
||||||
|
m_velocity = vel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::modulationChanged(int mod)
|
||||||
|
{
|
||||||
|
m_modulation = mod;
|
||||||
|
for (auto& v : m_engine->getActiveVoices())
|
||||||
|
v->setCtrlValue(1, int8_t(m_modulation));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::pitchChanged(int pitch)
|
||||||
|
{
|
||||||
|
m_pitch = pitch / 2048.f;
|
||||||
|
for (auto& v : m_engine->getActiveVoices())
|
||||||
|
v->setPitchWheel(m_pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::killSounds()
|
||||||
|
{
|
||||||
|
for (auto& v : m_engine->getActiveVoices())
|
||||||
|
v->kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::outlineCutAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::outlineCopyAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::outlinePasteAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::outlineDeleteAction()
|
||||||
|
{
|
||||||
|
if (!m_projectModel)
|
||||||
|
return;
|
||||||
|
QModelIndexList indexes = m_ui.projectOutline->selectionModel()->selectedIndexes();
|
||||||
|
if (indexes.empty())
|
||||||
|
return;
|
||||||
|
m_projectModel->del(indexes.front());
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
|
void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
|
||||||
{
|
{
|
||||||
disconnect(m_cutConn);
|
disconnect(m_cutConn);
|
||||||
|
@ -634,24 +885,52 @@ void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
|
||||||
|
|
||||||
if (now == m_ui.projectOutline || m_ui.projectOutline->isAncestorOf(now))
|
if (now == m_ui.projectOutline || m_ui.projectOutline->isAncestorOf(now))
|
||||||
{
|
{
|
||||||
m_ui.actionCut->setEnabled(false);
|
setOutlineEditEnabled(canEditOutline());
|
||||||
m_ui.actionCopy->setEnabled(false);
|
|
||||||
m_ui.actionPaste->setEnabled(false);
|
|
||||||
if (m_projectModel)
|
if (m_projectModel)
|
||||||
{
|
{
|
||||||
m_deleteConn = connect(m_ui.actionDelete, SIGNAL(triggered()), m_projectModel, SLOT(del()));
|
m_cutConn = connect(m_ui.actionCut, SIGNAL(triggered()), this, SLOT(outlineCutAction()));
|
||||||
m_ui.actionDelete->setEnabled(m_projectModel->canDelete());
|
m_copyConn = connect(m_ui.actionCopy, SIGNAL(triggered()), this, SLOT(outlineCopyAction()));
|
||||||
m_canEditConn = connect(m_projectModel, SIGNAL(canDeleteChanged(bool)),
|
m_pasteConn = connect(m_ui.actionPaste, SIGNAL(triggered()), this, SLOT(outlinePasteAction()));
|
||||||
m_ui.actionDelete, SLOT(setEnabled(bool)));
|
m_deleteConn = connect(m_ui.actionDelete, SIGNAL(triggered()), this, SLOT(outlineDeleteAction()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (now == m_ui.editorContents || m_ui.editorContents->isAncestorOf(now))
|
else if (now == m_ui.editorContents || m_ui.editorContents->isAncestorOf(now))
|
||||||
{
|
{
|
||||||
|
setOutlineEditEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setOutlineEditEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
m_ui.actionCut->setEnabled(enabled);
|
||||||
|
m_ui.actionCopy->setEnabled(enabled);
|
||||||
|
m_ui.actionPaste->setEnabled(enabled);
|
||||||
|
m_ui.actionDelete->setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::canEditOutline()
|
||||||
|
{
|
||||||
|
if (!m_projectModel)
|
||||||
|
return false;
|
||||||
|
QModelIndexList indexes = m_ui.projectOutline->selectionModel()->selectedIndexes();
|
||||||
|
if (indexes.empty())
|
||||||
|
return false;
|
||||||
|
return m_projectModel->canEdit(indexes.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
|
||||||
|
{
|
||||||
|
if (!m_projectModel)
|
||||||
|
return;
|
||||||
|
if (selected.indexes().empty())
|
||||||
|
{
|
||||||
|
setOutlineEditEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setOutlineEditEnabled(m_projectModel->canEdit(selected.indexes().front()));
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onTextSelect()
|
void MainWindow::onTextSelect()
|
||||||
{
|
{
|
||||||
if (QLineEdit* le = qobject_cast<QLineEdit*>(sender()))
|
if (QLineEdit* le = qobject_cast<QLineEdit*>(sender()))
|
||||||
|
|
|
@ -12,13 +12,15 @@
|
||||||
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
#include "boo/audiodev/IAudioVoiceEngine.hpp"
|
||||||
#include "ProjectModel.hpp"
|
#include "ProjectModel.hpp"
|
||||||
#include "EditorWidget.hpp"
|
#include "EditorWidget.hpp"
|
||||||
|
#include "MIDIReader.hpp"
|
||||||
|
|
||||||
|
#define MaxRecentFiles 4
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
class AudioGroupModel;
|
|
||||||
class SongGroupEditor;
|
class SongGroupEditor;
|
||||||
class SoundGroupEditor;
|
class SoundGroupEditor;
|
||||||
class SoundMacroEditor;
|
class SoundMacroEditor;
|
||||||
|
@ -67,12 +69,14 @@ public:
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
|
friend class MIDIReader;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Ui::MainWindow m_ui;
|
Ui::MainWindow m_ui;
|
||||||
|
QAction* m_clearRecentFileAct;
|
||||||
|
QAction* m_recentFileActs[MaxRecentFiles];
|
||||||
TreeDelegate m_treeDelegate;
|
TreeDelegate m_treeDelegate;
|
||||||
UIMessenger m_mainMessenger;
|
UIMessenger m_mainMessenger;
|
||||||
ProjectModel* m_projectModel = nullptr;
|
ProjectModel* m_projectModel = nullptr;
|
||||||
AudioGroupModel* m_focusAudioGroup = nullptr;
|
|
||||||
QWidget* m_faceSvg;
|
QWidget* m_faceSvg;
|
||||||
SongGroupEditor* m_songGroupEditor = nullptr;
|
SongGroupEditor* m_songGroupEditor = nullptr;
|
||||||
SoundGroupEditor* m_soundGroupEditor = nullptr;
|
SoundGroupEditor* m_soundGroupEditor = nullptr;
|
||||||
|
@ -83,8 +87,14 @@ class MainWindow : public QMainWindow
|
||||||
LayersEditor* m_layersEditor = nullptr;
|
LayersEditor* m_layersEditor = nullptr;
|
||||||
|
|
||||||
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
|
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
|
||||||
std::unique_ptr<amuse::BooBackendVoiceAllocator> m_voxAllocator;
|
std::unique_ptr<VoiceAllocator> m_voxAllocator;
|
||||||
std::unique_ptr<amuse::Engine> m_engine;
|
std::unique_ptr<amuse::Engine> m_engine;
|
||||||
|
std::shared_ptr<amuse::Voice> m_lastSound;
|
||||||
|
int m_velocity = 90;
|
||||||
|
int m_modulation = 0;
|
||||||
|
float m_pitch = 0.f;
|
||||||
|
bool m_sustain = false;
|
||||||
|
bool m_uiDisabled = false;
|
||||||
|
|
||||||
QUndoStack* m_undoStack;
|
QUndoStack* m_undoStack;
|
||||||
|
|
||||||
|
@ -100,10 +110,14 @@ class MainWindow : public QMainWindow
|
||||||
|
|
||||||
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
|
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
|
||||||
|
|
||||||
|
void updateRecentFileActions();
|
||||||
bool setProjectPath(const QString& path);
|
bool setProjectPath(const QString& path);
|
||||||
void setFocusAudioGroup(AudioGroupModel* group);
|
|
||||||
void refreshAudioIO();
|
void refreshAudioIO();
|
||||||
void refreshMIDIIO();
|
void refreshMIDIIO();
|
||||||
|
void timerEvent(QTimerEvent* ev);
|
||||||
|
void setSustain(bool sustain);
|
||||||
|
void keyPressEvent(QKeyEvent* ev);
|
||||||
|
void keyReleaseEvent(QKeyEvent* ev);
|
||||||
|
|
||||||
void startBackgroundTask(const QString& windowTitle, const QString& label,
|
void startBackgroundTask(const QString& windowTitle, const QString& label,
|
||||||
std::function<void(BackgroundTask&)>&& task);
|
std::function<void(BackgroundTask&)>&& task);
|
||||||
|
@ -114,6 +128,8 @@ public:
|
||||||
explicit MainWindow(QWidget* parent = Q_NULLPTR);
|
explicit MainWindow(QWidget* parent = Q_NULLPTR);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
bool openProject(const QString& path);
|
||||||
|
|
||||||
bool openEditor(ProjectModel::SongGroupNode* node);
|
bool openEditor(ProjectModel::SongGroupNode* node);
|
||||||
bool openEditor(ProjectModel::SoundGroupNode* node);
|
bool openEditor(ProjectModel::SoundGroupNode* node);
|
||||||
bool openEditor(ProjectModel::SoundMacroNode* node);
|
bool openEditor(ProjectModel::SoundMacroNode* node);
|
||||||
|
@ -124,11 +140,19 @@ public:
|
||||||
bool openEditor(ProjectModel::INode* node);
|
bool openEditor(ProjectModel::INode* node);
|
||||||
void closeEditor();
|
void closeEditor();
|
||||||
|
|
||||||
|
ProjectModel::INode* getEditorNode() const;
|
||||||
void pushUndoCommand(QUndoCommand* cmd);
|
void pushUndoCommand(QUndoCommand* cmd);
|
||||||
|
void aboutToDeleteNode(ProjectModel::INode* node);
|
||||||
|
|
||||||
|
ProjectModel* projectModel() const { return m_projectModel; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void newAction();
|
void newAction();
|
||||||
void openAction();
|
void openAction();
|
||||||
|
void openRecentFileAction();
|
||||||
|
void clearRecentFilesAction();
|
||||||
|
void saveAction();
|
||||||
|
void revertAction();
|
||||||
void importAction();
|
void importAction();
|
||||||
void exportAction();
|
void exportAction();
|
||||||
|
|
||||||
|
@ -147,7 +171,22 @@ public slots:
|
||||||
void setAudioIO();
|
void setAudioIO();
|
||||||
void setMIDIIO();
|
void setMIDIIO();
|
||||||
|
|
||||||
|
void notePressed(int key);
|
||||||
|
void noteReleased();
|
||||||
|
void velocityChanged(int vel);
|
||||||
|
void modulationChanged(int mod);
|
||||||
|
void pitchChanged(int pitch);
|
||||||
|
void killSounds();
|
||||||
|
|
||||||
|
void outlineCutAction();
|
||||||
|
void outlineCopyAction();
|
||||||
|
void outlinePasteAction();
|
||||||
|
void outlineDeleteAction();
|
||||||
|
|
||||||
void onFocusChanged(QWidget* old, QWidget* now);
|
void onFocusChanged(QWidget* old, QWidget* now);
|
||||||
|
void setOutlineEditEnabled(bool enabled);
|
||||||
|
bool canEditOutline();
|
||||||
|
void onOutlineSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||||
void onTextSelect();
|
void onTextSelect();
|
||||||
void onTextDelete();
|
void onTextDelete();
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1024</width>
|
<width>1360</width>
|
||||||
<height>768</height>
|
<height>768</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -98,22 +98,22 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>764</width>
|
<width>1100</width>
|
||||||
<height>610</height>
|
<height>610</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QScrollArea" name="keyboardScrollArea">
|
<widget class="QWidget" name="keyboard" native="true">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>500</width>
|
<width>0</width>
|
||||||
<height>100</height>
|
<height>100</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
@ -123,46 +123,157 @@
|
||||||
<height>100</height>
|
<height>100</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShape">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<property name="spacing">
|
||||||
</property>
|
<number>6</number>
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Plain</enum>
|
|
||||||
</property>
|
|
||||||
<property name="lineWidth">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="widgetResizable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<widget class="KeyboardWidget" name="keyboardContents">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>1501</width>
|
|
||||||
<height>85</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="leftMargin">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
<number>0</number>
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="topMargin">
|
||||||
<size>
|
<number>0</number>
|
||||||
<width>1501</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="rightMargin">
|
||||||
<size>
|
<number>6</number>
|
||||||
<width>16777215</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="keyboardScrollArea">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>500</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Plain</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="KeyboardWidget" name="keyboardContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1501</width>
|
||||||
|
<height>85</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>1501</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="VelocitySlider" name="velocitySlider">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>127</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>90</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="ModulationSlider" name="modulationSlider">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>127</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="PitchSlider" name="pitchSlider">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-2048</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>2048</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -174,7 +285,7 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1024</width>
|
<width>1360</width>
|
||||||
<height>27</height>
|
<height>27</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -182,8 +293,20 @@
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&File</string>
|
<string>&File</string>
|
||||||
</property>
|
</property>
|
||||||
|
<widget class="QMenu" name="menuRecent_Projects">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Recent &Projects</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
<addaction name="actionNew_Project"/>
|
<addaction name="actionNew_Project"/>
|
||||||
<addaction name="actionOpen_Project"/>
|
<addaction name="actionOpen_Project"/>
|
||||||
|
<addaction name="menuRecent_Projects"/>
|
||||||
|
<addaction name="actionSave_Project"/>
|
||||||
|
<addaction name="actionRevert_Project"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="actionImport"/>
|
<addaction name="actionImport"/>
|
||||||
<addaction name="actionExport_GameCube_Groups"/>
|
<addaction name="actionExport_GameCube_Groups"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -417,6 +540,22 @@
|
||||||
<string>New &Curve</string>
|
<string>New &Curve</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSave_Project">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Save Project</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionRevert_Project">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Revert Project</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -430,6 +569,21 @@
|
||||||
<extends>QStatusBar</extends>
|
<extends>QStatusBar</extends>
|
||||||
<header>StatusBarWidget.hpp</header>
|
<header>StatusBarWidget.hpp</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>ModulationSlider</class>
|
||||||
|
<extends>QSlider</extends>
|
||||||
|
<header>KeyboardWidget.hpp</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>PitchSlider</class>
|
||||||
|
<extends>QSlider</extends>
|
||||||
|
<header>KeyboardWidget.hpp</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>VelocitySlider</class>
|
||||||
|
<extends>QSlider</extends>
|
||||||
|
<header>KeyboardWidget.hpp</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|
|
@ -3,13 +3,98 @@
|
||||||
#include "ProjectModel.hpp"
|
#include "ProjectModel.hpp"
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
#include "athena/YAMLDocWriter.hpp"
|
#include "athena/YAMLDocWriter.hpp"
|
||||||
|
#include "MainWindow.hpp"
|
||||||
|
#include <QUndoCommand>
|
||||||
|
|
||||||
QIcon ProjectModel::GroupNode::Icon;
|
QIcon ProjectModel::GroupNode::Icon;
|
||||||
QIcon ProjectModel::SongGroupNode::Icon;
|
QIcon ProjectModel::SongGroupNode::Icon;
|
||||||
QIcon ProjectModel::SoundGroupNode::Icon;
|
QIcon ProjectModel::SoundGroupNode::Icon;
|
||||||
|
|
||||||
|
NullItemProxyModel::NullItemProxyModel(ProjectModel* source)
|
||||||
|
: QIdentityProxyModel(source)
|
||||||
|
{
|
||||||
|
setSourceModel(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex NullItemProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
|
||||||
|
{
|
||||||
|
if (!sourceIndex.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
if (sourceIndex.row() == sourceModel()->rowCount(sourceIndex.parent()))
|
||||||
|
return createIndex(0, sourceIndex.column(), sourceIndex.internalPointer());
|
||||||
|
return createIndex(sourceIndex.row() + 1, sourceIndex.column(), sourceIndex.internalPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex NullItemProxyModel::mapToSource(const QModelIndex& proxyIndex) const
|
||||||
|
{
|
||||||
|
if (!proxyIndex.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
return static_cast<ProjectModel*>(sourceModel())->
|
||||||
|
proxyCreateIndex(proxyIndex.row() - 1, proxyIndex.column(), proxyIndex.internalPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
int NullItemProxyModel::rowCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
return QIdentityProxyModel::rowCount(parent) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex NullItemProxyModel::index(int row, int column, const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
const QModelIndex sourceParent = mapToSource(parent);
|
||||||
|
const QModelIndex sourceIndex = sourceModel()->index(row - 1, column, sourceParent);
|
||||||
|
return mapFromSource(sourceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant NullItemProxyModel::data(const QModelIndex& proxyIndex, int role) const
|
||||||
|
{
|
||||||
|
if (!proxyIndex.isValid() || proxyIndex.row() == 0)
|
||||||
|
return QVariant();
|
||||||
|
return QIdentityProxyModel::data(proxyIndex, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectModel::INode::INode(INode* parent, int row) : m_parent(parent), m_row(row)
|
||||||
|
{
|
||||||
|
m_nullChild = std::make_unique<NullNode>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectModel::CollectionNode* ProjectModel::GroupNode::getCollectionOfType(Type tp) const
|
||||||
|
{
|
||||||
|
for (auto it = m_children.rbegin(); it != m_children.rend(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it)->type() == Type::Collection)
|
||||||
|
{
|
||||||
|
CollectionNode* col = static_cast<CollectionNode*>(it->get());
|
||||||
|
if (col->collectionType() == tp)
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProjectModel::CollectionNode::indexOfId(amuse::ObjectId id) const
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
for (auto& n : m_children)
|
||||||
|
{
|
||||||
|
if (static_cast<BasePoolObjectNode*>(n.get())->id() == id)
|
||||||
|
return ret;
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amuse::ObjectId ProjectModel::CollectionNode::idOfIndex(int idx) const
|
||||||
|
{
|
||||||
|
return static_cast<BasePoolObjectNode*>(m_children[idx].get())->id();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectModel::BasePoolObjectNode* ProjectModel::CollectionNode::nodeOfIndex(int idx) const
|
||||||
|
{
|
||||||
|
return static_cast<BasePoolObjectNode*>(m_children[idx].get());
|
||||||
|
}
|
||||||
|
|
||||||
ProjectModel::ProjectModel(const QString& path, QObject* parent)
|
ProjectModel::ProjectModel(const QString& path, QObject* parent)
|
||||||
: QAbstractItemModel(parent), m_dir(path)
|
: QAbstractItemModel(parent), m_dir(path), m_nullProxy(this)
|
||||||
{
|
{
|
||||||
m_root = std::make_shared<RootNode>();
|
m_root = std::make_shared<RootNode>();
|
||||||
|
|
||||||
|
@ -43,8 +128,8 @@ bool ProjectModel::importGroupData(const QString& groupName, const amuse::AudioG
|
||||||
m_projectDatabase.setIdDatabases();
|
m_projectDatabase.setIdDatabases();
|
||||||
|
|
||||||
amuse::AudioGroupDatabase& grp = m_groups.insert(std::make_pair(groupName, data)).first->second;
|
amuse::AudioGroupDatabase& grp = m_groups.insert(std::make_pair(groupName, data)).first->second;
|
||||||
grp.setIdDatabases();
|
//grp.setIdDatabases();
|
||||||
amuse::AudioGroupProject::BootstrapObjectIDs(data);
|
//amuse::AudioGroupProject::BootstrapObjectIDs(data);
|
||||||
|
|
||||||
if (!MkPath(m_dir.path(), messenger))
|
if (!MkPath(m_dir.path(), messenger))
|
||||||
return false;
|
return false;
|
||||||
|
@ -120,15 +205,13 @@ void ProjectModel::_resetModelData()
|
||||||
gn.makeChild<SongGroupNode>(grp.first, grp.second.get());
|
gn.makeChild<SongGroupNode>(grp.first, grp.second.get());
|
||||||
for (const auto& grp : SortUnorderedMap(sfxGroups))
|
for (const auto& grp : SortUnorderedMap(sfxGroups))
|
||||||
gn.makeChild<SoundGroupNode>(grp.first, grp.second.get());
|
gn.makeChild<SoundGroupNode>(grp.first, grp.second.get());
|
||||||
if (soundMacros.size())
|
|
||||||
{
|
{
|
||||||
CollectionNode& col =
|
CollectionNode& col =
|
||||||
gn.makeChild<CollectionNode>(tr("Sound Macros"), QIcon(":/icons/IconSoundMacro.svg"));
|
gn.makeChild<CollectionNode>(tr("Sound Macros"), QIcon(":/icons/IconSoundMacro.svg"), INode::Type::SoundMacro);
|
||||||
col.reserve(soundMacros.size());
|
col.reserve(soundMacros.size());
|
||||||
for (const auto& macro : SortUnorderedMap(soundMacros))
|
for (const auto& macro : SortUnorderedMap(soundMacros))
|
||||||
col.makeChild<SoundMacroNode>(macro.first, macro.second.get());
|
col.makeChild<SoundMacroNode>(macro.first, macro.second.get());
|
||||||
}
|
}
|
||||||
if (tables.size())
|
|
||||||
{
|
{
|
||||||
auto tablesSort = SortUnorderedMap(tables);
|
auto tablesSort = SortUnorderedMap(tables);
|
||||||
size_t ADSRCount = 0;
|
size_t ADSRCount = 0;
|
||||||
|
@ -141,10 +224,9 @@ void ProjectModel::_resetModelData()
|
||||||
else if (tp == amuse::ITable::Type::Curve)
|
else if (tp == amuse::ITable::Type::Curve)
|
||||||
curveCount += 1;
|
curveCount += 1;
|
||||||
}
|
}
|
||||||
if (ADSRCount)
|
|
||||||
{
|
{
|
||||||
CollectionNode& col =
|
CollectionNode& col =
|
||||||
gn.makeChild<CollectionNode>(tr("ADSRs"), QIcon(":/icons/IconADSR.svg"));
|
gn.makeChild<CollectionNode>(tr("ADSRs"), QIcon(":/icons/IconADSR.svg"), INode::Type::ADSR);
|
||||||
col.reserve(ADSRCount);
|
col.reserve(ADSRCount);
|
||||||
for (auto& t : tablesSort)
|
for (auto& t : tablesSort)
|
||||||
{
|
{
|
||||||
|
@ -153,10 +235,9 @@ void ProjectModel::_resetModelData()
|
||||||
col.makeChild<ADSRNode>(t.first, t.second.get());
|
col.makeChild<ADSRNode>(t.first, t.second.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (curveCount)
|
|
||||||
{
|
{
|
||||||
CollectionNode& col =
|
CollectionNode& col =
|
||||||
gn.makeChild<CollectionNode>(tr("Curves"), QIcon(":/icons/IconCurve.svg"));
|
gn.makeChild<CollectionNode>(tr("Curves"), QIcon(":/icons/IconCurve.svg"), INode::Type::Curve);
|
||||||
col.reserve(curveCount);
|
col.reserve(curveCount);
|
||||||
for (auto& t : tablesSort)
|
for (auto& t : tablesSort)
|
||||||
{
|
{
|
||||||
|
@ -166,18 +247,16 @@ void ProjectModel::_resetModelData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (keymaps.size())
|
|
||||||
{
|
{
|
||||||
CollectionNode& col =
|
CollectionNode& col =
|
||||||
gn.makeChild<CollectionNode>(tr("Keymaps"), QIcon(":/icons/IconKeymap.svg"));
|
gn.makeChild<CollectionNode>(tr("Keymaps"), QIcon(":/icons/IconKeymap.svg"), INode::Type::Keymap);
|
||||||
col.reserve(keymaps.size());
|
col.reserve(keymaps.size());
|
||||||
for (auto& keymap : SortUnorderedMap(keymaps))
|
for (auto& keymap : SortUnorderedMap(keymaps))
|
||||||
col.makeChild<KeymapNode>(keymap.first, keymap.second.get());
|
col.makeChild<KeymapNode>(keymap.first, keymap.second.get());
|
||||||
}
|
}
|
||||||
if (layers.size())
|
|
||||||
{
|
{
|
||||||
CollectionNode& col =
|
CollectionNode& col =
|
||||||
gn.makeChild<CollectionNode>(tr("Layers"), QIcon(":/icons/IconLayers.svg"));
|
gn.makeChild<CollectionNode>(tr("Layers"), QIcon(":/icons/IconLayers.svg"), INode::Type::Layer);
|
||||||
col.reserve(layers.size());
|
col.reserve(layers.size());
|
||||||
for (auto& keymap : SortUnorderedMap(layers))
|
for (auto& keymap : SortUnorderedMap(layers))
|
||||||
col.makeChild<LayersNode>(keymap.first, keymap.second.get());
|
col.makeChild<LayersNode>(keymap.first, keymap.second.get());
|
||||||
|
@ -195,8 +274,30 @@ void ProjectModel::ensureModelData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QModelIndex ProjectModel::proxyCreateIndex(int arow, int acolumn, void *adata) const
|
||||||
|
{
|
||||||
|
if (arow < 0)
|
||||||
|
{
|
||||||
|
INode* childItem = static_cast<INode*>(adata);
|
||||||
|
return createIndex(childItem->parent()->childCount(), acolumn, adata);
|
||||||
|
}
|
||||||
|
return createIndex(arow, acolumn, adata);
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent) const
|
QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent) const
|
||||||
{
|
{
|
||||||
|
if (row < 0)
|
||||||
|
{
|
||||||
|
INode* parentItem;
|
||||||
|
if (!parent.isValid())
|
||||||
|
parentItem = m_root.get();
|
||||||
|
else
|
||||||
|
parentItem = static_cast<INode*>(parent.internalPointer());
|
||||||
|
|
||||||
|
INode* childItem = parentItem->nullChild();
|
||||||
|
return createIndex(childItem->row(), column, childItem);
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasIndex(row, column, parent))
|
if (!hasIndex(row, column, parent))
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
|
|
||||||
|
@ -213,6 +314,13 @@ QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent)
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QModelIndex ProjectModel::index(INode* node) const
|
||||||
|
{
|
||||||
|
if (node == m_root.get())
|
||||||
|
return QModelIndex();
|
||||||
|
return createIndex(node->row(), 0, node);
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex ProjectModel::parent(const QModelIndex& index) const
|
QModelIndex ProjectModel::parent(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
|
@ -267,22 +375,73 @@ Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return QAbstractItemModel::flags(index);
|
return static_cast<INode*>(index.internalPointer())->flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectModel::INode* ProjectModel::node(const QModelIndex& index) const
|
ProjectModel::INode* ProjectModel::node(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return nullptr;
|
return m_root.get();
|
||||||
return static_cast<INode*>(index.internalPointer());
|
return static_cast<INode*>(index.internalPointer());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectModel::canDelete() const
|
ProjectModel::GroupNode* ProjectModel::getGroupNode(INode* node) const
|
||||||
{
|
{
|
||||||
return false;
|
if (!node)
|
||||||
|
return nullptr;
|
||||||
|
if (node->type() == INode::Type::Group)
|
||||||
|
return static_cast<GroupNode*>(node);
|
||||||
|
return getGroupNode(node->parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectModel::del()
|
bool ProjectModel::canEdit(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return false;
|
||||||
|
return (static_cast<INode*>(index.internalPointer())->flags() & Qt::ItemIsSelectable) != Qt::NoItemFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteNodeUndoCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
QModelIndex m_deleteIdx;
|
||||||
|
std::shared_ptr<ProjectModel::INode> m_node;
|
||||||
|
public:
|
||||||
|
DeleteNodeUndoCommand(const QModelIndex& index)
|
||||||
|
: QUndoCommand(QUndoStack::tr("Delete %1").arg(index.data().toString())), m_deleteIdx(index) {}
|
||||||
|
void undo()
|
||||||
|
{
|
||||||
|
g_MainWindow->projectModel()->_undoDel(m_deleteIdx, std::move(m_node));
|
||||||
|
m_node.reset();
|
||||||
|
}
|
||||||
|
void redo()
|
||||||
|
{
|
||||||
|
m_node = g_MainWindow->projectModel()->_redoDel(m_deleteIdx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void ProjectModel::_undoDel(const QModelIndex& index, std::shared_ptr<ProjectModel::INode>&& n)
|
||||||
|
{
|
||||||
|
beginInsertRows(index.parent(), index.row(), index.row());
|
||||||
|
node(index.parent())->insertChild(index.row(), std::move(n));
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ProjectModel::INode> ProjectModel::_redoDel(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
node(index)->depthTraverse([](INode* node)
|
||||||
|
{
|
||||||
|
g_MainWindow->aboutToDeleteNode(node);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
beginRemoveRows(index.parent(), index.row(), index.row());
|
||||||
|
std::shared_ptr<ProjectModel::INode> ret = node(index.parent())->removeChild(index.row());
|
||||||
|
endRemoveRows();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectModel::del(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
g_MainWindow->pushUndoCommand(new DeleteNodeUndoCommand(index));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define AMUSE_PROJECT_MODEL_HPP
|
#define AMUSE_PROJECT_MODEL_HPP
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
#include <QIdentityProxyModel>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -12,6 +13,20 @@
|
||||||
#include "amuse/AudioGroupPool.hpp"
|
#include "amuse/AudioGroupPool.hpp"
|
||||||
#include "amuse/AudioGroupSampleDirectory.hpp"
|
#include "amuse/AudioGroupSampleDirectory.hpp"
|
||||||
|
|
||||||
|
class ProjectModel;
|
||||||
|
|
||||||
|
class NullItemProxyModel : public QIdentityProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit NullItemProxyModel(ProjectModel* source);
|
||||||
|
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
|
||||||
|
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
|
||||||
|
int rowCount(const QModelIndex& parent) const;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex& parent) const;
|
||||||
|
QVariant data(const QModelIndex& proxyIndex, int role) const;
|
||||||
|
};
|
||||||
|
|
||||||
class ProjectModel : public QAbstractItemModel
|
class ProjectModel : public QAbstractItemModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -25,6 +40,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
|
NullItemProxyModel m_nullProxy;
|
||||||
|
|
||||||
amuse::ProjectDatabase m_projectDatabase;
|
amuse::ProjectDatabase m_projectDatabase;
|
||||||
std::map<QString, amuse::AudioGroupDatabase> m_groups;
|
std::map<QString, amuse::AudioGroupDatabase> m_groups;
|
||||||
|
@ -35,6 +51,7 @@ public:
|
||||||
public:
|
public:
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
|
Null,
|
||||||
Root,
|
Root,
|
||||||
Group, // Top-level group
|
Group, // Top-level group
|
||||||
SongGroup,
|
SongGroup,
|
||||||
|
@ -46,30 +63,79 @@ public:
|
||||||
Keymap,
|
Keymap,
|
||||||
Layer
|
Layer
|
||||||
};
|
};
|
||||||
private:
|
protected:
|
||||||
INode* m_parent;
|
INode* m_parent;
|
||||||
std::vector<std::shared_ptr<INode>> m_children;
|
std::vector<std::shared_ptr<INode>> m_children;
|
||||||
|
std::unique_ptr<INode> m_nullChild;
|
||||||
int m_row;
|
int m_row;
|
||||||
public:
|
public:
|
||||||
virtual ~INode() = default;
|
virtual ~INode() = default;
|
||||||
INode(INode* parent, int row) : m_parent(parent), m_row(row) {}
|
INode(INode* parent) : m_parent(parent), m_row(0)
|
||||||
|
{
|
||||||
|
/* ONLY USED BY NULL NODE! */
|
||||||
|
}
|
||||||
|
INode(INode* parent, int row);
|
||||||
|
|
||||||
int childCount() const { return int(m_children.size()); }
|
int childCount() const { return int(m_children.size()); }
|
||||||
INode* child(int row) const { return m_children[row].get(); }
|
INode* child(int row) const
|
||||||
|
{
|
||||||
|
if (row == m_children.size())
|
||||||
|
return nullChild();
|
||||||
|
return m_children[row].get();
|
||||||
|
}
|
||||||
|
INode* nullChild() const { return m_nullChild.get(); }
|
||||||
INode* parent() const { return m_parent; }
|
INode* parent() const { return m_parent; }
|
||||||
int row() const { return m_row; }
|
int row() const { return m_row; }
|
||||||
|
|
||||||
|
void reindexRows(int row)
|
||||||
|
{
|
||||||
|
for (auto it = m_children.begin() + row; it != m_children.end(); ++it)
|
||||||
|
(*it)->m_row = row++;
|
||||||
|
m_nullChild->m_row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertChild(int row, std::shared_ptr<INode>&& n)
|
||||||
|
{
|
||||||
|
m_children.insert(m_children.begin() + row, std::move(n));
|
||||||
|
reindexRows(row);
|
||||||
|
}
|
||||||
|
std::shared_ptr<INode> removeChild(int row)
|
||||||
|
{
|
||||||
|
std::shared_ptr<INode> ret = std::move(m_children[row]);
|
||||||
|
m_children.erase(m_children.begin() + row);
|
||||||
|
reindexRows(row);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void reserve(size_t sz) { m_children.reserve(sz); }
|
void reserve(size_t sz) { m_children.reserve(sz); }
|
||||||
template<class T, class... _Args>
|
template<class T, class... _Args>
|
||||||
T& makeChild(_Args&&... args)
|
T& makeChild(_Args&&... args)
|
||||||
{
|
{
|
||||||
m_children.push_back(std::make_shared<T>(this, m_children.size(), std::forward<_Args>(args)...));
|
m_children.push_back(std::make_shared<T>(this, m_children.size(), std::forward<_Args>(args)...));
|
||||||
|
m_nullChild->m_row = int(m_children.size());
|
||||||
return static_cast<T&>(*m_children.back());
|
return static_cast<T&>(*m_children.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool depthTraverse(const std::function<bool(INode* node)>& func)
|
||||||
|
{
|
||||||
|
for (auto& n : m_children)
|
||||||
|
if (!n->depthTraverse(func))
|
||||||
|
break;
|
||||||
|
return func(this);
|
||||||
|
}
|
||||||
|
|
||||||
virtual Type type() const = 0;
|
virtual Type type() const = 0;
|
||||||
virtual QString text() const = 0;
|
virtual QString text() const = 0;
|
||||||
virtual QIcon icon() const = 0;
|
virtual QIcon icon() const = 0;
|
||||||
|
virtual Qt::ItemFlags flags() const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; }
|
||||||
|
};
|
||||||
|
struct NullNode : INode
|
||||||
|
{
|
||||||
|
NullNode(INode* parent) : INode(parent) {}
|
||||||
|
|
||||||
|
Type type() const { return Type::Null; }
|
||||||
|
QString text() const { return {}; }
|
||||||
|
QIcon icon() const { return {}; }
|
||||||
};
|
};
|
||||||
struct RootNode : INode
|
struct RootNode : INode
|
||||||
{
|
{
|
||||||
|
@ -78,7 +144,9 @@ public:
|
||||||
Type type() const { return Type::Root; }
|
Type type() const { return Type::Root; }
|
||||||
QString text() const { return {}; }
|
QString text() const { return {}; }
|
||||||
QIcon icon() const { return {}; }
|
QIcon icon() const { return {}; }
|
||||||
|
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
|
||||||
};
|
};
|
||||||
|
struct CollectionNode;
|
||||||
struct GroupNode : INode
|
struct GroupNode : INode
|
||||||
{
|
{
|
||||||
std::map<QString, amuse::AudioGroupDatabase>::iterator m_it;
|
std::map<QString, amuse::AudioGroupDatabase>::iterator m_it;
|
||||||
|
@ -90,6 +158,9 @@ public:
|
||||||
QString text() const { return m_it->first; }
|
QString text() const { return m_it->first; }
|
||||||
QIcon icon() const { return Icon; }
|
QIcon icon() const { return Icon; }
|
||||||
|
|
||||||
|
CollectionNode* getCollectionOfType(Type tp) const;
|
||||||
|
amuse::AudioGroupDatabase* getAudioGroup() const { return &m_it->second; }
|
||||||
|
|
||||||
std::shared_ptr<GroupNode> shared_from_this()
|
std::shared_ptr<GroupNode> shared_from_this()
|
||||||
{ return std::static_pointer_cast<GroupNode>(INode::shared_from_this()); }
|
{ return std::static_pointer_cast<GroupNode>(INode::shared_from_this()); }
|
||||||
};
|
};
|
||||||
|
@ -125,28 +196,42 @@ public:
|
||||||
std::shared_ptr<SoundGroupNode> shared_from_this()
|
std::shared_ptr<SoundGroupNode> shared_from_this()
|
||||||
{ return std::static_pointer_cast<SoundGroupNode>(INode::shared_from_this()); }
|
{ return std::static_pointer_cast<SoundGroupNode>(INode::shared_from_this()); }
|
||||||
};
|
};
|
||||||
|
struct BasePoolObjectNode;
|
||||||
struct CollectionNode : INode
|
struct CollectionNode : INode
|
||||||
{
|
{
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QIcon m_icon;
|
QIcon m_icon;
|
||||||
CollectionNode(INode* parent, int row, const QString& name, const QIcon& icon)
|
Type m_collectionType;
|
||||||
: INode(parent, row), m_name(name), m_icon(icon) {}
|
CollectionNode(INode* parent, int row, const QString& name, const QIcon& icon, Type collectionType)
|
||||||
|
: INode(parent, row), m_name(name), m_icon(icon), m_collectionType(collectionType) {}
|
||||||
|
|
||||||
Type type() const { return Type::Collection; }
|
Type type() const { return Type::Collection; }
|
||||||
QString text() const { return m_name; }
|
QString text() const { return m_name; }
|
||||||
QIcon icon() const { return m_icon; }
|
QIcon icon() const { return m_icon; }
|
||||||
|
Qt::ItemFlags flags() const { return Qt::ItemIsEnabled; }
|
||||||
|
|
||||||
|
Type collectionType() const { return m_collectionType; }
|
||||||
|
int indexOfId(amuse::ObjectId id) const;
|
||||||
|
amuse::ObjectId idOfIndex(int idx) const;
|
||||||
|
BasePoolObjectNode* nodeOfIndex(int idx) const;
|
||||||
|
|
||||||
std::shared_ptr<CollectionNode> shared_from_this()
|
std::shared_ptr<CollectionNode> shared_from_this()
|
||||||
{ return std::static_pointer_cast<CollectionNode>(INode::shared_from_this()); }
|
{ return std::static_pointer_cast<CollectionNode>(INode::shared_from_this()); }
|
||||||
};
|
};
|
||||||
template <class ID, class T, INode::Type TP>
|
struct BasePoolObjectNode : INode
|
||||||
struct PoolObjectNode : INode
|
{
|
||||||
|
amuse::ObjectId m_id;
|
||||||
|
BasePoolObjectNode(INode* parent, int row, amuse::ObjectId id)
|
||||||
|
: INode(parent, row), m_id(id) {}
|
||||||
|
amuse::ObjectId id() const { return m_id; }
|
||||||
|
};
|
||||||
|
template <class ID, class T, INode::Type TP>
|
||||||
|
struct PoolObjectNode : BasePoolObjectNode
|
||||||
{
|
{
|
||||||
ID m_id;
|
|
||||||
QString m_name;
|
QString m_name;
|
||||||
std::shared_ptr<T> m_obj;
|
std::shared_ptr<T> m_obj;
|
||||||
PoolObjectNode(INode* parent, int row, ID id, std::shared_ptr<T> obj)
|
PoolObjectNode(INode* parent, int row, ID id, std::shared_ptr<T> obj)
|
||||||
: INode(parent, row), m_id(id), m_name(ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
|
: BasePoolObjectNode(parent, row, id), m_name(ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
|
||||||
|
|
||||||
Type type() const { return TP; }
|
Type type() const { return TP; }
|
||||||
QString text() const { return m_name; }
|
QString text() const { return m_name; }
|
||||||
|
@ -177,22 +262,23 @@ public:
|
||||||
|
|
||||||
void ensureModelData();
|
void ensureModelData();
|
||||||
|
|
||||||
|
QModelIndex proxyCreateIndex(int arow, int acolumn, void *adata) const;
|
||||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
|
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
|
||||||
|
QModelIndex index(INode* node) const;
|
||||||
QModelIndex parent(const QModelIndex& child) const;
|
QModelIndex parent(const QModelIndex& child) const;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
int columnCount(const QModelIndex& parent = QModelIndex()) const;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
INode* node(const QModelIndex& index) const;
|
INode* node(const QModelIndex& index) const;
|
||||||
|
GroupNode* getGroupNode(INode* node) const;
|
||||||
|
bool canEdit(const QModelIndex& index) const;
|
||||||
|
void _undoDel(const QModelIndex& index, std::shared_ptr<ProjectModel::INode>&& node);
|
||||||
|
std::shared_ptr<ProjectModel::INode> _redoDel(const QModelIndex& index);
|
||||||
|
void del(const QModelIndex& index);
|
||||||
|
|
||||||
QString path() const { return m_dir.path(); }
|
QString path() const { return m_dir.path(); }
|
||||||
bool canDelete() const;
|
NullItemProxyModel* getNullProxy() { return &m_nullProxy; }
|
||||||
|
|
||||||
public slots:
|
|
||||||
void del();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void canDeleteChanged(bool canDelete);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,124 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
|
||||||
CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, QWidget* parent)
|
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
|
||||||
: QWidget(parent), m_cmd(cmd), m_introspection(amuse::SoundMacro::GetCmdIntrospection(op))
|
: FieldComboBox(parent), m_collection(collection)
|
||||||
|
{
|
||||||
|
ProjectModel* model = g_MainWindow->projectModel();
|
||||||
|
setModel(model->getNullProxy());
|
||||||
|
setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetButton::TargetButton(QWidget* parent)
|
||||||
|
: QPushButton(parent)
|
||||||
|
{
|
||||||
|
QIcon targetIcon(QStringLiteral(":/icons/IconSoundMacroTarget.svg"));
|
||||||
|
targetIcon.addFile(QStringLiteral(":/icons/IconSoundMacroTargetDisabled.svg"), QSize(), QIcon::Disabled);
|
||||||
|
setIcon(targetIcon);
|
||||||
|
setToolTip(tr("Set step with target click"));
|
||||||
|
setFixedSize(29, 29);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundMacroEditor* FieldSoundMacroStep::getEditor() const
|
||||||
|
{
|
||||||
|
return qobject_cast<SoundMacroEditor*>(
|
||||||
|
parentWidget()->parentWidget()->parentWidget()->
|
||||||
|
parentWidget()->parentWidget()->parentWidget()->parentWidget());
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundMacroListing* FieldSoundMacroStep::getListing() const
|
||||||
|
{
|
||||||
|
return qobject_cast<SoundMacroListing*>(
|
||||||
|
parentWidget()->parentWidget()->parentWidget());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldSoundMacroStep::targetPressed()
|
||||||
|
{
|
||||||
|
ProjectModel::SoundMacroNode* node = nullptr;
|
||||||
|
if (m_macroField)
|
||||||
|
{
|
||||||
|
int val = m_macroField->currentIndex();
|
||||||
|
if (val != 0)
|
||||||
|
node = static_cast<ProjectModel::SoundMacroNode*>(m_macroField->collection()->nodeOfIndex(val - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_macroField || node == getListing()->currentNode())
|
||||||
|
if (SoundMacroEditor* editor = getEditor())
|
||||||
|
editor->beginStepTarget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldSoundMacroStep::updateMacroField()
|
||||||
|
{
|
||||||
|
if (!m_macroField)
|
||||||
|
{
|
||||||
|
int numCmds = int(static_cast<ProjectModel::SoundMacroNode*>(
|
||||||
|
getListing()->currentNode())->m_obj->m_cmds.size());
|
||||||
|
m_spinBox.setMaximum(numCmds - 1);
|
||||||
|
m_spinBox.setDisabled(false);
|
||||||
|
m_targetButton.setDisabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = m_macroField->currentIndex();
|
||||||
|
if (val == 0)
|
||||||
|
{
|
||||||
|
m_spinBox.setValue(0);
|
||||||
|
m_spinBox.setDisabled(true);
|
||||||
|
m_targetButton.setDisabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProjectModel::SoundMacroNode* node = static_cast<ProjectModel::SoundMacroNode*>(
|
||||||
|
m_macroField->collection()->nodeOfIndex(val - 1));
|
||||||
|
int numCmds = int(node->m_obj->m_cmds.size());
|
||||||
|
m_spinBox.setMaximum(numCmds - 1);
|
||||||
|
m_spinBox.setDisabled(false);
|
||||||
|
m_targetButton.setEnabled(node == getListing()->currentNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldSoundMacroStep::setIndex(int index)
|
||||||
|
{
|
||||||
|
m_targetButton.setDown(false);
|
||||||
|
m_spinBox.setValue(index);
|
||||||
|
if (SoundMacroEditor* editor = getEditor())
|
||||||
|
editor->endStepTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FieldSoundMacroStep::cancel()
|
||||||
|
{
|
||||||
|
m_targetButton.setDown(false);
|
||||||
|
if (SoundMacroEditor* editor = getEditor())
|
||||||
|
editor->endStepTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldSoundMacroStep::~FieldSoundMacroStep()
|
||||||
|
{
|
||||||
|
if (SoundMacroEditor* editor = getEditor())
|
||||||
|
if (editor->m_targetField == this)
|
||||||
|
editor->endStepTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldSoundMacroStep::FieldSoundMacroStep(FieldProjectNode* macroField, QWidget* parent)
|
||||||
|
: QWidget(parent), m_macroField(macroField)
|
||||||
|
{
|
||||||
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
layout->setContentsMargins(QMargins());
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(&m_spinBox);
|
||||||
|
layout->addWidget(&m_targetButton);
|
||||||
|
m_spinBox.setMinimum(0);
|
||||||
|
m_spinBox.setDisabled(true);
|
||||||
|
m_targetButton.setDisabled(true);
|
||||||
|
connect(&m_spinBox, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int)));
|
||||||
|
connect(&m_targetButton, SIGNAL(pressed()), this, SLOT(targetPressed()));
|
||||||
|
if (macroField)
|
||||||
|
connect(macroField, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMacroField()));
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing)
|
||||||
|
: QWidget(nullptr), m_cmd(cmd), m_introspection(amuse::SoundMacro::GetCmdIntrospection(op))
|
||||||
{
|
{
|
||||||
QFont titleFont = m_titleLabel.font();
|
QFont titleFont = m_titleLabel.font();
|
||||||
titleFont.setWeight(QFont::Bold);
|
titleFont.setWeight(QFont::Bold);
|
||||||
|
@ -44,6 +160,7 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
|
||||||
m_deleteButton.setFixedSize(21, 21);
|
m_deleteButton.setFixedSize(21, 21);
|
||||||
m_deleteButton.setIcon(QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg")));
|
m_deleteButton.setIcon(QIcon(QStringLiteral(":/icons/IconSoundMacroDelete.svg")));
|
||||||
m_deleteButton.setFlat(true);
|
m_deleteButton.setFlat(true);
|
||||||
|
m_deleteButton.setToolTip(tr("Delete this SoundMacro"));
|
||||||
connect(&m_deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked()));
|
connect(&m_deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteClicked()));
|
||||||
headLayout->addWidget(&m_deleteButton);
|
headLayout->addWidget(&m_deleteButton);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +175,7 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
|
||||||
{
|
{
|
||||||
m_titleLabel.setText(tr(m_introspection->m_name.data()));
|
m_titleLabel.setText(tr(m_introspection->m_name.data()));
|
||||||
m_titleLabel.setToolTip(tr(m_introspection->m_description.data()));
|
m_titleLabel.setToolTip(tr(m_introspection->m_description.data()));
|
||||||
|
FieldProjectNode* nf = nullptr;
|
||||||
for (int f = 0; f < 7; ++f)
|
for (int f = 0; f < 7; ++f)
|
||||||
{
|
{
|
||||||
const amuse::SoundMacro::CmdIntrospection::Field& field = m_introspection->m_fields[f];
|
const amuse::SoundMacro::CmdIntrospection::Field& field = m_introspection->m_fields[f];
|
||||||
|
@ -89,6 +207,7 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
|
||||||
sb->setProperty("fieldName", fieldName);
|
sb->setProperty("fieldName", fieldName);
|
||||||
sb->setMinimum(int(field.m_min));
|
sb->setMinimum(int(field.m_min));
|
||||||
sb->setMaximum(int(field.m_max));
|
sb->setMaximum(int(field.m_max));
|
||||||
|
sb->setToolTip(QStringLiteral("[%1,%2]").arg(int(field.m_min)).arg(int(field.m_max)));
|
||||||
switch (field.m_tp)
|
switch (field.m_tp)
|
||||||
{
|
{
|
||||||
case amuse::SoundMacro::CmdIntrospection::Field::Type::Int8:
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::Int8:
|
||||||
|
@ -116,6 +235,44 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
|
||||||
layout->addWidget(sb, 1, f);
|
layout->addWidget(sb, 1, f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
|
||||||
|
{
|
||||||
|
ProjectModel::INode::Type collectionType;
|
||||||
|
if (field.m_tp == amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId)
|
||||||
|
{
|
||||||
|
collectionType = ProjectModel::INode::Type::SoundMacro;
|
||||||
|
}
|
||||||
|
else if (field.m_tp == amuse::SoundMacro::CmdIntrospection::Field::Type::TableId)
|
||||||
|
{
|
||||||
|
if (!field.m_name.compare("ADSR"))
|
||||||
|
collectionType = ProjectModel::INode::Type::ADSR;
|
||||||
|
else
|
||||||
|
collectionType = ProjectModel::INode::Type::Curve;
|
||||||
|
}
|
||||||
|
auto* collection = g_MainWindow->projectModel()->getGroupNode(listing->currentNode())->
|
||||||
|
getCollectionOfType(collectionType);
|
||||||
|
nf = new FieldProjectNode(collection);
|
||||||
|
nf->setProperty("fieldIndex", f);
|
||||||
|
nf->setProperty("fieldName", fieldName);
|
||||||
|
int index = collection->indexOfId(
|
||||||
|
amuse::AccessField<amuse::SoundMacroIdDNA<athena::Little>>(m_cmd, field).id);
|
||||||
|
nf->setCurrentIndex(index < 0 ? 0 : index + 1);
|
||||||
|
connect(nf, SIGNAL(currentIndexChanged(int)), this, SLOT(nodeChanged(int)));
|
||||||
|
layout->addWidget(nf, 1, f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroStep:
|
||||||
|
{
|
||||||
|
FieldSoundMacroStep* sb = new FieldSoundMacroStep(nf);
|
||||||
|
sb->setProperty("fieldIndex", f);
|
||||||
|
sb->setProperty("fieldName", fieldName);
|
||||||
|
sb->m_spinBox.setValue(amuse::AccessField<amuse::SoundMacroStepDNA<athena::Little>>(m_cmd, field).step);
|
||||||
|
connect(sb, SIGNAL(valueChanged(int)), this, SLOT(numChanged(int)));
|
||||||
|
layout->addWidget(sb, 1, f);
|
||||||
|
m_stepField = sb;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case amuse::SoundMacro::CmdIntrospection::Field::Type::Choice:
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::Choice:
|
||||||
{
|
{
|
||||||
FieldComboBox* cb = new FieldComboBox;
|
FieldComboBox* cb = new FieldComboBox;
|
||||||
|
@ -128,7 +285,7 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
|
||||||
cb->addItem(tr(field.m_choices[j].data()));
|
cb->addItem(tr(field.m_choices[j].data()));
|
||||||
}
|
}
|
||||||
cb->setCurrentIndex(int(amuse::AccessField<int8_t>(m_cmd, field)));
|
cb->setCurrentIndex(int(amuse::AccessField<int8_t>(m_cmd, field)));
|
||||||
connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(choiceChanged(int)));
|
connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(numChanged(int)));
|
||||||
layout->addWidget(cb, 1, f);
|
layout->addWidget(cb, 1, f);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -144,11 +301,11 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, QWidget* parent)
|
CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing)
|
||||||
: CommandWidget(cmd, cmd->Isa(), parent) {}
|
: CommandWidget(cmd, cmd->Isa(), listing) {}
|
||||||
|
|
||||||
CommandWidget::CommandWidget(amuse::SoundMacro::CmdOp op, QWidget* parent)
|
CommandWidget::CommandWidget(amuse::SoundMacro::CmdOp op, SoundMacroListing* listing)
|
||||||
: CommandWidget(nullptr, op, parent) {}
|
: CommandWidget(nullptr, op, listing) {}
|
||||||
|
|
||||||
class ValChangedUndoCommand : public EditorUndoCommand
|
class ValChangedUndoCommand : public EditorUndoCommand
|
||||||
{
|
{
|
||||||
|
@ -189,6 +346,12 @@ public:
|
||||||
case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt32:
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt32:
|
||||||
amuse::AccessField<uint32_t>(m_cmd, m_field) = uint32_t(m_undoVal);
|
amuse::AccessField<uint32_t>(m_cmd, m_field) = uint32_t(m_undoVal);
|
||||||
break;
|
break;
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroStep:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId:
|
||||||
|
amuse::AccessField<amuse::SoundMacroIdDNA<athena::Little>>(m_cmd, m_field).id = uint16_t(m_undoVal);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +390,13 @@ public:
|
||||||
m_undoVal = amuse::AccessField<uint32_t>(m_cmd, m_field);
|
m_undoVal = amuse::AccessField<uint32_t>(m_cmd, m_field);
|
||||||
amuse::AccessField<uint32_t>(m_cmd, m_field) = uint32_t(m_redoVal);
|
amuse::AccessField<uint32_t>(m_cmd, m_field) = uint32_t(m_redoVal);
|
||||||
break;
|
break;
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroStep:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId:
|
||||||
|
m_undoVal = amuse::AccessField<amuse::SoundMacroIdDNA<athena::Little>>(m_cmd, m_field).id;
|
||||||
|
amuse::AccessField<amuse::SoundMacroIdDNA<athena::Little>>(m_cmd, m_field).id = uint16_t(m_redoVal);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -250,11 +420,10 @@ void CommandWidget::boolChanged(int state)
|
||||||
{
|
{
|
||||||
if (m_introspection)
|
if (m_introspection)
|
||||||
{
|
{
|
||||||
QCheckBox* cb = static_cast<QCheckBox*>(sender());
|
|
||||||
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
||||||
m_introspection->m_fields[cb->property("fieldIndex").toInt()];
|
m_introspection->m_fields[sender()->property("fieldIndex").toInt()];
|
||||||
g_MainWindow->pushUndoCommand(new ValChangedUndoCommand(m_cmd, cb->property("fieldName").toString(), field,
|
g_MainWindow->pushUndoCommand(new ValChangedUndoCommand(m_cmd, sender()->property("fieldName").toString(),
|
||||||
state == Qt::Checked, getParent()->m_node));
|
field, state == Qt::Checked, getParent()->m_node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,23 +431,23 @@ void CommandWidget::numChanged(int value)
|
||||||
{
|
{
|
||||||
if (m_introspection)
|
if (m_introspection)
|
||||||
{
|
{
|
||||||
FieldSpinBox* sb = static_cast<FieldSpinBox*>(sender());
|
|
||||||
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
||||||
m_introspection->m_fields[sb->property("fieldIndex").toInt()];
|
m_introspection->m_fields[sender()->property("fieldIndex").toInt()];
|
||||||
g_MainWindow->pushUndoCommand(new ValChangedUndoCommand(m_cmd, sb->property("fieldName").toString(), field,
|
g_MainWindow->pushUndoCommand(new ValChangedUndoCommand(m_cmd, sender()->property("fieldName").toString(),
|
||||||
value, getParent()->m_node));
|
field, value, getParent()->m_node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandWidget::choiceChanged(int choice)
|
void CommandWidget::nodeChanged(int value)
|
||||||
{
|
{
|
||||||
if (m_introspection)
|
if (m_introspection)
|
||||||
{
|
{
|
||||||
FieldComboBox* cb = static_cast<FieldComboBox*>(sender());
|
FieldProjectNode* fieldW = static_cast<FieldProjectNode*>(sender());
|
||||||
|
int v = value == 0 ? 65535 : fieldW->collection()->idOfIndex(value - 1).id;
|
||||||
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
||||||
m_introspection->m_fields[cb->property("fieldIndex").toInt()];
|
m_introspection->m_fields[fieldW->property("fieldIndex").toInt()];
|
||||||
g_MainWindow->pushUndoCommand(new ValChangedUndoCommand(m_cmd, cb->property("fieldName").toString(), field,
|
g_MainWindow->pushUndoCommand(new ValChangedUndoCommand(m_cmd, fieldW->property("fieldName").toString(),
|
||||||
choice, getParent()->m_node));
|
field, v, getParent()->m_node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +462,8 @@ void CommandWidget::setIndex(int index)
|
||||||
{
|
{
|
||||||
m_index = index;
|
m_index = index;
|
||||||
m_numberText.setText(QString::number(index));
|
m_numberText.setText(QString::number(index));
|
||||||
|
if (m_stepField)
|
||||||
|
m_stepField->updateMacroField();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,10 +564,10 @@ CommandWidgetContainer::CommandWidgetContainer(CommandWidget* child, QWidget* pa
|
||||||
{
|
{
|
||||||
setMinimumHeight(100);
|
setMinimumHeight(100);
|
||||||
setContentsMargins(QMargins());
|
setContentsMargins(QMargins());
|
||||||
QBoxLayout* outerLayout = new QVBoxLayout;
|
QVBoxLayout* outerLayout = new QVBoxLayout;
|
||||||
|
outerLayout->setAlignment(Qt::AlignBottom);
|
||||||
outerLayout->setContentsMargins(QMargins());
|
outerLayout->setContentsMargins(QMargins());
|
||||||
outerLayout->setSpacing(0);
|
outerLayout->setSpacing(0);
|
||||||
outerLayout->addStretch();
|
|
||||||
outerLayout->addWidget(child);
|
outerLayout->addWidget(child);
|
||||||
setLayout(outerLayout);
|
setLayout(outerLayout);
|
||||||
}
|
}
|
||||||
|
@ -472,13 +643,13 @@ public:
|
||||||
void undo()
|
void undo()
|
||||||
{
|
{
|
||||||
m_undid = true;
|
m_undid = true;
|
||||||
std::static_pointer_cast<ProjectModel::SoundMacroNode>(m_node)->
|
static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||||
m_obj->swapPositions(m_a, m_b);
|
m_obj->swapPositions(m_a, m_b);
|
||||||
EditorUndoCommand::undo();
|
EditorUndoCommand::undo();
|
||||||
}
|
}
|
||||||
void redo()
|
void redo()
|
||||||
{
|
{
|
||||||
std::static_pointer_cast<ProjectModel::SoundMacroNode>(m_node)->
|
static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||||
m_obj->swapPositions(m_a, m_b);
|
m_obj->swapPositions(m_a, m_b);
|
||||||
if (m_undid)
|
if (m_undid)
|
||||||
EditorUndoCommand::redo();
|
EditorUndoCommand::redo();
|
||||||
|
@ -605,7 +776,7 @@ public:
|
||||||
: EditorUndoCommand(node, QUndoStack::tr("Insert %1").arg(text)), m_insertIdx(insertIdx) {}
|
: EditorUndoCommand(node, QUndoStack::tr("Insert %1").arg(text)), m_insertIdx(insertIdx) {}
|
||||||
void undo()
|
void undo()
|
||||||
{
|
{
|
||||||
m_cmd = std::static_pointer_cast<ProjectModel::SoundMacroNode>(m_node)->
|
m_cmd = static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||||
m_obj->deleteCmd(m_insertIdx);
|
m_obj->deleteCmd(m_insertIdx);
|
||||||
EditorUndoCommand::undo();
|
EditorUndoCommand::undo();
|
||||||
}
|
}
|
||||||
|
@ -613,7 +784,7 @@ public:
|
||||||
{
|
{
|
||||||
if (!m_cmd)
|
if (!m_cmd)
|
||||||
return;
|
return;
|
||||||
std::static_pointer_cast<ProjectModel::SoundMacroNode>(m_node)->
|
static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||||
m_obj->insertCmd(m_insertIdx, std::move(m_cmd));
|
m_obj->insertCmd(m_insertIdx, std::move(m_cmd));
|
||||||
m_cmd.reset();
|
m_cmd.reset();
|
||||||
EditorUndoCommand::redo();
|
EditorUndoCommand::redo();
|
||||||
|
@ -644,7 +815,7 @@ void SoundMacroListing::insert(amuse::SoundMacro::CmdOp op, const QString& text)
|
||||||
|
|
||||||
g_MainWindow->pushUndoCommand(new InsertCommandUndoCommand(insertIdx, text, m_node));
|
g_MainWindow->pushUndoCommand(new InsertCommandUndoCommand(insertIdx, text, m_node));
|
||||||
m_layout->insertWidget(insertIdx,
|
m_layout->insertWidget(insertIdx,
|
||||||
new CommandWidgetContainer(new CommandWidget(m_node->m_obj->insertNewCmd(insertIdx, op))));
|
new CommandWidgetContainer(new CommandWidget(m_node->m_obj->insertNewCmd(insertIdx, op), this)));
|
||||||
|
|
||||||
stopAutoscroll();
|
stopAutoscroll();
|
||||||
reindex();
|
reindex();
|
||||||
|
@ -661,14 +832,14 @@ public:
|
||||||
void undo()
|
void undo()
|
||||||
{
|
{
|
||||||
m_undid = true;
|
m_undid = true;
|
||||||
std::static_pointer_cast<ProjectModel::SoundMacroNode>(m_node)->
|
static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||||
m_obj->insertCmd(m_deleteIdx, std::move(m_cmd));
|
m_obj->insertCmd(m_deleteIdx, std::move(m_cmd));
|
||||||
m_cmd.reset();
|
m_cmd.reset();
|
||||||
EditorUndoCommand::undo();
|
EditorUndoCommand::undo();
|
||||||
}
|
}
|
||||||
void redo()
|
void redo()
|
||||||
{
|
{
|
||||||
m_cmd = std::static_pointer_cast<ProjectModel::SoundMacroNode>(m_node)->
|
m_cmd = static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||||
m_obj->deleteCmd(m_deleteIdx);
|
m_obj->deleteCmd(m_deleteIdx);
|
||||||
if (m_undid)
|
if (m_undid)
|
||||||
EditorUndoCommand::redo();
|
EditorUndoCommand::redo();
|
||||||
|
@ -712,9 +883,10 @@ bool SoundMacroListing::loadData(ProjectModel::SoundMacroNode* node)
|
||||||
{
|
{
|
||||||
if (cmd->Isa() == amuse::SoundMacro::CmdOp::End)
|
if (cmd->Isa() == amuse::SoundMacro::CmdOp::End)
|
||||||
break;
|
break;
|
||||||
m_layout->insertWidget(i++, new CommandWidgetContainer(new CommandWidget(cmd.get())));
|
m_layout->insertWidget(i++, new CommandWidgetContainer(new CommandWidget(cmd.get(), this)));
|
||||||
}
|
}
|
||||||
reindex();
|
reindex();
|
||||||
|
update();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,12 +895,18 @@ void SoundMacroListing::unloadData()
|
||||||
m_node.reset();
|
m_node.reset();
|
||||||
clear();
|
clear();
|
||||||
reindex();
|
reindex();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectModel::INode* SoundMacroListing::currentNode() const
|
||||||
|
{
|
||||||
|
return m_node.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundMacroListing::SoundMacroListing(QWidget* parent)
|
SoundMacroListing::SoundMacroListing(QWidget* parent)
|
||||||
: QWidget(parent), m_layout(new QVBoxLayout)
|
: QWidget(parent), m_layout(new QVBoxLayout)
|
||||||
{
|
{
|
||||||
m_layout->addWidget(new CommandWidgetContainer(new CommandWidget(amuse::SoundMacro::CmdOp::End)));
|
m_layout->addWidget(new CommandWidgetContainer(new CommandWidget(amuse::SoundMacro::CmdOp::End, this)));
|
||||||
m_layout->addStretch();
|
m_layout->addStretch();
|
||||||
setLayout(m_layout);
|
setLayout(m_layout);
|
||||||
reindex();
|
reindex();
|
||||||
|
@ -855,6 +1033,21 @@ void SoundMacroEditor::beginCatalogueDrag(CatalogueItem* item, const QPoint& eve
|
||||||
m_draggedItem->show();
|
m_draggedItem->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundMacroEditor::beginStepTarget(FieldSoundMacroStep* stepField)
|
||||||
|
{
|
||||||
|
m_targetField = stepField;
|
||||||
|
m_catalogue->setDisabled(true);
|
||||||
|
m_listing->setDisabled(true);
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundMacroEditor::endStepTarget()
|
||||||
|
{
|
||||||
|
m_targetField = nullptr;
|
||||||
|
m_catalogue->setDisabled(false);
|
||||||
|
m_listing->setDisabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
void SoundMacroEditor::mousePressEvent(QMouseEvent* event)
|
void SoundMacroEditor::mousePressEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
if (m_catalogue->geometry().contains(event->pos()))
|
if (m_catalogue->geometry().contains(event->pos()))
|
||||||
|
@ -884,11 +1077,19 @@ void SoundMacroEditor::mousePressEvent(QMouseEvent* event)
|
||||||
ch = ch->parentWidget();
|
ch = ch->parentWidget();
|
||||||
if (child)
|
if (child)
|
||||||
{
|
{
|
||||||
|
if (m_targetField)
|
||||||
|
{
|
||||||
|
m_targetField->setIndex(m_listing->layout()->indexOf(child->parentWidget()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
QPoint fromParent2 = child->mapFrom(m_listing, fromParent1);
|
QPoint fromParent2 = child->mapFrom(m_listing, fromParent1);
|
||||||
beginCommandDrag(child, event->pos(), fromParent2);
|
beginCommandDrag(child, event->pos(), fromParent2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_targetField)
|
||||||
|
m_targetField->cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundMacroEditor::mouseReleaseEvent(QMouseEvent* event)
|
void SoundMacroEditor::mouseReleaseEvent(QMouseEvent* event)
|
||||||
|
@ -964,6 +1165,10 @@ void SoundMacroEditor::keyPressEvent(QKeyEvent* event)
|
||||||
m_listing->cancelDrag();
|
m_listing->cancelDrag();
|
||||||
m_draggedCmd = nullptr;
|
m_draggedCmd = nullptr;
|
||||||
}
|
}
|
||||||
|
else if (m_targetField)
|
||||||
|
{
|
||||||
|
m_targetField->cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -979,14 +1184,21 @@ void SoundMacroEditor::catalogueDoubleClicked(QTreeWidgetItem* item, int column)
|
||||||
|
|
||||||
bool SoundMacroEditor::loadData(ProjectModel::SoundMacroNode* node)
|
bool SoundMacroEditor::loadData(ProjectModel::SoundMacroNode* node)
|
||||||
{
|
{
|
||||||
|
endStepTarget();
|
||||||
return m_listing->loadData(node);
|
return m_listing->loadData(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundMacroEditor::unloadData()
|
void SoundMacroEditor::unloadData()
|
||||||
{
|
{
|
||||||
|
endStepTarget();
|
||||||
m_listing->unloadData();
|
m_listing->unloadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProjectModel::INode* SoundMacroEditor::currentNode() const
|
||||||
|
{
|
||||||
|
return m_listing->currentNode();
|
||||||
|
}
|
||||||
|
|
||||||
SoundMacroEditor::SoundMacroEditor(QWidget* parent)
|
SoundMacroEditor::SoundMacroEditor(QWidget* parent)
|
||||||
: EditorWidget(parent), m_splitter(new QSplitter),
|
: EditorWidget(parent), m_splitter(new QSplitter),
|
||||||
m_listing(new SoundMacroListing), m_catalogue(new SoundMacroCatalogue)
|
m_listing(new SoundMacroListing), m_catalogue(new SoundMacroCatalogue)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
|
class SoundMacroEditor;
|
||||||
class SoundMacroListing;
|
class SoundMacroListing;
|
||||||
class CatalogueItem;
|
class CatalogueItem;
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ class FieldSpinBox : public QSpinBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
FieldSpinBox(QWidget* parent = Q_NULLPTR)
|
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR)
|
||||||
: QSpinBox(parent) {}
|
: QSpinBox(parent) {}
|
||||||
|
|
||||||
/* Don't scroll */
|
/* Don't scroll */
|
||||||
|
@ -29,15 +30,54 @@ public:
|
||||||
|
|
||||||
class FieldComboBox : public QComboBox
|
class FieldComboBox : public QComboBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
FieldComboBox(QWidget* parent = Q_NULLPTR)
|
explicit FieldComboBox(QWidget* parent = Q_NULLPTR)
|
||||||
: QComboBox(parent) {}
|
: QComboBox(parent) {}
|
||||||
|
|
||||||
/* Don't scroll */
|
/* Don't scroll */
|
||||||
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
void wheelEvent(QWheelEvent* event) { event->ignore(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FieldProjectNode : public FieldComboBox
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
ProjectModel::CollectionNode* m_collection;
|
||||||
|
public:
|
||||||
|
explicit FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent = Q_NULLPTR);
|
||||||
|
ProjectModel::CollectionNode* collection() const { return m_collection; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class TargetButton : public QPushButton
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TargetButton(QWidget* parent = Q_NULLPTR);
|
||||||
|
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
|
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class FieldSoundMacroStep : public QWidget
|
||||||
|
{
|
||||||
|
friend class CommandWidget;
|
||||||
|
Q_OBJECT
|
||||||
|
FieldProjectNode* m_macroField;
|
||||||
|
FieldSpinBox m_spinBox;
|
||||||
|
TargetButton m_targetButton;
|
||||||
|
SoundMacroEditor* getEditor() const;
|
||||||
|
SoundMacroListing* getListing() const;
|
||||||
|
signals:
|
||||||
|
void valueChanged(int);
|
||||||
|
public slots:
|
||||||
|
void targetPressed();
|
||||||
|
void updateMacroField();
|
||||||
|
public:
|
||||||
|
explicit FieldSoundMacroStep(FieldProjectNode* macroField = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
|
||||||
|
~FieldSoundMacroStep();
|
||||||
|
void setIndex(int index);
|
||||||
|
void cancel();
|
||||||
|
};
|
||||||
|
|
||||||
class CommandWidget : public QWidget
|
class CommandWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -49,18 +89,19 @@ class CommandWidget : public QWidget
|
||||||
int m_index = -1;
|
int m_index = -1;
|
||||||
amuse::SoundMacro::ICmd* m_cmd;
|
amuse::SoundMacro::ICmd* m_cmd;
|
||||||
const amuse::SoundMacro::CmdIntrospection* m_introspection;
|
const amuse::SoundMacro::CmdIntrospection* m_introspection;
|
||||||
|
FieldSoundMacroStep* m_stepField = nullptr;
|
||||||
void setIndex(int index);
|
void setIndex(int index);
|
||||||
SoundMacroListing* getParent() const;
|
SoundMacroListing* getParent() const;
|
||||||
private slots:
|
private slots:
|
||||||
void boolChanged(int);
|
void boolChanged(int);
|
||||||
void numChanged(int);
|
void numChanged(int);
|
||||||
void choiceChanged(int);
|
void nodeChanged(int);
|
||||||
void deleteClicked();
|
void deleteClicked();
|
||||||
private:
|
private:
|
||||||
CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, QWidget* parent = Q_NULLPTR);
|
CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
||||||
public:
|
public:
|
||||||
CommandWidget(amuse::SoundMacro::ICmd* cmd, QWidget* parent = Q_NULLPTR);
|
CommandWidget(amuse::SoundMacro::ICmd* cmd, SoundMacroListing* listing);
|
||||||
CommandWidget(amuse::SoundMacro::CmdOp op, QWidget* parent = Q_NULLPTR);
|
CommandWidget(amuse::SoundMacro::CmdOp op, SoundMacroListing* listing);
|
||||||
void paintEvent(QPaintEvent* event);
|
void paintEvent(QPaintEvent* event);
|
||||||
QString getText() const { return m_titleLabel.text(); }
|
QString getText() const { return m_titleLabel.text(); }
|
||||||
};
|
};
|
||||||
|
@ -113,6 +154,7 @@ public:
|
||||||
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
|
explicit SoundMacroListing(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::SoundMacroNode* node);
|
bool loadData(ProjectModel::SoundMacroNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
ProjectModel::INode* currentNode() const;
|
||||||
void timerEvent(QTimerEvent* event);
|
void timerEvent(QTimerEvent* event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,19 +186,24 @@ class SoundMacroEditor : public EditorWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
friend class SoundMacroCatalogue;
|
friend class SoundMacroCatalogue;
|
||||||
|
friend class FieldSoundMacroStep;
|
||||||
QSplitter* m_splitter;
|
QSplitter* m_splitter;
|
||||||
SoundMacroListing* m_listing;
|
SoundMacroListing* m_listing;
|
||||||
SoundMacroCatalogue* m_catalogue;
|
SoundMacroCatalogue* m_catalogue;
|
||||||
CommandWidget* m_draggedCmd = nullptr;
|
CommandWidget* m_draggedCmd = nullptr;
|
||||||
CatalogueItem* m_draggedItem = nullptr;
|
CatalogueItem* m_draggedItem = nullptr;
|
||||||
|
FieldSoundMacroStep* m_targetField = nullptr;
|
||||||
QPoint m_draggedPt;
|
QPoint m_draggedPt;
|
||||||
int m_dragInsertIdx = -1;
|
int m_dragInsertIdx = -1;
|
||||||
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
void beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt);
|
||||||
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
void beginCatalogueDrag(CatalogueItem* item, const QPoint& eventPt, const QPoint& pt);
|
||||||
|
void beginStepTarget(FieldSoundMacroStep* stepField);
|
||||||
|
void endStepTarget();
|
||||||
public:
|
public:
|
||||||
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
|
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
|
||||||
bool loadData(ProjectModel::SoundMacroNode* node);
|
bool loadData(ProjectModel::SoundMacroNode* node);
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
ProjectModel::INode* currentNode() const;
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent* event);
|
void mousePressEvent(QMouseEvent* event);
|
||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <QStatusBar>
|
#include <QStatusBar>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
class StatusBarFocus;
|
class StatusBarFocus;
|
||||||
|
|
||||||
|
@ -10,15 +11,35 @@ class StatusBarWidget : public QStatusBar
|
||||||
{
|
{
|
||||||
friend class StatusBarFocus;
|
friend class StatusBarFocus;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QLabel* m_normalMessage;
|
QLabel m_normalMessage;
|
||||||
|
QPushButton m_killButton;
|
||||||
|
QLabel m_voiceCount;
|
||||||
|
int m_cachedVoiceCount = -1;
|
||||||
StatusBarFocus* m_curFocus = nullptr;
|
StatusBarFocus* m_curFocus = nullptr;
|
||||||
|
void setKillVisible(bool vis) { m_killButton.setVisible(vis); m_voiceCount.setVisible(vis); }
|
||||||
public:
|
public:
|
||||||
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR) : QStatusBar(parent)
|
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR) : QStatusBar(parent)
|
||||||
{
|
{
|
||||||
m_normalMessage = new QLabel(this);
|
addWidget(&m_normalMessage);
|
||||||
addWidget(m_normalMessage);
|
m_killButton.setIcon(QIcon(QStringLiteral(":/icons/IconKill.svg")));
|
||||||
|
m_killButton.setVisible(false);
|
||||||
|
m_killButton.setToolTip(tr("Immediately kill active voices"));
|
||||||
|
m_voiceCount.setVisible(false);
|
||||||
|
addPermanentWidget(&m_voiceCount);
|
||||||
|
addPermanentWidget(&m_killButton);
|
||||||
}
|
}
|
||||||
void setNormalMessage(const QString& message) { m_normalMessage->setText(message); }
|
void setNormalMessage(const QString& message) { m_normalMessage.setText(message); }
|
||||||
|
void setVoiceCount(int voices)
|
||||||
|
{
|
||||||
|
if (voices != m_cachedVoiceCount)
|
||||||
|
{
|
||||||
|
m_voiceCount.setText(QString::number(voices));
|
||||||
|
m_cachedVoiceCount = voices;
|
||||||
|
setKillVisible(voices != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void connectKillClicked(const QObject* receiver, const char* method)
|
||||||
|
{ connect(&m_killButton, SIGNAL(clicked(bool)), receiver, method); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatusBarFocus : public QObject
|
class StatusBarFocus : public QObject
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
<?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="IconKill.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#353535"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="39.226909"
|
||||||
|
inkscape:cx="9.3355483"
|
||||||
|
inkscape:cy="10.583863"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1452"
|
||||||
|
inkscape:window-height="1061"
|
||||||
|
inkscape:window-x="651"
|
||||||
|
inkscape:window-y="170"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
gridtolerance="10"
|
||||||
|
showguides="false">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid817"
|
||||||
|
empspacing="0"
|
||||||
|
spacingx="0.52916666"
|
||||||
|
spacingy="0.52916666"
|
||||||
|
visible="false" />
|
||||||
|
</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)">
|
||||||
|
<path
|
||||||
|
style="fill:#fffff8;fill-opacity:1;stroke:#000000;stroke-width:0.30217573px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 1.4046674,296.70833 v -1.17708 c 0,0 -1.24116588,-0.58856 -1.24116588,-1.1771 0,-0.58853 0,-2.35417 2.48233178,-2.35417 2.4823318,0 2.4823318,1.76564 2.4823318,2.35417 0,0.58854 -1.2411659,1.1771 -1.2411659,1.1771 v 1.17708 z"
|
||||||
|
id="path842"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:#000006;fill-opacity:1;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.49803922"
|
||||||
|
id="path844"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="1.8520833"
|
||||||
|
sodipodi:cy="293.56039"
|
||||||
|
sodipodi:rx="0.47706053"
|
||||||
|
sodipodi:ry="0.43746531"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="5.866374"
|
||||||
|
d="m 2.3291439,293.56039 a 0.47706053,0.43746531 0 0 1 -0.4274394,0.4351 0.47706053,0.43746531 0 0 1 -0.5163591,-0.34458 0.47706053,0.43746531 0 0 1 0.3200219,-0.50678 0.47706053,0.43746531 0 0 1 0.5829328,0.23916 l -0.4362168,0.1771 z" />
|
||||||
|
<path
|
||||||
|
d="m 3.9161899,293.56039 a 0.47706053,0.43746531 0 0 1 -0.4274394,0.4351 0.47706053,0.43746531 0 0 1 -0.5163591,-0.34458 0.47706053,0.43746531 0 0 1 0.3200219,-0.50678 0.47706053,0.43746531 0 0 1 0.5829328,0.23916 l -0.4362167,0.1771 z"
|
||||||
|
sodipodi:end="5.866374"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:ry="0.43746531"
|
||||||
|
sodipodi:rx="0.47706053"
|
||||||
|
sodipodi:cy="293.56039"
|
||||||
|
sodipodi:cx="3.4391294"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
id="path846"
|
||||||
|
style="fill:#000006;fill-opacity:1;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.49803922" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.29107958px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 2.6458333,295.41249 v 1.28091"
|
||||||
|
id="path848"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.29184496px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 2.0289823,295.41249 v 1.28766"
|
||||||
|
id="path850"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.29107958px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 3.2694292,295.41249 v 1.28091"
|
||||||
|
id="path852"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.3 KiB |
|
@ -0,0 +1,80 @@
|
||||||
|
<?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="IconSoundMacroTarget.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#353535"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="14.029515"
|
||||||
|
inkscape:cx="4.9858624"
|
||||||
|
inkscape:cy="10.149308"
|
||||||
|
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">
|
||||||
|
<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 />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-291.70832)">
|
||||||
|
<ellipse
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#a4a4a4;stroke-width:0.52916664;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path842"
|
||||||
|
cx="2.6458333"
|
||||||
|
cy="294.35416"
|
||||||
|
rx="2.1166666"
|
||||||
|
ry="2.1166699" />
|
||||||
|
<ellipse
|
||||||
|
style="fill:#a4a4a4;fill-opacity:1;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path844"
|
||||||
|
cy="294.35416"
|
||||||
|
cx="2.6458333"
|
||||||
|
rx="1.0583333"
|
||||||
|
ry="1.0583365" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,80 @@
|
||||||
|
<?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="IconSoundMacroTargetDisabled.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#353535"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="14.029515"
|
||||||
|
inkscape:cx="4.9858624"
|
||||||
|
inkscape:cy="10.149308"
|
||||||
|
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">
|
||||||
|
<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)">
|
||||||
|
<ellipse
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#a4a4a4;stroke-width:0.52916664;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.49803922"
|
||||||
|
id="path842"
|
||||||
|
cx="2.6458333"
|
||||||
|
cy="294.35416"
|
||||||
|
rx="2.1166666"
|
||||||
|
ry="2.1166699" />
|
||||||
|
<ellipse
|
||||||
|
style="fill:#a4a4a4;fill-opacity:0.49803922;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path844"
|
||||||
|
cy="294.35416"
|
||||||
|
cx="2.6458333"
|
||||||
|
rx="1.0583333"
|
||||||
|
ry="1.0583365" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -1,6 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="de_DE">
|
<TS version="2.1" language="de_DE">
|
||||||
|
<context>
|
||||||
|
<name>CommandWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../SoundMacroEditor.cpp" line="+163"/>
|
||||||
|
<source>Delete this SoundMacro</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
<message>
|
<message>
|
||||||
|
@ -9,12 +17,12 @@
|
||||||
<translation>Amuse</translation>
|
<translation>Amuse</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+169"/>
|
<location line="+280"/>
|
||||||
<source>&File</source>
|
<source>&File</source>
|
||||||
<translation>&Datei</translation>
|
<translation>&Datei</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+9"/>
|
<location line="+21"/>
|
||||||
<source>P&roject</source>
|
<source>P&roject</source>
|
||||||
<translation>Projekt</translation>
|
<translation>Projekt</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -52,7 +60,12 @@
|
||||||
<translation type="vanished">& Wiederholen</translation>
|
<translation type="vanished">& Wiederholen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+8"/>
|
<location line="-65"/>
|
||||||
|
<source>Recent &Projects</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location line="+73"/>
|
||||||
<source>&Cut</source>
|
<source>&Cut</source>
|
||||||
<translation>&Schnitt</translation>
|
<translation>&Schnitt</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -147,12 +160,22 @@
|
||||||
<translation>Neu und Kurve</translation>
|
<translation>Neu und Kurve</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../MainWindow.cpp" line="+44"/>
|
<location line="+8"/>
|
||||||
|
<source>&Save Project</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location line="+8"/>
|
||||||
|
<source>&Revert Project</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../MainWindow.cpp" line="+70"/>
|
||||||
<source>Quit</source>
|
<source>Quit</source>
|
||||||
<translation>Verlassen</translation>
|
<translation>Verlassen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+106"/>
|
<location line="+134"/>
|
||||||
<source>The directory at '%1' must exist for the Amuse editor.</source>
|
<source>The directory at '%1' must exist for the Amuse editor.</source>
|
||||||
<translation>Das Verzeichnis unter '% 1' muss für den Amuse-Editor vorhanden sein.</translation>
|
<translation>Das Verzeichnis unter '% 1' muss für den Amuse-Editor vorhanden sein.</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -177,7 +200,7 @@
|
||||||
<translation>Es konnte nicht in das Verzeichnis geschrieben werden</translation>
|
<translation>Es konnte nicht in das Verzeichnis geschrieben werden</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+38"/>
|
<location line="+44"/>
|
||||||
<source>No Audio Devices Found</source>
|
<source>No Audio Devices Found</source>
|
||||||
<translation>Keine Audiogeräte gefunden</translation>
|
<translation>Keine Audiogeräte gefunden</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -187,22 +210,44 @@
|
||||||
<translation>Keine MIDI-Geräte gefunden</translation>
|
<translation>Keine MIDI-Geräte gefunden</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+125"/>
|
<location line="+193"/>
|
||||||
<source>New Project</source>
|
<source>New Project</source>
|
||||||
<translation>Neues Projekt</translation>
|
<translation>Neues Projekt</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+14"/>
|
<location line="+60"/>
|
||||||
<source>Open Project</source>
|
<source>Open Project</source>
|
||||||
<translation>Offenes Projekt</translation>
|
<translation>Offenes Projekt</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+7"/>
|
<location line="-37"/>
|
||||||
<source>The directory at '%1' does not exist.</source>
|
<source>The directory at '%1' does not exist.</source>
|
||||||
<translation>Das Verzeichnis '% 1' existiert nicht.</translation>
|
<translation>Das Verzeichnis '% 1' existiert nicht.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+1"/>
|
<location line="-432"/>
|
||||||
|
<source>Clear Recent Projects</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location line="+134"/>
|
||||||
|
<location line="+292"/>
|
||||||
|
<source>The directory at '%1' must not be empty.</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location line="-291"/>
|
||||||
|
<location line="+292"/>
|
||||||
|
<source>Directory empty</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location line="-181"/>
|
||||||
|
<source>SUSTAIN</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location line="+187"/>
|
||||||
<source>Bad Directory</source>
|
<source>Bad Directory</source>
|
||||||
<translation>Schlechtes Verzeichnis</translation>
|
<translation>Schlechtes Verzeichnis</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -213,18 +258,18 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+0"/>
|
<location line="+0"/>
|
||||||
<location line="+83"/>
|
<location line="+127"/>
|
||||||
<location line="+45"/>
|
<location line="+45"/>
|
||||||
<source>Scanning Project</source>
|
<source>Scanning Project</source>
|
||||||
<translation>Projekt scannen</translation>
|
<translation>Projekt scannen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="-116"/>
|
<location line="-160"/>
|
||||||
<source>Opening %1</source>
|
<source>Opening %1</source>
|
||||||
<translation>Eröffnung% 1</translation>
|
<translation>Eröffnung% 1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+10"/>
|
<location line="+54"/>
|
||||||
<source>Import Project</source>
|
<source>Import Project</source>
|
||||||
<translation>Projekt importieren</translation>
|
<translation>Projekt importieren</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -296,30 +341,46 @@
|
||||||
<translation>Importieren von% 1</translation>
|
<translation>Importieren von% 1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>ModulationSlider</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../KeyboardWidget.cpp" line="+267"/>
|
||||||
|
<source>Modulation: %1</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PitchSlider</name>
|
||||||
|
<message>
|
||||||
|
<location line="+9"/>
|
||||||
|
<source>Pitch: %1</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ProjectModel</name>
|
<name>ProjectModel</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../ProjectModel.cpp" line="+126"/>
|
<location filename="../ProjectModel.cpp" line="+210"/>
|
||||||
<source>Sound Macros</source>
|
<source>Sound Macros</source>
|
||||||
<translation>Sound-Makros</translation>
|
<translation>Sound-Makros</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+21"/>
|
<location line="+19"/>
|
||||||
<source>ADSRs</source>
|
<source>ADSRs</source>
|
||||||
<translation>ADSRs</translation>
|
<translation>ADSRs</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+12"/>
|
<location line="+11"/>
|
||||||
<source>Curves</source>
|
<source>Curves</source>
|
||||||
<translation>Kurven</translation>
|
<translation>Kurven</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+13"/>
|
<location line="+12"/>
|
||||||
<source>Keymaps</source>
|
<source>Keymaps</source>
|
||||||
<translation>Schlüsselkarten</translation>
|
<translation>Schlüsselkarten</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+8"/>
|
<location line="+7"/>
|
||||||
<source>Layers</source>
|
<source>Layers</source>
|
||||||
<translation>Lagen</translation>
|
<translation>Lagen</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -340,12 +401,12 @@
|
||||||
<context>
|
<context>
|
||||||
<name>QUndoStack</name>
|
<name>QUndoStack</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../SoundMacroEditor.cpp" line="+163"/>
|
<location filename="../SoundMacroEditor.cpp" line="+157"/>
|
||||||
<source>Change %1</source>
|
<source>Change %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+308"/>
|
<location line="+322"/>
|
||||||
<source>Reorder %1</source>
|
<source>Reorder %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -355,7 +416,8 @@
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location line="+55"/>
|
<location filename="../ProjectModel.cpp" line="+151"/>
|
||||||
|
<location filename="../SoundMacroEditor.cpp" line="+55"/>
|
||||||
<source>Delete %1</source>
|
<source>Delete %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -363,7 +425,7 @@
|
||||||
<context>
|
<context>
|
||||||
<name>SoundMacroCatalogue</name>
|
<name>SoundMacroCatalogue</name>
|
||||||
<message>
|
<message>
|
||||||
<location line="+111"/>
|
<location filename="../SoundMacroEditor.cpp" line="+118"/>
|
||||||
<source>Control</source>
|
<source>Control</source>
|
||||||
<translation>Steuerung</translation>
|
<translation>Steuerung</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -433,4 +495,28 @@
|
||||||
<translation>Befehle zum Steuern der Lautstärke der Stimme</translation>
|
<translation>Befehle zum Steuern der Lautstärke der Stimme</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>StatusBarWidget</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../StatusBarWidget.hpp" line="+26"/>
|
||||||
|
<source>Immediately kill active voices</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>TargetButton</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../SoundMacroEditor.cpp" line="-939"/>
|
||||||
|
<source>Set step with target click</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>VelocitySlider</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../KeyboardWidget.cpp" line="-18"/>
|
||||||
|
<source>Velocity: %1</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<file>IconCurve.svg</file>
|
<file>IconCurve.svg</file>
|
||||||
<file>IconNewCurve.svg</file>
|
<file>IconNewCurve.svg</file>
|
||||||
<file>IconSoundMacroDelete.svg</file>
|
<file>IconSoundMacroDelete.svg</file>
|
||||||
|
<file>IconSoundMacroTarget.svg</file>
|
||||||
|
<file>IconSoundMacroTargetDisabled.svg</file>
|
||||||
|
<file>IconKill.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/bg">
|
<qresource prefix="/bg">
|
||||||
<file>FaceGrey.svg</file>
|
<file>FaceGrey.svg</file>
|
||||||
|
|
|
@ -36,20 +36,23 @@ public:
|
||||||
AudioGroupProject& getProj() { return m_proj; }
|
AudioGroupProject& getProj() { return m_proj; }
|
||||||
AudioGroupPool& getPool() { return m_pool; }
|
AudioGroupPool& getPool() { return m_pool; }
|
||||||
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
|
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
|
||||||
|
|
||||||
|
virtual void setIdDatabases() const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioGroupDatabase : public AudioGroup
|
class AudioGroupDatabase final : public AudioGroup
|
||||||
{
|
{
|
||||||
amuse::NameDB m_soundMacroDb;
|
NameDB m_soundMacroDb;
|
||||||
amuse::NameDB m_sampleDb;
|
NameDB m_sampleDb;
|
||||||
amuse::NameDB m_tableDb;
|
NameDB m_tableDb;
|
||||||
amuse::NameDB m_keymapDb;
|
NameDB m_keymapDb;
|
||||||
amuse::NameDB m_layersDb;
|
NameDB m_layersDb;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AudioGroupDatabase() = default;
|
AudioGroupDatabase() = default;
|
||||||
explicit AudioGroupDatabase(const AudioGroupData& data)
|
explicit AudioGroupDatabase(const AudioGroupData& data)
|
||||||
{
|
{
|
||||||
|
setIdDatabases();
|
||||||
assign(data);
|
assign(data);
|
||||||
}
|
}
|
||||||
explicit AudioGroupDatabase(SystemStringView groupPath)
|
explicit AudioGroupDatabase(SystemStringView groupPath)
|
||||||
|
@ -58,28 +61,28 @@ public:
|
||||||
assign(groupPath);
|
assign(groupPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setIdDatabases()
|
void setIdDatabases() const
|
||||||
{
|
{
|
||||||
amuse::SoundMacroId::CurNameDB = &m_soundMacroDb;
|
SoundMacroId::CurNameDB = const_cast<NameDB*>(&m_soundMacroDb);
|
||||||
amuse::SampleId::CurNameDB = &m_sampleDb;
|
SampleId::CurNameDB = const_cast<NameDB*>(&m_sampleDb);
|
||||||
amuse::TableId::CurNameDB = &m_tableDb;
|
TableId::CurNameDB = const_cast<NameDB*>(&m_tableDb);
|
||||||
amuse::KeymapId::CurNameDB = &m_keymapDb;
|
KeymapId::CurNameDB = const_cast<NameDB*>(&m_keymapDb);
|
||||||
amuse::LayersId::CurNameDB = &m_layersDb;
|
LayersId::CurNameDB = const_cast<NameDB*>(&m_layersDb);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProjectDatabase
|
class ProjectDatabase
|
||||||
{
|
{
|
||||||
amuse::NameDB m_songDb;
|
NameDB m_songDb;
|
||||||
amuse::NameDB m_sfxDb;
|
NameDB m_sfxDb;
|
||||||
amuse::NameDB m_groupDb;
|
NameDB m_groupDb;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setIdDatabases()
|
void setIdDatabases() const
|
||||||
{
|
{
|
||||||
amuse::SongId::CurNameDB = &m_songDb;
|
SongId::CurNameDB = const_cast<NameDB*>(&m_songDb);
|
||||||
amuse::SFXId::CurNameDB = &m_sfxDb;
|
SFXId::CurNameDB = const_cast<NameDB*>(&m_sfxDb);
|
||||||
amuse::GroupId::CurNameDB = &m_groupDb;
|
GroupId::CurNameDB = const_cast<NameDB*>(&m_groupDb);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,7 @@ struct SoundMacro
|
||||||
Int32,
|
Int32,
|
||||||
UInt32,
|
UInt32,
|
||||||
SoundMacroId,
|
SoundMacroId,
|
||||||
|
SoundMacroStep,
|
||||||
TableId,
|
TableId,
|
||||||
SampleId,
|
SampleId,
|
||||||
Choice
|
Choice
|
||||||
|
@ -200,7 +201,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Value<atInt8> key;
|
Value<atInt8> key;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::SplitKey; }
|
CmdOp Isa() const { return CmdOp::SplitKey; }
|
||||||
};
|
};
|
||||||
|
@ -211,7 +212,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Value<atInt8> velocity;
|
Value<atInt8> velocity;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::SplitVel; }
|
CmdOp Isa() const { return CmdOp::SplitVel; }
|
||||||
};
|
};
|
||||||
|
@ -237,7 +238,7 @@ struct SoundMacro
|
||||||
Value<bool> keyOff;
|
Value<bool> keyOff;
|
||||||
Value<bool> random;
|
Value<bool> random;
|
||||||
Value<bool> sampleEnd;
|
Value<bool> sampleEnd;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
Value<atUint16> times;
|
Value<atUint16> times;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::Loop; }
|
CmdOp Isa() const { return CmdOp::Loop; }
|
||||||
|
@ -249,7 +250,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Seek<1, athena::SeekOrigin::Current> dummy;
|
Seek<1, athena::SeekOrigin::Current> dummy;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::Goto; }
|
CmdOp Isa() const { return CmdOp::Goto; }
|
||||||
};
|
};
|
||||||
|
@ -274,7 +275,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Value<atInt8> addNote;
|
Value<atInt8> addNote;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
Value<atUint8> priority;
|
Value<atUint8> priority;
|
||||||
Value<atUint8> maxVoices;
|
Value<atUint8> maxVoices;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
|
@ -297,7 +298,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Value<atInt8> modValue;
|
Value<atInt8> modValue;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::SplitMod; }
|
CmdOp Isa() const { return CmdOp::SplitMod; }
|
||||||
};
|
};
|
||||||
|
@ -398,7 +399,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Value<atUint8> rnd;
|
Value<atUint8> rnd;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::SplitRnd; }
|
CmdOp Isa() const { return CmdOp::SplitRnd; }
|
||||||
};
|
};
|
||||||
|
@ -623,7 +624,7 @@ struct SoundMacro
|
||||||
static const CmdIntrospection Introspective;
|
static const CmdIntrospection Introspective;
|
||||||
Seek<1, athena::SeekOrigin::Current> seek;
|
Seek<1, athena::SeekOrigin::Current> seek;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::GoSub; }
|
CmdOp Isa() const { return CmdOp::GoSub; }
|
||||||
};
|
};
|
||||||
|
@ -640,7 +641,7 @@ struct SoundMacro
|
||||||
};
|
};
|
||||||
Value<EventType> event;
|
Value<EventType> event;
|
||||||
SoundMacroIdDNA<athena::Little> macro;
|
SoundMacroIdDNA<athena::Little> macro;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::TrapEvent; }
|
CmdOp Isa() const { return CmdOp::TrapEvent; }
|
||||||
};
|
};
|
||||||
|
@ -1098,7 +1099,7 @@ struct SoundMacro
|
||||||
Value<bool> varCtrlB;
|
Value<bool> varCtrlB;
|
||||||
Value<atInt8> b;
|
Value<atInt8> b;
|
||||||
Value<bool> notEq;
|
Value<bool> notEq;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::IfEqual; }
|
CmdOp Isa() const { return CmdOp::IfEqual; }
|
||||||
};
|
};
|
||||||
|
@ -1112,7 +1113,7 @@ struct SoundMacro
|
||||||
Value<bool> varCtrlB;
|
Value<bool> varCtrlB;
|
||||||
Value<atInt8> b;
|
Value<atInt8> b;
|
||||||
Value<bool> notLt;
|
Value<bool> notLt;
|
||||||
Value<atUint16> macroStep;
|
SoundMacroStepDNA<athena::Little> macroStep;
|
||||||
bool Do(SoundMacroState& st, Voice& vox) const;
|
bool Do(SoundMacroState& st, Voice& vox) const;
|
||||||
CmdOp Isa() const { return CmdOp::IfLess; }
|
CmdOp Isa() const { return CmdOp::IfLess; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,6 +71,7 @@ public:
|
||||||
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
|
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
|
||||||
{
|
{
|
||||||
friend class BooBackendVoiceAllocator;
|
friend class BooBackendVoiceAllocator;
|
||||||
|
protected:
|
||||||
Engine& m_engine;
|
Engine& m_engine;
|
||||||
std::unique_ptr<boo::IMIDIIn> m_midiIn;
|
std::unique_ptr<boo::IMIDIIn> m_midiIn;
|
||||||
boo::MIDIDecoder m_decoder;
|
boo::MIDIDecoder m_decoder;
|
||||||
|
@ -119,6 +120,7 @@ public:
|
||||||
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback
|
class BooBackendVoiceAllocator : public IBackendVoiceAllocator, public boo::IAudioVoiceEngineCallback
|
||||||
{
|
{
|
||||||
friend class BooBackendMIDIReader;
|
friend class BooBackendMIDIReader;
|
||||||
|
protected:
|
||||||
boo::IAudioVoiceEngine& m_booEngine;
|
boo::IAudioVoiceEngine& m_booEngine;
|
||||||
Engine* m_cbInterface = nullptr;
|
Engine* m_cbInterface = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,26 @@ PageObjectIdDNA : BigDNA
|
||||||
operator ObjectId() const { return id; }
|
operator ObjectId() const { return id; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SoundMacroStep
|
||||||
|
{
|
||||||
|
uint16_t step = 0;
|
||||||
|
operator uint16_t() const { return step; }
|
||||||
|
SoundMacroStep() = default;
|
||||||
|
SoundMacroStep(uint16_t idIn) : step(idIn) {}
|
||||||
|
SoundMacroStep& operator=(uint16_t idIn) { step = idIn; return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <athena::Endian DNAEn>
|
||||||
|
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
|
||||||
|
SoundMacroStepDNA : BigDNA
|
||||||
|
{
|
||||||
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
|
SoundMacroStep step;
|
||||||
|
SoundMacroStepDNA() = default;
|
||||||
|
SoundMacroStepDNA(SoundMacroStep idIn) : step(idIn) {}
|
||||||
|
operator SoundMacroStep() const { return step; }
|
||||||
|
};
|
||||||
|
|
||||||
struct LittleUInt24 : LittleDNA
|
struct LittleUInt24 : LittleDNA
|
||||||
{
|
{
|
||||||
AT_DECL_EXPLICIT_DNA_YAML
|
AT_DECL_EXPLICIT_DNA_YAML
|
||||||
|
|
|
@ -96,6 +96,15 @@ public:
|
||||||
return fxStart(sfxId, vol, pan, m_defaultStudio);
|
return fxStart(sfxId, vol, pan, m_defaultStudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Start SoundMacro node playing directly (for editor use) */
|
||||||
|
std::shared_ptr<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
|
||||||
|
uint8_t vel, uint8_t mod, std::weak_ptr<Studio> smx);
|
||||||
|
std::shared_ptr<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
|
||||||
|
uint8_t vel, uint8_t mod)
|
||||||
|
{
|
||||||
|
return macroStart(group, id, key, vel, mod, m_defaultStudio);
|
||||||
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||||
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||||
int sfxId, float minVol, float maxVol, bool doppler,
|
int sfxId, float minVol, float maxVol, bool doppler,
|
||||||
|
@ -136,6 +145,9 @@ public:
|
||||||
/** Obtain next random number from engine's PRNG */
|
/** Obtain next random number from engine's PRNG */
|
||||||
uint32_t nextRandom() { return m_random(); }
|
uint32_t nextRandom() { return m_random(); }
|
||||||
|
|
||||||
|
/** Obtain list of active voices */
|
||||||
|
std::list<std::shared_ptr<Voice>>& getActiveVoices() { return m_activeVoices; }
|
||||||
|
|
||||||
/** Obtain list of active sequencers */
|
/** Obtain list of active sequencers */
|
||||||
std::list<std::shared_ptr<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
|
std::list<std::shared_ptr<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ void AudioGroup::assign(const AudioGroupData& data)
|
||||||
void AudioGroup::assign(SystemStringView groupPath)
|
void AudioGroup::assign(SystemStringView groupPath)
|
||||||
{
|
{
|
||||||
/* Reverse order when loading intermediates */
|
/* Reverse order when loading intermediates */
|
||||||
|
m_groupPath = groupPath;
|
||||||
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
|
||||||
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
|
||||||
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
|
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
|
||||||
|
@ -32,6 +33,7 @@ const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupS
|
||||||
{
|
{
|
||||||
if (sample->m_looseData)
|
if (sample->m_looseData)
|
||||||
{
|
{
|
||||||
|
setIdDatabases();
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
SystemString basePath = m_groupPath + _S('/') +
|
SystemString basePath = m_groupPath + _S('/') +
|
||||||
athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
|
athena::utility::utf8ToWide(SampleId::CurNameDB->resolveNameFromId(sfxId));
|
||||||
|
|
|
@ -61,6 +61,7 @@ struct MakeDefaultCmdOp
|
||||||
AccessField<uint32_t>(ret.get(), field) = uint32_t(field.m_default);
|
AccessField<uint32_t>(ret.get(), field) = uint32_t(field.m_default);
|
||||||
break;
|
break;
|
||||||
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId:
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId:
|
||||||
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroStep:
|
||||||
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
|
||||||
case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId:
|
case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId:
|
||||||
AccessField<SoundMacroIdDNA<athena::Little>>(ret.get(), field).id = uint16_t(field.m_default);
|
AccessField<SoundMacroIdDNA<athena::Little>>(ret.get(), field).id = uint16_t(field.m_default);
|
||||||
|
@ -153,7 +154,8 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
|
||||||
objHead.read(r);
|
objHead.read(r);
|
||||||
KeymapDNA<DNAE> kmData;
|
KeymapDNA<DNAE> kmData;
|
||||||
kmData.read(r);
|
kmData.read(r);
|
||||||
*ret.m_keymaps[objHead.objectId.id] = kmData;
|
auto& km = ret.m_keymaps[objHead.objectId.id];
|
||||||
|
km = std::make_shared<Keymap>(kmData);
|
||||||
r.seek(startPos + objHead.size, athena::Begin);
|
r.seek(startPos + objHead.size, athena::Begin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,15 +168,16 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
|
||||||
ObjectHeader<DNAE> objHead;
|
ObjectHeader<DNAE> objHead;
|
||||||
atInt64 startPos = r.position();
|
atInt64 startPos = r.position();
|
||||||
objHead.read(r);
|
objHead.read(r);
|
||||||
std::vector<LayerMapping>& lm = *ret.m_layers[objHead.objectId.id];
|
auto& lm = ret.m_layers[objHead.objectId.id];
|
||||||
|
lm = std::make_shared<std::vector<LayerMapping>>();
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
|
||||||
lm.reserve(count);
|
lm->reserve(count);
|
||||||
for (uint32_t i = 0; i < count; ++i)
|
for (uint32_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
LayerMappingDNA<DNAE> lmData;
|
LayerMappingDNA<DNAE> lmData;
|
||||||
lmData.read(r);
|
lmData.read(r);
|
||||||
lm.push_back(lmData);
|
lm->push_back(lmData);
|
||||||
}
|
}
|
||||||
r.seek(startPos + objHead.size, athena::Begin);
|
r.seek(startPos + objHead.size, athena::Begin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,29 @@ static bool AtEnd16(athena::io::IStreamReader& r)
|
||||||
return v == 0xffff;
|
return v == 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp)
|
||||||
|
{
|
||||||
|
uint16_t id;
|
||||||
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(id), DNAE>({}, id, r);
|
||||||
|
if ((id & 0x8000) == 0x8000)
|
||||||
|
{
|
||||||
|
uint16_t endId;
|
||||||
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(endId), DNAE>({}, endId, r);
|
||||||
|
for (uint16_t i = uint16_t(id & 0x7fff); i <= uint16_t(endId & 0x7fff); ++i)
|
||||||
|
{
|
||||||
|
ObjectId useId = i;
|
||||||
|
if (tp == NameDB::Type::Layer)
|
||||||
|
useId.id |= 0x8000;
|
||||||
|
db->registerPair(NameDB::generateName(useId, tp), useId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db->registerPair(NameDB::generateName(id, tp), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
|
AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
|
||||||
{
|
{
|
||||||
while (!AtEnd32(r))
|
while (!AtEnd32(r))
|
||||||
|
@ -28,6 +51,33 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
|
||||||
GroupHeader<athena::Big> header;
|
GroupHeader<athena::Big> header;
|
||||||
header.read(r);
|
header.read(r);
|
||||||
|
|
||||||
|
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
|
||||||
|
|
||||||
|
/* Sound Macros */
|
||||||
|
r.seek(header.soundMacroIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<athena::Big>(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro);
|
||||||
|
|
||||||
|
/* Samples */
|
||||||
|
r.seek(header.samplIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<athena::Big>(SampleId::CurNameDB, r, NameDB::Type::Sample);
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
r.seek(header.tableIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<athena::Big>(TableId::CurNameDB, r, NameDB::Type::Table);
|
||||||
|
|
||||||
|
/* Keymaps */
|
||||||
|
r.seek(header.keymapIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<athena::Big>(KeymapId::CurNameDB, r, NameDB::Type::Keymap);
|
||||||
|
|
||||||
|
/* Layers */
|
||||||
|
r.seek(header.layerIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<athena::Big>(LayersId::CurNameDB, r, NameDB::Type::Layer);
|
||||||
|
|
||||||
if (header.type == GroupType::Song)
|
if (header.type == GroupType::Song)
|
||||||
{
|
{
|
||||||
auto& idx = m_songGroups[header.groupId];
|
auto& idx = m_songGroups[header.groupId];
|
||||||
|
@ -60,6 +110,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
|
||||||
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
||||||
for (int i = 0; i < 16 ; ++i)
|
for (int i = 0; i < 16 ; ++i)
|
||||||
setup[i].read(r);
|
setup[i].read(r);
|
||||||
|
SongId::CurNameDB->registerPair(NameDB::generateName(songId, NameDB::Type::Song), songId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (header.type == GroupType::SFX)
|
else if (header.type == GroupType::SFX)
|
||||||
|
@ -77,6 +128,8 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
|
||||||
SFXGroupIndex::SFXEntryDNA<athena::Big> entry;
|
SFXGroupIndex::SFXEntryDNA<athena::Big> entry;
|
||||||
entry.read(r);
|
entry.read(r);
|
||||||
idx->m_sfxEntries[entry.sfxId.id] = entry;
|
idx->m_sfxEntries[entry.sfxId.id] = entry;
|
||||||
|
SFXId::CurNameDB->registerPair(
|
||||||
|
NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +149,33 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade
|
||||||
GroupHeader<DNAE> header;
|
GroupHeader<DNAE> header;
|
||||||
header.read(r);
|
header.read(r);
|
||||||
|
|
||||||
|
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
|
||||||
|
|
||||||
|
/* Sound Macros */
|
||||||
|
r.seek(subDataOff + header.soundMacroIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<DNAE>(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro);
|
||||||
|
|
||||||
|
/* Samples */
|
||||||
|
r.seek(subDataOff + header.samplIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<DNAE>(SampleId::CurNameDB, r, NameDB::Type::Sample);
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
r.seek(subDataOff + header.tableIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<DNAE>(TableId::CurNameDB, r, NameDB::Type::Table);
|
||||||
|
|
||||||
|
/* Keymaps */
|
||||||
|
r.seek(subDataOff + header.keymapIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<DNAE>(KeymapId::CurNameDB, r, NameDB::Type::Keymap);
|
||||||
|
|
||||||
|
/* Layers */
|
||||||
|
r.seek(subDataOff + header.layerIdsOff, athena::Begin);
|
||||||
|
while (!AtEnd16(r))
|
||||||
|
ReadRangedObjectIds<DNAE>(LayersId::CurNameDB, r, NameDB::Type::Layer);
|
||||||
|
|
||||||
if (header.type == GroupType::Song)
|
if (header.type == GroupType::Song)
|
||||||
{
|
{
|
||||||
auto& idx = ret.m_songGroups[header.groupId];
|
auto& idx = ret.m_songGroups[header.groupId];
|
||||||
|
@ -131,6 +211,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade
|
||||||
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
||||||
for (int i = 0; i < 16 ; ++i)
|
for (int i = 0; i < 16 ; ++i)
|
||||||
setup[i].read(r);
|
setup[i].read(r);
|
||||||
|
SongId::CurNameDB->registerPair(NameDB::generateName(songId, NameDB::Type::Song), songId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -167,6 +248,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade
|
||||||
ent.read(r);
|
ent.read(r);
|
||||||
setup[i] = ent;
|
setup[i] = ent;
|
||||||
}
|
}
|
||||||
|
SongId::CurNameDB->registerPair(NameDB::generateName(songId, NameDB::Type::Song), songId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,6 +269,8 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade
|
||||||
entry.read(r);
|
entry.read(r);
|
||||||
r.seek(2, athena::Current);
|
r.seek(2, athena::Current);
|
||||||
idx->m_sfxEntries[entry.sfxId.id] = entry;
|
idx->m_sfxEntries[entry.sfxId.id] = entry;
|
||||||
|
SFXId::CurNameDB->registerPair(
|
||||||
|
NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,29 +407,6 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <athena::Endian DNAE>
|
|
||||||
static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp)
|
|
||||||
{
|
|
||||||
uint16_t id;
|
|
||||||
athena::io::Read<athena::io::PropType::None>::Do<decltype(id), DNAE>({}, id, r);
|
|
||||||
if ((id & 0x8000) == 0x8000)
|
|
||||||
{
|
|
||||||
uint16_t endId;
|
|
||||||
athena::io::Read<athena::io::PropType::None>::Do<decltype(endId), DNAE>({}, endId, r);
|
|
||||||
for (uint16_t i = uint16_t(id & 0x7fff); i <= uint16_t(endId & 0x7fff); ++i)
|
|
||||||
{
|
|
||||||
ObjectId useId = i;
|
|
||||||
if (tp == NameDB::Type::Layer)
|
|
||||||
useId.id |= 0x8000;
|
|
||||||
db->registerPair(NameDB::generateName(useId, tp), useId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
db->registerPair(NameDB::generateName(id, tp), id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag)
|
void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag)
|
||||||
{
|
{
|
||||||
while (!AtEnd32(r))
|
while (!AtEnd32(r))
|
||||||
|
|
|
@ -45,6 +45,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
|
||||||
EntryDNA<athena::Big> ent;
|
EntryDNA<athena::Big> ent;
|
||||||
ent.read(r);
|
ent.read(r);
|
||||||
m_entries[ent.m_sfxId] = ent;
|
m_entries[ent.m_sfxId] = ent;
|
||||||
|
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& p : m_entries)
|
for (auto& p : m_entries)
|
||||||
|
@ -68,6 +69,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
|
||||||
MusyX1AbsSdirEntry<athena::Big> ent;
|
MusyX1AbsSdirEntry<athena::Big> ent;
|
||||||
ent.read(r);
|
ent.read(r);
|
||||||
m_entries[ent.m_sfxId] = ent;
|
m_entries[ent.m_sfxId] = ent;
|
||||||
|
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -77,6 +79,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
|
||||||
MusyX1SdirEntry<athena::Big> ent;
|
MusyX1SdirEntry<athena::Big> ent;
|
||||||
ent.read(r);
|
ent.read(r);
|
||||||
m_entries[ent.m_sfxId] = ent;
|
m_entries[ent.m_sfxId] = ent;
|
||||||
|
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +101,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
|
||||||
Entry& store = m_entries[ent.m_sfxId];
|
Entry& store = m_entries[ent.m_sfxId];
|
||||||
store = ent;
|
store = ent;
|
||||||
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
|
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
|
||||||
|
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -109,6 +113,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
|
||||||
Entry& store = m_entries[ent.m_sfxId];
|
Entry& store = m_entries[ent.m_sfxId];
|
||||||
store = ent;
|
store = ent;
|
||||||
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
|
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
|
||||||
|
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,64 @@ const char* PageObjectIdDNA<DNAE>::DNAType()
|
||||||
template struct PageObjectIdDNA<athena::Big>;
|
template struct PageObjectIdDNA<athena::Big>;
|
||||||
template struct PageObjectIdDNA<athena::Little>;
|
template struct PageObjectIdDNA<athena::Little>;
|
||||||
|
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
|
||||||
|
{
|
||||||
|
step = reader.readUint16Little();
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
|
||||||
|
{
|
||||||
|
writer.writeUint16Little(step);
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
|
||||||
|
{
|
||||||
|
sz += 2;
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
||||||
|
{
|
||||||
|
step = reader.readUint16(nullptr);
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
||||||
|
{
|
||||||
|
writer.writeUint16(nullptr, step);
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
|
||||||
|
{
|
||||||
|
step = reader.readUint16Big();
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
|
||||||
|
{
|
||||||
|
writer.writeUint16Big(step);
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
|
||||||
|
{
|
||||||
|
sz += 2;
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
|
||||||
|
{
|
||||||
|
step = reader.readUint16(nullptr);
|
||||||
|
}
|
||||||
|
template<> template<>
|
||||||
|
void SoundMacroStepDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
|
||||||
|
{
|
||||||
|
writer.writeUint16(nullptr, step);
|
||||||
|
}
|
||||||
|
template <athena::Endian DNAE>
|
||||||
|
const char* SoundMacroStepDNA<DNAE>::DNAType()
|
||||||
|
{
|
||||||
|
return "amuse::SoundMacroStepDNA";
|
||||||
|
}
|
||||||
|
template struct SoundMacroStepDNA<athena::Big>;
|
||||||
|
template struct SoundMacroStepDNA<athena::Little>;
|
||||||
|
|
||||||
ObjectId NameDB::generateId(Type tp) const
|
ObjectId NameDB::generateId(Type tp) const
|
||||||
{
|
{
|
||||||
uint16_t maxMatch = uint16_t(tp == Type::Layer ? 0x8000 : 0);
|
uint16_t maxMatch = uint16_t(tp == Type::Layer ? 0x8000 : 0);
|
||||||
|
|
|
@ -280,12 +280,12 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::wea
|
||||||
{
|
{
|
||||||
auto search = m_sfxLookup.find(sfxId);
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
if (search == m_sfxLookup.end())
|
if (search == m_sfxLookup.end())
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
AudioGroup* grp = std::get<0>(search->second);
|
AudioGroup* grp = std::get<0>(search->second);
|
||||||
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
std::list<std::shared_ptr<Voice>>::iterator ret =
|
std::list<std::shared_ptr<Voice>>::iterator ret =
|
||||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
|
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
|
||||||
|
@ -301,6 +301,25 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::wea
|
||||||
return *ret;
|
return *ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Start SoundMacro node playing directly (for editor use) */
|
||||||
|
std::shared_ptr<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel,
|
||||||
|
uint8_t mod, std::weak_ptr<Studio> smx)
|
||||||
|
{
|
||||||
|
if (!group)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::list<std::shared_ptr<Voice>>::iterator ret =
|
||||||
|
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
|
||||||
|
|
||||||
|
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod))
|
||||||
|
{
|
||||||
|
_destroyVoice(ret);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return *ret;
|
||||||
|
}
|
||||||
|
|
||||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||||
std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||||
int sfxId, float minVol, float maxVol, bool doppler,
|
int sfxId, float minVol, float maxVol, bool doppler,
|
||||||
|
@ -308,12 +327,12 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
|
||||||
{
|
{
|
||||||
auto search = m_sfxLookup.find(sfxId);
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
if (search == m_sfxLookup.end())
|
if (search == m_sfxLookup.end())
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
AudioGroup* grp = std::get<0>(search->second);
|
AudioGroup* grp = std::get<0>(search->second);
|
||||||
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
const SFXGroupIndex::SFXEntry* entry = std::get<2>(search->second);
|
||||||
if (!grp)
|
if (!grp)
|
||||||
return nullptr;
|
return {};
|
||||||
|
|
||||||
std::list<std::shared_ptr<Voice>>::iterator vox =
|
std::list<std::shared_ptr<Voice>>::iterator vox =
|
||||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
||||||
|
|
|
@ -169,6 +169,9 @@ template <>
|
||||||
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<SoundMacroIdDNA<athena::Little>>()
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<SoundMacroIdDNA<athena::Little>>()
|
||||||
{ return SoundMacro::CmdIntrospection::Field::Type::SoundMacroId; }
|
{ return SoundMacro::CmdIntrospection::Field::Type::SoundMacroId; }
|
||||||
template <>
|
template <>
|
||||||
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<SoundMacroStepDNA<athena::Little>>()
|
||||||
|
{ return SoundMacro::CmdIntrospection::Field::Type::SoundMacroStep; }
|
||||||
|
template <>
|
||||||
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<TableIdDNA<athena::Little>>()
|
constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType<TableIdDNA<athena::Little>>()
|
||||||
{ return SoundMacro::CmdIntrospection::Field::Type::TableId; }
|
{ return SoundMacro::CmdIntrospection::Field::Type::TableId; }
|
||||||
template <>
|
template <>
|
||||||
|
@ -214,12 +217,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSplitKey::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitKey, macro),
|
FIELD_HEAD(SoundMacro::CmdSplitKey, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitKey, macroStep),
|
FIELD_HEAD(SoundMacro::CmdSplitKey, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,9 +233,9 @@ bool SoundMacro::CmdSplitKey::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
/* Do Branch */
|
/* Do Branch */
|
||||||
if (macro.id == std::get<0>(st.m_pc.back()))
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
else
|
else
|
||||||
vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -251,12 +254,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSplitVel::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitVel, macro),
|
FIELD_HEAD(SoundMacro::CmdSplitVel, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitVel, macroStep),
|
FIELD_HEAD(SoundMacro::CmdSplitVel, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,9 +270,9 @@ bool SoundMacro::CmdSplitVel::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
/* Do Branch */
|
/* Do Branch */
|
||||||
if (macro.id == std::get<0>(st.m_pc.back()))
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
else
|
else
|
||||||
vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -370,7 +373,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdLoop::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdLoop, macroStep),
|
FIELD_HEAD(SoundMacro::CmdLoop, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -400,7 +403,7 @@ bool SoundMacro::CmdLoop::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
/* Loop back to step */
|
/* Loop back to step */
|
||||||
--st.m_loopCountdown;
|
--st.m_loopCountdown;
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
}
|
}
|
||||||
else /* Break out of loop */
|
else /* Break out of loop */
|
||||||
st.m_loopCountdown = -1;
|
st.m_loopCountdown = -1;
|
||||||
|
@ -416,12 +419,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdGoto::Introspective =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdGoto, macro),
|
FIELD_HEAD(SoundMacro::CmdGoto, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdGoto, macroStep),
|
FIELD_HEAD(SoundMacro::CmdGoto, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,9 +433,9 @@ bool SoundMacro::CmdGoto::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
/* Do Branch */
|
/* Do Branch */
|
||||||
if (macro.id == std::get<0>(st.m_pc.back()))
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
else
|
else
|
||||||
vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -516,12 +519,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPlayMacro::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdPlayMacro, macro),
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdPlayMacro, macroStep),
|
FIELD_HEAD(SoundMacro::CmdPlayMacro, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -538,7 +541,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPlayMacro::Introspective =
|
||||||
};
|
};
|
||||||
bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const
|
bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<Voice> sibVox = vox.startChildMacro(addNote, macro.id, macroStep);
|
std::shared_ptr<Voice> sibVox = vox.startChildMacro(addNote, macro.id, macroStep.step);
|
||||||
if (sibVox)
|
if (sibVox)
|
||||||
st.m_lastPlayMacroVid = sibVox->vid();
|
st.m_lastPlayMacroVid = sibVox->vid();
|
||||||
|
|
||||||
|
@ -597,12 +600,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSplitMod::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitMod, macro),
|
FIELD_HEAD(SoundMacro::CmdSplitMod, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitMod, macroStep),
|
FIELD_HEAD(SoundMacro::CmdSplitMod, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,9 +616,9 @@ bool SoundMacro::CmdSplitMod::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
/* Do Branch */
|
/* Do Branch */
|
||||||
if (macro.id == std::get<0>(st.m_pc.back()))
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
else
|
else
|
||||||
vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -663,7 +666,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSetAdsr::Introspective =
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSetAdsr, table),
|
FIELD_HEAD(SoundMacro::CmdSetAdsr, table),
|
||||||
"ADSR"sv,
|
"ADSR"sv,
|
||||||
0, 16383, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSetAdsr, dlsMode),
|
FIELD_HEAD(SoundMacro::CmdSetAdsr, dlsMode),
|
||||||
|
@ -698,7 +701,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdScaleVolume::Introspective =
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdScaleVolume, table),
|
FIELD_HEAD(SoundMacro::CmdScaleVolume, table),
|
||||||
"Curve"sv,
|
"Curve"sv,
|
||||||
0, 16383, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdScaleVolume, originalVol),
|
FIELD_HEAD(SoundMacro::CmdScaleVolume, originalVol),
|
||||||
|
@ -777,7 +780,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdEnvelope::Introspective =
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdEnvelope, table),
|
FIELD_HEAD(SoundMacro::CmdEnvelope, table),
|
||||||
"Curve"sv,
|
"Curve"sv,
|
||||||
0, 16383, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdEnvelope, msSwitch),
|
FIELD_HEAD(SoundMacro::CmdEnvelope, msSwitch),
|
||||||
|
@ -900,12 +903,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSplitRnd::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitRnd, macro),
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitRnd, macroStep),
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -916,9 +919,9 @@ bool SoundMacro::CmdSplitRnd::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
/* Do branch */
|
/* Do branch */
|
||||||
if (macro.id == std::get<0>(st.m_pc.back()))
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
else
|
else
|
||||||
vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -944,7 +947,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdFadeIn::Introspective =
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdFadeIn, table),
|
FIELD_HEAD(SoundMacro::CmdFadeIn, table),
|
||||||
"Curve"sv,
|
"Curve"sv,
|
||||||
0, 16383, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdFadeIn, msSwitch),
|
FIELD_HEAD(SoundMacro::CmdFadeIn, msSwitch),
|
||||||
|
@ -1474,7 +1477,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSetPitchAdsr::Introspective =
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, table),
|
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, table),
|
||||||
"ADSR"sv,
|
"ADSR"sv,
|
||||||
0, 16383, 0,
|
0, 65535, 65535,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, keys),
|
FIELD_HEAD(SoundMacro::CmdSetPitchAdsr, keys),
|
||||||
|
@ -1592,12 +1595,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdGoSub::Introspective =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitRnd, macro),
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSplitRnd, macroStep),
|
FIELD_HEAD(SoundMacro::CmdSplitRnd, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1606,9 +1609,9 @@ bool SoundMacro::CmdGoSub::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
if (macro.id == std::get<0>(st.m_pc.back()))
|
if (macro.id == std::get<0>(st.m_pc.back()))
|
||||||
st.m_pc.emplace_back(std::get<0>(st.m_pc.back()), std::get<1>(st.m_pc.back()),
|
st.m_pc.emplace_back(std::get<0>(st.m_pc.back()), std::get<1>(st.m_pc.back()),
|
||||||
std::get<1>(st.m_pc.back())->assertPC(macroStep));
|
std::get<1>(st.m_pc.back())->assertPC(macroStep.step));
|
||||||
else
|
else
|
||||||
vox.loadMacroObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod, true);
|
vox.loadMacroObject(macro.id, macroStep.step, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod, true);
|
||||||
|
|
||||||
vox._setObjectId(std::get<0>(st.m_pc.back()));
|
vox._setObjectId(std::get<0>(st.m_pc.back()));
|
||||||
|
|
||||||
|
@ -1633,12 +1636,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdTrapEvent::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdTrapEvent, macro),
|
FIELD_HEAD(SoundMacro::CmdTrapEvent, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
0, 65535, 0
|
0, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdTrapEvent, macroStep),
|
FIELD_HEAD(SoundMacro::CmdTrapEvent, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1649,15 +1652,15 @@ bool SoundMacro::CmdTrapEvent::Do(SoundMacroState& st, Voice& vox) const
|
||||||
{
|
{
|
||||||
case EventType::KeyOff:
|
case EventType::KeyOff:
|
||||||
vox.m_keyoffTrap.macroId = macro.id;
|
vox.m_keyoffTrap.macroId = macro.id;
|
||||||
vox.m_keyoffTrap.macroStep = macroStep;
|
vox.m_keyoffTrap.macroStep = macroStep.step;
|
||||||
break;
|
break;
|
||||||
case EventType::SampleEnd:
|
case EventType::SampleEnd:
|
||||||
vox.m_sampleEndTrap.macroId = macro.id;
|
vox.m_sampleEndTrap.macroId = macro.id;
|
||||||
vox.m_sampleEndTrap.macroStep = macroStep;
|
vox.m_sampleEndTrap.macroStep = macroStep.step;
|
||||||
break;
|
break;
|
||||||
case EventType::MessageRecv:
|
case EventType::MessageRecv:
|
||||||
vox.m_messageTrap.macroId = macro.id;
|
vox.m_messageTrap.macroId = macro.id;
|
||||||
vox.m_messageTrap.macroStep = macroStep;
|
vox.m_messageTrap.macroStep = macroStep.step;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1722,8 +1725,8 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdSendMessage::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSendMessage, macro),
|
FIELD_HEAD(SoundMacro::CmdSendMessage, macro),
|
||||||
"SoundMacro"sv,
|
"Macro"sv,
|
||||||
1, 16383, 1
|
1, 65535, 65535
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdSendMessage, voiceVar),
|
FIELD_HEAD(SoundMacro::CmdSendMessage, voiceVar),
|
||||||
|
@ -3118,7 +3121,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdIfEqual::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdIfEqual, macroStep),
|
FIELD_HEAD(SoundMacro::CmdIfEqual, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -3138,7 +3141,7 @@ bool SoundMacro::CmdIfEqual::Do(SoundMacroState& st, Voice& vox) const
|
||||||
useB = st.m_variables[b & 0x1f];
|
useB = st.m_variables[b & 0x1f];
|
||||||
|
|
||||||
if ((useA == useB) ^ notEq)
|
if ((useA == useB) ^ notEq)
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3176,7 +3179,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdIfLess::Introspective =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
FIELD_HEAD(SoundMacro::CmdIfLess, macroStep),
|
FIELD_HEAD(SoundMacro::CmdIfLess, macroStep),
|
||||||
"SoundMacro Step"sv,
|
"Macro Step"sv,
|
||||||
0, 65535, 0
|
0, 65535, 0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -3196,7 +3199,7 @@ bool SoundMacro::CmdIfLess::Do(SoundMacroState& st, Voice& vox) const
|
||||||
useB = st.m_variables[b & 0x1f];
|
useB = st.m_variables[b & 0x1f];
|
||||||
|
|
||||||
if ((useA < useB) ^ notLt)
|
if ((useA < useB) ^ notLt)
|
||||||
st._setPC(macroStep);
|
st._setPC(macroStep.step);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue