From ca81c0760071fd58fe01202d97a552b4cc1d52f4 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 24 Jul 2018 20:01:01 -1000 Subject: [PATCH] Add translation infrastructure --- Editor/CMakeLists.txt | 8 + Editor/KeyboardWidget.cpp | 4 +- Editor/SoundMacroEditor.cpp | 180 +++++++++--- Editor/SoundMacroEditor.hpp | 23 +- Editor/main.cpp | 7 + Editor/resources/lang_de.ts | 415 +++++++++++++++++++++++++++ Editor/resources/translation_res.qrc | 5 + include/amuse/AudioGroupPool.hpp | 19 ++ lib/AudioGroupPool.cpp | 45 ++- lib/SoundMacroState.cpp | 103 ++++++- 10 files changed, 747 insertions(+), 62 deletions(-) create mode 100644 Editor/resources/lang_de.ts create mode 100644 Editor/resources/translation_res.qrc diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index b706998..89bd103 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -9,6 +9,7 @@ find_package(Qt5Widgets) find_package(Qt5Network) find_package(Qt5Xml) find_package(Qt5Svg) +find_package(Qt5LinguistTools) if(WIN32) list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest) @@ -22,7 +23,13 @@ add_subdirectory(platforms/freedesktop) declare_qticon_target() list(APPEND PLAT_SRCS mainicon_qt.cpp) +configure_file(resources/translation_res.qrc translation_res.qrc @ONLY) +set(TRANSLATIONS + resources/lang_de.ts) +QT5_CREATE_TRANSLATION(QM_FILES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../lib ${TRANSLATIONS}) + QT5_ADD_RESOURCES(qrc_resources.cpp resources/resources.qrc) +QT5_ADD_RESOURCES(qrc_translation_res.cpp ${CMAKE_CURRENT_BINARY_DIR}/translation_res.qrc OPTIONS -no-compress) add_executable(amuse-gui WIN32 MACOSX_BUNDLE Common.hpp Common.cpp @@ -42,6 +49,7 @@ add_executable(amuse-gui WIN32 MACOSX_BUNDLE SongGroupEditor.hpp SongGroupEditor.cpp AudioGroupModel.hpp AudioGroupModel.cpp resources/resources.qrc qrc_resources.cpp + ${QM_FILES} qrc_translation_res.cpp ${PLAT_SRCS} main.cpp) if(COMMAND add_sanitizers) diff --git a/Editor/KeyboardWidget.cpp b/Editor/KeyboardWidget.cpp index 2274d0b..0eae81d 100644 --- a/Editor/KeyboardWidget.cpp +++ b/Editor/KeyboardWidget.cpp @@ -208,7 +208,7 @@ void KeyboardWidget::showEvent(QShowEvent* event) { if (QScrollArea* scroll = qobject_cast(parentWidget()->parentWidget())) { - /* Scroll to C3 */ - scroll->ensureVisible(141 * 4 + scroll->width(), 0, 0, 0); + /* Scroll to C2 */ + scroll->ensureVisible(141 * 3 + scroll->width(), 0, 0, 0); } } diff --git a/Editor/SoundMacroEditor.cpp b/Editor/SoundMacroEditor.cpp index 8ced696..dda78a8 100644 --- a/Editor/SoundMacroEditor.cpp +++ b/Editor/SoundMacroEditor.cpp @@ -29,20 +29,21 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm QGridLayout* layout = new QGridLayout; if (m_introspection) { - m_titleText.setText(StringViewToQString(m_introspection->m_name)); + m_titleText.setText(tr(m_introspection->m_name.data())); for (int f = 0; f < 7; ++f) { const amuse::SoundMacro::CmdIntrospection::Field& field = m_introspection->m_fields[f]; if (!field.m_name.empty()) { - layout->addWidget(new QLabel(StringViewToQString(field.m_name)), 0, f); + layout->addWidget(new QLabel(tr(field.m_name.data())), 0, f); + int value; switch (field.m_tp) { case amuse::SoundMacro::CmdIntrospection::Field::Type::Bool: { QCheckBox* cb = new QCheckBox; cb->setProperty("fieldIndex", f); - cb->setCheckState(field.m_default ? Qt::Checked : Qt::Unchecked); + cb->setCheckState(amuse::AccessField(m_cmd, field) ? Qt::Checked : Qt::Unchecked); connect(cb, SIGNAL(stateChanged(int)), this, SLOT(boolChanged(int))); layout->addWidget(cb, 1, f); break; @@ -58,11 +59,48 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm sb->setProperty("fieldIndex", f); sb->setMinimum(int(field.m_min)); sb->setMaximum(int(field.m_max)); - sb->setValue(int(field.m_default)); + switch (field.m_tp) + { + case amuse::SoundMacro::CmdIntrospection::Field::Type::Int8: + sb->setValue(amuse::AccessField(m_cmd, field)); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt8: + sb->setValue(amuse::AccessField(m_cmd, field)); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::Int16: + sb->setValue(amuse::AccessField(m_cmd, field)); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt16: + sb->setValue(amuse::AccessField(m_cmd, field)); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::Int32: + sb->setValue(amuse::AccessField(m_cmd, field)); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt32: + sb->setValue(amuse::AccessField(m_cmd, field)); + break; + default: + break; + } connect(sb, SIGNAL(valueChanged(int)), this, SLOT(numChanged(int))); layout->addWidget(sb, 1, f); break; } + case amuse::SoundMacro::CmdIntrospection::Field::Type::Choice: + { + FieldComboBox* cb = new FieldComboBox; + cb->setProperty("fieldIndex", f); + for (int j = 0; j < 4; ++j) + { + if (field.m_choices->empty()) + break; + cb->addItem(tr(field.m_choices[j].data())); + } + cb->setCurrentIndex(int(amuse::AccessField(m_cmd, field))); + connect(cb, SIGNAL(currentIndexChanged(int)), this, SLOT(choiceChanged(int))); + layout->addWidget(cb, 1, f); + break; + } default: break; } @@ -79,12 +117,6 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, QWidget* parent) CommandWidget::CommandWidget(amuse::SoundMacro::CmdOp op, QWidget* parent) : CommandWidget(nullptr, op, parent) {} -template -static T& AccessField(amuse::SoundMacro::ICmd* cmd, const amuse::SoundMacro::CmdIntrospection::Field& field) -{ - return *reinterpret_cast(reinterpret_cast(std::addressof(*cmd)) + field.m_offset); -} - void CommandWidget::boolChanged(int state) { if (m_introspection) @@ -92,7 +124,7 @@ void CommandWidget::boolChanged(int state) QCheckBox* cb = static_cast(sender()); const amuse::SoundMacro::CmdIntrospection::Field& field = m_introspection->m_fields[cb->property("fieldIndex").toInt()]; - AccessField(m_cmd, field) = state == Qt::Checked; + amuse::AccessField(m_cmd, field) = state == Qt::Checked; } } @@ -106,22 +138,22 @@ void CommandWidget::numChanged(int value) switch (field.m_tp) { case amuse::SoundMacro::CmdIntrospection::Field::Type::Int8: - AccessField(m_cmd, field) = int8_t(value); + amuse::AccessField(m_cmd, field) = int8_t(value); break; case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt8: - AccessField(m_cmd, field) = uint8_t(value); + amuse::AccessField(m_cmd, field) = uint8_t(value); break; case amuse::SoundMacro::CmdIntrospection::Field::Type::Int16: - AccessField(m_cmd, field) = int16_t(value); + amuse::AccessField(m_cmd, field) = int16_t(value); break; case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt16: - AccessField(m_cmd, field) = uint16_t(value); + amuse::AccessField(m_cmd, field) = uint16_t(value); break; case amuse::SoundMacro::CmdIntrospection::Field::Type::Int32: - AccessField(m_cmd, field) = int32_t(value); + amuse::AccessField(m_cmd, field) = int32_t(value); break; case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt32: - AccessField(m_cmd, field) = uint32_t(value); + amuse::AccessField(m_cmd, field) = uint32_t(value); break; default: break; @@ -179,11 +211,6 @@ void CommandWidget::animationDestroyed() m_animation = nullptr; } -SoundMacroListing* CommandWidget::getParent() const -{ - return qobject_cast(parentWidget()); -} - void CommandWidget::paintEvent(QPaintEvent* event) { /* Rounded frame */ @@ -433,7 +460,11 @@ CatalogueItem::CatalogueItem(amuse::SoundMacro::CmdOp op, const QString& name, { QHBoxLayout* layout = new QHBoxLayout; QLabel* iconLab = new QLabel; - iconLab->setPixmap(QIcon(":/icons/IconOpen.svg").pixmap(32, 32)); + QString iconPath = QStringLiteral(":/commands/%1.svg").arg(name); + if (QFile(iconPath).exists()) + iconLab->setPixmap(QIcon(iconPath).pixmap(32, 32)); + else + iconLab->setPixmap(QIcon(QStringLiteral(":/icons/IconOpen.svg")).pixmap(32, 32)); layout->addWidget(iconLab); layout->addWidget(new QLabel(name)); layout->addStretch(); @@ -456,22 +487,74 @@ CatalogueItem::CatalogueItem(const CatalogueItem& other, QWidget* parent) setLayout(layout); } -SoundMacroCatalogue::SoundMacroCatalogue(QWidget* parent) -: QWidget(parent) +static const char* CategoryStrings[] = { - QVBoxLayout* layout = new QVBoxLayout; + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Control"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Pitch"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Sample"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Setup"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Special"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Structure"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Volume") +}; + +static const char* CategoryDocStrings[] = +{ + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Commands to control the voice"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Commands to control the voice's pitch"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Commands to control the voice's sample playback"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Commands to setup the voice's mixing process"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Miscellaneous commands"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Commands to control macro branching"), + QT_TRANSLATE_NOOP("SoundMacroCatalogue", "Commands to control the voice's volume") +}; + +SoundMacroCatalogue::SoundMacroCatalogue(QWidget* parent) +: QTreeWidget(parent) +{ + setSelectionMode(QAbstractItemView::NoSelection); + setColumnCount(1); + setHeaderHidden(true); + + QTreeWidgetItem* rootItems[int(amuse::SoundMacro::CmdType::CmdTypeMAX)]; + for (int i = 0; i < int(amuse::SoundMacro::CmdType::CmdTypeMAX); ++i) + { + rootItems[i] = new QTreeWidgetItem(this); + setItemWidget(rootItems[i], 0, new CatalogueItem(amuse::SoundMacro::CmdOp::Invalid, + tr(CategoryStrings[i]), tr(CategoryDocStrings[i]))); + } + for (int i = 1; i < int(amuse::SoundMacro::CmdOp::CmdOpMAX); ++i) { const amuse::SoundMacro::CmdIntrospection* cmd = amuse::SoundMacro::GetCmdIntrospection(amuse::SoundMacro::CmdOp(i)); if (cmd) { - layout->addWidget(new CatalogueItem(amuse::SoundMacro::CmdOp(i), StringViewToQString(cmd->m_name), - StringViewToQString(cmd->m_description))); + QTreeWidgetItem* item = new QTreeWidgetItem(rootItems[int(cmd->m_tp)]); + setItemWidget(item, 0, new CatalogueItem(amuse::SoundMacro::CmdOp(i), tr(cmd->m_name.data()), + tr(cmd->m_description.data()))); } } - layout->addStretch(); - setLayout(layout); +} + +void SoundMacroCatalogue::mousePressEvent(QMouseEvent* event) +{ + QTreeWidget::mousePressEvent(event); + event->ignore(); +} + +void SoundMacroCatalogue::mouseReleaseEvent(QMouseEvent* event) +{ + QTreeWidget::mouseReleaseEvent(event); + event->ignore(); +} + +void SoundMacroCatalogue::mouseMoveEvent(QMouseEvent* event) +{ + SoundMacroEditor* editor = qobject_cast(parentWidget()->parentWidget()); + if (!editor || !editor->m_draggedItem) + QTreeWidget::mouseMoveEvent(event); + event->ignore(); } void SoundMacroEditor::beginCommandDrag(CommandWidget* widget, const QPoint& eventPt, const QPoint& pt) @@ -495,7 +578,7 @@ void SoundMacroEditor::beginCatalogueDrag(CatalogueItem* item, const QPoint& eve void SoundMacroEditor::mousePressEvent(QMouseEvent* event) { - if (m_catalogue->parentWidget()->parentWidget()->geometry().contains(event->pos())) + if (m_catalogue->geometry().contains(event->pos())) { QPoint fromParent1 = m_catalogue->mapFrom(this, event->pos()); QWidget* ch = m_catalogue->childAt(fromParent1); @@ -504,7 +587,7 @@ void SoundMacroEditor::mousePressEvent(QMouseEvent* event) CatalogueItem* child = nullptr; while (ch && !(child = qobject_cast(ch))) ch = ch->parentWidget(); - if (child) + if (child && child->getCmdOp() != amuse::SoundMacro::CmdOp::Invalid) { QPoint fromParent2 = child->mapFrom(m_catalogue, fromParent1); beginCatalogueDrag(child, event->pos(), fromParent2); @@ -571,6 +654,7 @@ void SoundMacroEditor::mouseMoveEvent(QMouseEvent* event) m_listing->insertDragout(); m_dragInsertIdx = -1; } + m_catalogue->update(); update(); } else if (m_draggedCmd) @@ -579,6 +663,7 @@ void SoundMacroEditor::mouseMoveEvent(QMouseEvent* event) m_draggedCmd->move(m_draggedCmd->x(), listingPt.y() - m_draggedPt.y()); if (m_listing->parentWidget()->parentWidget()->geometry().contains(event->pos())) m_listing->moveDrag(m_draggedCmd, listingPt, this, event); + m_listing->update(); update(); } } @@ -601,6 +686,16 @@ void SoundMacroEditor::keyPressEvent(QKeyEvent* event) } } +void SoundMacroEditor::catalogueDoubleClicked(QTreeWidgetItem* item, int column) +{ + if (CatalogueItem* cItem = qobject_cast(m_catalogue->itemWidget(item, column))) + { + amuse::SoundMacro::CmdOp op = cItem->getCmdOp(); + if (op != amuse::SoundMacro::CmdOp::Invalid) + m_listing->insert(op); + } +} + SoundMacroEditor::SoundMacroEditor(ProjectModel::SoundMacroNode* node, QWidget* parent) : EditorWidget(parent), m_splitter(new QSplitter), m_listing(new SoundMacroListing(node)), m_catalogue(new SoundMacroCatalogue) @@ -618,24 +713,19 @@ SoundMacroEditor::SoundMacroEditor(ProjectModel::SoundMacroNode* node, QWidget* } { - QScrollArea* catalogueScroll = new QScrollArea; - catalogueScroll->setWidget(m_catalogue); - catalogueScroll->setWidgetResizable(true); QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); - catalogueScroll->setSizePolicy(sizePolicy); - catalogueScroll->setMinimumWidth(150); - catalogueScroll->setGeometry(0, 0, 215, 0); - catalogueScroll->setMaximumWidth(300); - catalogueScroll->setBackgroundRole(QPalette::Base); - catalogueScroll->setAutoFillBackground(true); - catalogueScroll->setFrameShape(QFrame::StyledPanel); - catalogueScroll->setFrameShadow(QFrame::Sunken); - catalogueScroll->setLineWidth(1); - m_splitter->addWidget(catalogueScroll); + m_catalogue->setSizePolicy(sizePolicy); + m_catalogue->setMinimumWidth(150); + m_catalogue->setGeometry(0, 0, 215, 0); + m_catalogue->setMaximumWidth(300); + m_splitter->addWidget(m_catalogue); } + connect(m_catalogue, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), + this, SLOT(catalogueDoubleClicked(QTreeWidgetItem*, int))); + m_splitter->setCollapsible(0, false); QGridLayout* layout = new QGridLayout; layout->setContentsMargins(QMargins()); diff --git a/Editor/SoundMacroEditor.hpp b/Editor/SoundMacroEditor.hpp index f03bb79..06dccfd 100644 --- a/Editor/SoundMacroEditor.hpp +++ b/Editor/SoundMacroEditor.hpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include class SoundMacroListing; class CatalogueItem; @@ -24,6 +26,17 @@ public: void wheelEvent(QWheelEvent* event) { event->ignore(); } }; +class FieldComboBox : public QComboBox +{ +Q_OBJECT +public: + FieldComboBox(QWidget* parent = Q_NULLPTR) + : QComboBox(parent) {} + + /* Don't scroll */ + void wheelEvent(QWheelEvent* event) { event->ignore(); } +}; + class CommandWidget : public QWidget { Q_OBJECT @@ -40,7 +53,6 @@ class CommandWidget : public QWidget void snapOpen(); void snapClosed(); void setIndex(int index); - SoundMacroListing* getParent() const; private slots: void animationDestroyed(); void boolChanged(int); @@ -94,16 +106,20 @@ public: amuse::SoundMacro::CmdOp getCmdOp() const { return m_op; } }; -class SoundMacroCatalogue : public QWidget +class SoundMacroCatalogue : public QTreeWidget { Q_OBJECT public: explicit SoundMacroCatalogue(QWidget* parent = Q_NULLPTR); + void mousePressEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event); }; class SoundMacroEditor : public EditorWidget { Q_OBJECT + friend class SoundMacroCatalogue; QSplitter* m_splitter; SoundMacroListing* m_listing; SoundMacroCatalogue* m_catalogue; @@ -120,6 +136,9 @@ public: void mouseReleaseEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void keyPressEvent(QKeyEvent* event); + +public slots: + void catalogueDoubleClicked(QTreeWidgetItem* item, int column); }; diff --git a/Editor/main.cpp b/Editor/main.cpp index 6f10f5f..17eaed1 100644 --- a/Editor/main.cpp +++ b/Editor/main.cpp @@ -1,8 +1,10 @@ #include #include #include +#include #include "MainWindow.hpp" #include "boo/IApplication.hpp" +#include using namespace std::literals; @@ -89,6 +91,11 @@ int main(int argc, char* argv[]) BooInterface booApp; boo::APP = &booApp; + Q_INIT_RESOURCE(translation_res); + QTranslator translator; + if (translator.load(QLocale(), QLatin1String("lang"), QLatin1String("_"), QLatin1String(":/translations"))) + a.installTranslator(&translator); + MainWindow w; w.show(); return a.exec(); diff --git a/Editor/resources/lang_de.ts b/Editor/resources/lang_de.ts new file mode 100644 index 0000000..6cb12ce --- /dev/null +++ b/Editor/resources/lang_de.ts @@ -0,0 +1,415 @@ + + + + + MainWindow + + + Amuse + Amuse + + + + &File + &Datei + + + + P&roject + Projekt + + + + &Audio + &Audio + + + + &MIDI + &MIDI + + + + &Edit + &Bearbeiten + + + + &New Project + &Neues Projekt + + + + &Open Project + &Offenes Projekt + + + + &Undo + &Rückgängig machen + + + + &Redo + & Wiederholen + + + + &Cut + &Schnitt + + + + C&opy + Kopieren + + + + &Paste + &Einfügen + + + + &Delete + &Löschen + + + + &Import + &Einführen + + + + Ctrl+I + Strg + I + + + + New SF&X Group + Neue SF & X-Gruppe + + + + New Son&g Group + Neue Son & g-Gruppe + + + + New Sound &Macro + Neuer Sound und Makro + + + + New &Keymap + Neue & Keymap + + + + New &Layers + Neu & Schichten + + + + &Auto-Play + &Automatisches Abspielen + + + + &Output Device: + &Ausgabegerät: + + + + &Input Device: + &Eingabegerät: + + + + &Export GameCube Groups + & GameCube-Gruppen exportieren + + + + Ctrl+E + Strg + E + + + + &New Subproject + & Neues Teilprojekt + + + + New &ADSR + Neu & ADSR + + + + New &Curve + Neu und Kurve + + + + Quit + Verlassen + + + + The directory at '%1' must exist for the Amuse editor. + Das Verzeichnis unter '% 1' muss für den Amuse-Editor vorhanden sein. + + + + Directory does not exist + Verzeichnis existiert nicht + + + + test + Prüfung + + + + The directory at '%1' must be writable for the Amuse editor. + Das Verzeichnis unter '% 1' muss für den Amuse-Editor beschreibbar sein. + + + + Unable to write to directory + Es konnte nicht in das Verzeichnis geschrieben werden + + + + No Audio Devices Found + Keine Audiogeräte gefunden + + + + No MIDI Devices Found + Keine MIDI-Geräte gefunden + + + + New Project + Neues Projekt + + + + Open Project + Offenes Projekt + + + + The directory at '%1' does not exist. + Das Verzeichnis '% 1' existiert nicht. + + + + Bad Directory + Schlechtes Verzeichnis + + + + Opening + Öffnung + + + + + + Scanning Project + Projekt scannen + + + + Opening %1 + Eröffnung% 1 + + + + Import Project + Projekt importieren + + + + The file at '%1' could not be interpreted as a MusyX container. + Die Datei '% 1' konnte nicht als MusyX-Container interpretiert werden. + + + + Unsupported MusyX Container + Nicht unterstützter MusyX-Container + + + + Sample Import Mode + Beispiel-Importmodus + + + + Amuse can import samples as WAV files for ease of editing, import original compressed data for lossless repacking, or both. Exporting the project will prefer whichever version was modified most recently. + Amuse kann Stichproben als WAV-Dateien importieren, um die Bearbeitung zu vereinfachen, original komprimierte Daten für verlustfreies Repacking importieren oder beides. Beim Exportieren des Projekts wird die Version bevorzugt, die zuletzt geändert wurde. + + + + Import Compressed + Komprimiert importieren + + + + Import WAVs + Importieren Sie WAVs + + + + Import Both + Beide importieren + + + + Raw Import Mode + Roher Import-Modus + + + + Would you like to scan for all MusyX group files in this directory? + Möchten Sie nach allen MusyX-Gruppendateien in diesem Verzeichnis suchen? + + + + Project Name + Projektname + + + + What should this project be named? + Wie soll dieses Projekt benannt werden? + + + + + Importing + Importieren + + + + + Importing %1 + Importieren von% 1 + + + + ProjectModel + + + Sound Macros + Sound-Makros + + + + ADSRs + ADSRs + + + + Curves + Kurven + + + + Keymaps + Schlüsselkarten + + + + Layers + Lagen + + + + QObject + + + A directory at '%1/%2' could not be created. + Ein Verzeichnis unter '% 1 /% 2' konnte nicht erstellt werden. + + + + Unable to create directory + Kann kein Verzeichnis erstellen + + + + SoundMacroCatalogue + + + Control + Steuerung + + + + Pitch + Tonhöhe + + + + Sample + Probe + + + + Setup + Konfiguration + + + + Special + Besondere + + + + Structure + Struktur + + + + Volume + Volumen + + + + Commands to control the voice + Befehle zum Steuern der Stimme + + + + Commands to control the voice's pitch + Befehle zum Steuern der Tonhöhe der Stimme + + + + Commands to control the voice's sample playback + Befehle zum Steuern der Sample-Wiedergabe der Voice + + + + Commands to setup the voice's mixing process + Befehle zum Einrichten des Mischprozesses der Voice + + + + Miscellaneous commands + Verschiedene Befehle + + + + Commands to control macro branching + Befehle zum Steuern der Makroverzweigung + + + + Commands to control the voice's volume + Befehle zum Steuern der Lautstärke der Stimme + + + diff --git a/Editor/resources/translation_res.qrc b/Editor/resources/translation_res.qrc new file mode 100644 index 0000000..162455d --- /dev/null +++ b/Editor/resources/translation_res.qrc @@ -0,0 +1,5 @@ + + + lang_de.qm + + diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index 928f76a..1834049 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -125,6 +125,18 @@ struct SoundMacro Invalid = 0xff }; + enum class CmdType : uint8_t + { + Control, + Pitch, + Sample, + Setup, + Special, + Structure, + Volume, + CmdTypeMAX + }; + /** Introspection structure used by editors to define user interactivity per command */ struct CmdIntrospection { @@ -151,6 +163,7 @@ struct SoundMacro int64_t m_min, m_max, m_default; std::string_view m_choices[4]; }; + CmdType m_tp; std::string_view m_name; std::string_view m_description; Field m_fields[7]; @@ -1120,6 +1133,12 @@ struct SoundMacro void readCmds(athena::io::IStreamReader& r, uint32_t size); }; +template +static inline T& AccessField(SoundMacro::ICmd* cmd, const SoundMacro::CmdIntrospection::Field& field) +{ + return *reinterpret_cast(reinterpret_cast(std::addressof(*cmd)) + field.m_offset); +} + /** Converts time-cents representation to seconds */ static inline double TimeCentsToSeconds(int32_t tc) { diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp index d7afc6b..fd0a022 100644 --- a/lib/AudioGroupPool.cpp +++ b/lib/AudioGroupPool.cpp @@ -28,7 +28,50 @@ struct MakeDefaultCmdOp template static std::unique_ptr Do(R& r) { - return std::make_unique(); + std::unique_ptr ret = std::make_unique(); + if (const SoundMacro::CmdIntrospection* introspection = SoundMacro::GetCmdIntrospection(r)) + { + for (int f = 0; f < 7; ++f) + { + const amuse::SoundMacro::CmdIntrospection::Field& field = introspection->m_fields[f]; + if (!field.m_name.empty()) + { + switch (field.m_tp) + { + case amuse::SoundMacro::CmdIntrospection::Field::Type::Bool: + AccessField(ret.get(), field) = bool(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::Int8: + case amuse::SoundMacro::CmdIntrospection::Field::Type::Choice: + AccessField(ret.get(), field) = int8_t(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt8: + AccessField(ret.get(), field) = uint8_t(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::Int16: + AccessField(ret.get(), field) = int16_t(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt16: + AccessField(ret.get(), field) = uint16_t(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::Int32: + AccessField(ret.get(), field) = int32_t(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::UInt32: + AccessField(ret.get(), field) = uint32_t(field.m_default); + break; + case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId: + case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId: + case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId: + AccessField>(ret.get(), field).id = uint16_t(field.m_default); + break; + default: + break; + } + } + } + } + return ret; } }; diff --git a/lib/SoundMacroState.cpp b/lib/SoundMacroState.cpp index fe97f39..c4857e8 100644 --- a/lib/SoundMacroState.cpp +++ b/lib/SoundMacroState.cpp @@ -142,7 +142,8 @@ constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType() { return SoundMacro::CmdIntrospection::Field::Type::Invalid; } template , int> = 0> constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType() -{ return SoundMacro::CmdIntrospection::Field::Type::Choice; } +{ static_assert(sizeof(T) == 1, "Enum must be an 8-bit type"); + return SoundMacro::CmdIntrospection::Field::Type::Choice; } template <> constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType() { return SoundMacro::CmdIntrospection::Field::Type::Bool; } @@ -178,6 +179,7 @@ constexpr SoundMacro::CmdIntrospection::Field::Type GetFieldType