summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2023-03-21 17:33:41 +0100
committerStanislaw Halik <sthalik@misaki.pl>2023-04-13 23:55:27 +0200
commit051fb2f94f6364b80219a3c671bb953d2e54a140 (patch)
treed76351f995884acbada1e26b5fe869daf5f4a8a1
parent94e7374bad0da0d9d132401bd57526b509497c7d (diff)
proto/osc: add open sound control output method
-rw-r--r--proto-osc/CMakeLists.txt15
-rw-r--r--proto-osc/dialog.cpp38
-rw-r--r--proto-osc/dialog.hpp29
-rw-r--r--proto-osc/dialog.ui131
-rw-r--r--proto-osc/images/osc-icon.pngbin0 -> 868 bytes
-rw-r--r--proto-osc/lang/nl_NL.ts45
-rw-r--r--proto-osc/lang/ru_RU.ts45
-rw-r--r--proto-osc/lang/stub.ts45
-rw-r--r--proto-osc/lang/zh_CN.ts45
-rw-r--r--proto-osc/metadata.cpp8
-rw-r--r--proto-osc/metadata.hpp10
-rw-r--r--proto-osc/osc-res.qrc5
-rw-r--r--proto-osc/proto.cpp54
-rw-r--r--proto-osc/proto.hpp29
-rw-r--r--proto-osc/settings.hpp14
15 files changed, 513 insertions, 0 deletions
diff --git a/proto-osc/CMakeLists.txt b/proto-osc/CMakeLists.txt
new file mode 100644
index 00000000..eeaf206c
--- /dev/null
+++ b/proto-osc/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(SDK_OSCPACK "" CACHE PATH "oscpack build directory")
+if(SDK_OSCPACK)
+ if(WIN32)
+ if(NOT EXISTS "${SDK_OSCPACK}/include/.")
+ message(FATAL_ERROR "SDK_OSCPACK should have 'include' subdirectory (or symlink) to src dir")
+ endif()
+ link_directories("${SDK_OSCPACK}")
+ include_directories("${SDK_OSCPACK}/include" "${SDK_OSCPACK}/include/oscpack")
+ else()
+ link_directories("${SDK_OSCPACK}/lib" "${SDK_OSCPACK}/lib32")
+ include_directories("${SDK_OSCPACK}/include/oscpack")
+ endif()
+ link_libraries(oscpack)
+ otr_module(proto-osc)
+endif()
diff --git a/proto-osc/dialog.cpp b/proto-osc/dialog.cpp
new file mode 100644
index 00000000..54bf6885
--- /dev/null
+++ b/proto-osc/dialog.cpp
@@ -0,0 +1,38 @@
+#include "dialog.hpp"
+#include <QHostAddress>
+#include <QPalette>
+
+void osc_dialog::host_address_edited(const QString& str)
+{
+ bool bad = QHostAddress{str}.isNull();
+ auto pal = pal_;
+ for (auto role : { QPalette::Highlight, QPalette::Window })
+ if (bad)
+ pal.setColor(role, Qt::red);
+ ui.address->setPalette(pal);
+}
+
+osc_dialog::osc_dialog() :
+ pal_{palette()}
+{
+ ui.setupUi( this );
+
+ tie_setting(s.address, ui.address);
+ tie_setting(s.port, ui.port);
+ connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &osc_dialog::doOK);
+ connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &osc_dialog::doCancel);
+ connect(ui.address, &QLineEdit::textChanged, this, &osc_dialog::host_address_edited);
+ host_address_edited(ui.address->text());
+}
+
+void osc_dialog::save() { s.b->save(); }
+void osc_dialog::reload() { s.b->reload(); }
+
+void osc_dialog::doOK() { s.b->save(); close(); }
+void osc_dialog::doCancel() { close(); }
+
+void osc_dialog::register_protocol(IProtocol*) {}
+void osc_dialog::unregister_protocol() {}
+
+bool osc_dialog::embeddable() noexcept { return true; }
+void osc_dialog::set_buttons_visible(bool x) noexcept { ui.buttonBox->setVisible(x); }
diff --git a/proto-osc/dialog.hpp b/proto-osc/dialog.hpp
new file mode 100644
index 00000000..29682843
--- /dev/null
+++ b/proto-osc/dialog.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "settings.hpp"
+#include "ui_dialog.h"
+#include "api/plugin-api.hpp"
+
+class osc_dialog: public IProtocolDialog
+{
+ Q_OBJECT
+
+public:
+ osc_dialog();
+ void register_protocol(IProtocol*) override;
+ void unregister_protocol() override;
+private:
+ void set_buttons_visible(bool x) noexcept override;
+ bool embeddable() noexcept override;
+ void save() override;
+ void reload() override;
+
+ Ui_OSC_Dialog ui;
+ osc_settings s;
+ const QPalette pal_;
+
+private slots:
+ void doOK();
+ void doCancel();
+ void host_address_edited(const QString& str);
+};
diff --git a/proto-osc/dialog.ui b/proto-osc/dialog.ui
new file mode 100644
index 00000000..5a078bbd
--- /dev/null
+++ b/proto-osc/dialog.ui
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OSC_Dialog</class>
+ <widget class="QWidget" name="OSC_Dialog">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>284</width>
+ <height>102</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>284</width>
+ <height>102</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>OSC protocol settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="osc-res.qrc">
+ <normaloff>:/images/osc-icon.png</normaloff>:/images/osc-icon.png</iconset>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Destination address</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="address">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="maxLength">
+ <number>256</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="port">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../gui/opentrack-res.qrc"/>
+ <include location="osc-res.qrc"/>
+ </resources>
+ <connections/>
+ <designerdata>
+ <property name="gridDeltaX">
+ <number>5</number>
+ </property>
+ <property name="gridDeltaY">
+ <number>5</number>
+ </property>
+ <property name="gridSnapX">
+ <bool>true</bool>
+ </property>
+ <property name="gridSnapY">
+ <bool>true</bool>
+ </property>
+ <property name="gridVisible">
+ <bool>false</bool>
+ </property>
+ </designerdata>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/proto-osc/images/osc-icon.png b/proto-osc/images/osc-icon.png
new file mode 100644
index 00000000..3ef546e9
--- /dev/null
+++ b/proto-osc/images/osc-icon.png
Binary files differ
diff --git a/proto-osc/lang/nl_NL.ts b/proto-osc/lang/nl_NL.ts
new file mode 100644
index 00000000..260b7adc
--- /dev/null
+++ b/proto-osc/lang/nl_NL.ts
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>OSC_Dialog</name>
+ <message>
+ <source>OSC protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Destination address</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_metadata</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_proto</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invalid destination address &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-osc/lang/ru_RU.ts b/proto-osc/lang/ru_RU.ts
new file mode 100644
index 00000000..498d68d6
--- /dev/null
+++ b/proto-osc/lang/ru_RU.ts
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>OSC_Dialog</name>
+ <message>
+ <source>OSC protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Destination address</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_metadata</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_proto</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invalid destination address &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-osc/lang/stub.ts b/proto-osc/lang/stub.ts
new file mode 100644
index 00000000..20828cbd
--- /dev/null
+++ b/proto-osc/lang/stub.ts
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>OSC_Dialog</name>
+ <message>
+ <source>OSC protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Destination address</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_metadata</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_proto</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invalid destination address &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-osc/lang/zh_CN.ts b/proto-osc/lang/zh_CN.ts
new file mode 100644
index 00000000..e0d49844
--- /dev/null
+++ b/proto-osc/lang/zh_CN.ts
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>OSC_Dialog</name>
+ <message>
+ <source>OSC protocol settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Destination address</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_metadata</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>osc_proto</name>
+ <message>
+ <source>Open Sound Control</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Error binding socket to INADDR_ANY</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>%1: %2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Invalid destination address &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/proto-osc/metadata.cpp b/proto-osc/metadata.cpp
new file mode 100644
index 00000000..9d69347d
--- /dev/null
+++ b/proto-osc/metadata.cpp
@@ -0,0 +1,8 @@
+#include "metadata.hpp"
+#include "proto.hpp"
+#include "dialog.hpp"
+
+QString osc_metadata::name() { return tr("Open Sound Control"); }
+QIcon osc_metadata::icon() { return QIcon(":/images/osc-icon.png"); }
+
+OPENTRACK_DECLARE_PROTOCOL(osc_proto, osc_dialog, osc_metadata)
diff --git a/proto-osc/metadata.hpp b/proto-osc/metadata.hpp
new file mode 100644
index 00000000..c0a947aa
--- /dev/null
+++ b/proto-osc/metadata.hpp
@@ -0,0 +1,10 @@
+#pragma once
+#include "api/plugin-api.hpp"
+
+class osc_metadata : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override;
+ QIcon icon() override;
+};
diff --git a/proto-osc/osc-res.qrc b/proto-osc/osc-res.qrc
new file mode 100644
index 00000000..ade4c629
--- /dev/null
+++ b/proto-osc/osc-res.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/osc-icon.png</file>
+ </qresource>
+</RCC>
diff --git a/proto-osc/proto.cpp b/proto-osc/proto.cpp
new file mode 100644
index 00000000..2f90957c
--- /dev/null
+++ b/proto-osc/proto.cpp
@@ -0,0 +1,54 @@
+#include "proto.hpp"
+#include "ui_dialog.h"
+#include "api/plugin-api.hpp"
+#include <QQuaternion>
+#include <QHostAddress>
+#include "osc/OscOutboundPacketStream.h"
+
+osc_proto::osc_proto()
+{
+ auto reload_fn = [this] {
+ dest = QHostAddress{s.address };
+ port = (unsigned short)s.port;
+ };
+ connect(&*s.b, &bundle_::changed, this, reload_fn);
+ connect(&*s.b, &bundle_::reloading, this, reload_fn);
+}
+
+void osc_proto::pose(const double* data, const double*)
+{
+ if (dest.isNull())
+ return;
+
+ static constexpr unsigned buffer_size = 1024;
+ char buffer[buffer_size] = {};
+ osc::OutboundPacketStream p{buffer, buffer_size};
+ auto q = QQuaternion::fromEulerAngles((float)data[Pitch], (float)data[Yaw], (float)-data[Roll]).normalized();
+ p << osc::BeginMessage("/bridge/quat") << q.scalar() << q.x() << q.y() << q.z() << osc::EndMessage;
+ sock.writeDatagram(p.Data(), (int)p.Size(), dest, port);
+}
+
+module_status osc_proto::initialize()
+{
+ QString error;
+
+ dest = QHostAddress{s.address };
+ port = (unsigned short)s.port;
+
+ if (dest.isNull())
+ {
+ error = tr("Invalid destination address '%1'").arg(s.address);
+ goto fail;
+ }
+
+ if (!sock.bind())
+ {
+ error = tr("Error binding socket to INADDR_ANY");
+ goto fail;
+ }
+
+ return status_ok();
+
+fail:
+ return { tr("%1: %2").arg(error, sock.errorString()) };
+}
diff --git a/proto-osc/proto.hpp b/proto-osc/proto.hpp
new file mode 100644
index 00000000..53a5dee4
--- /dev/null
+++ b/proto-osc/proto.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+/* Copyright (c) 2023 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+
+#include "settings.hpp"
+#include "api/plugin-api.hpp"
+#include <QUdpSocket>
+#include <QHostAddress>
+
+class osc_proto : public QObject, public IProtocol
+{
+ Q_OBJECT
+
+ osc_settings s;
+ QUdpSocket sock;
+ QHostAddress dest;
+ unsigned short port = 0;
+
+public:
+ osc_proto();
+ module_status initialize() override;
+ void pose(const double *headpose, const double*) override;
+ QString game_name() override { return tr("Open Sound Control"); }
+};
diff --git a/proto-osc/settings.hpp b/proto-osc/settings.hpp
new file mode 100644
index 00000000..edeb45c6
--- /dev/null
+++ b/proto-osc/settings.hpp
@@ -0,0 +1,14 @@
+#pragma once
+#include <QString>
+#include "options/options.hpp"
+
+using namespace options;
+
+struct osc_settings : opts
+{
+ value<QString> address;
+ value<int> port;
+ osc_settings() : opts("proto-osc"), address{b, "address", "127.0.0.1"},
+ port(b, "port", 53101)
+ {}
+};