summaryrefslogtreecommitdiffhomepage
path: root/filter-nm
diff options
context:
space:
mode:
Diffstat (limited to 'filter-nm')
-rw-r--r--filter-nm/CMakeLists.txt1
-rw-r--r--filter-nm/ftnoir_filter_nm.cpp67
-rw-r--r--filter-nm/ftnoir_filter_nm.h81
-rw-r--r--filter-nm/ftnoir_filter_nm_dialog.cpp56
-rw-r--r--filter-nm/ftnoir_nm_filtercontrols.ui312
-rw-r--r--filter-nm/lang/de_DE.ts62
-rw-r--r--filter-nm/lang/nl_NL.ts62
-rw-r--r--filter-nm/lang/ru_RU.ts62
-rw-r--r--filter-nm/lang/stub.ts62
-rw-r--r--filter-nm/lang/zh_CN.ts62
10 files changed, 827 insertions, 0 deletions
diff --git a/filter-nm/CMakeLists.txt b/filter-nm/CMakeLists.txt
new file mode 100644
index 00000000..49daaee2
--- /dev/null
+++ b/filter-nm/CMakeLists.txt
@@ -0,0 +1 @@
+otr_module(filter-nm)
diff --git a/filter-nm/ftnoir_filter_nm.cpp b/filter-nm/ftnoir_filter_nm.cpp
new file mode 100644
index 00000000..b222184b
--- /dev/null
+++ b/filter-nm/ftnoir_filter_nm.cpp
@@ -0,0 +1,67 @@
+/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net>
+ *
+ * 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 "ftnoir_filter_nm.h"
+#include "compat/math-imports.hpp"
+#include "compat/macros.h"
+
+#include "api/plugin-api.hpp"
+#include "opentrack/defs.hpp"
+
+#include <algorithm>
+
+filter_nm::filter_nm()
+{
+}
+
+void filter_nm::filter(const double* input, double* output)
+{
+ tVector position = { input[TX], input[TY], input[TZ] };
+ tQuat rotation = QuatFromYPR(input + Yaw);
+
+ // order of axes: x, y, z, yaw, pitch, roll
+ if (unlikely(first_run))
+ {
+ first_run = false;
+ t.start();
+
+ last_pos_speed = tVector();
+ last_rot_speed = tQuat();
+ last_pos_out = position;
+ last_rot_out = rotation;
+ }
+ else
+ {
+ const double dt = t.elapsed_seconds();
+ t.start();
+
+ const tVector pos_speed = (position - last_pos_in) / dt;
+ const double pos_tau = 1. / *s.pos_responsiveness;
+ double alpha = dt / (dt + pos_tau);
+ last_pos_speed += (pos_speed - last_pos_speed) * alpha;
+ const double factor_pos = min(1.0, VectorLength(last_pos_speed) / (*s.pos_drift_speed * 3.0));
+ alpha *= factor_pos * factor_pos;
+ last_pos_out += (position - last_pos_out) * alpha;
+
+ const tQuat rot_delta = QuatDivide(rotation, last_rot_in);
+ constexpr double ms_per_s = 1000.0; // angular speed quaternions need to be small to work so use °/ms
+ const tQuat rot_speed = Slerp(tQuat(), rot_delta, 1.0 / ms_per_s / dt );
+ const double rot_tau = 1. / *s.rot_responsiveness;
+ alpha = dt / (dt + rot_tau);
+ last_rot_speed = Slerp(last_rot_speed, rot_speed, alpha);
+ const double angular_speed = AngleBetween(tQuat(), last_rot_speed) * ms_per_s;
+ const double factor_rot = min(1.0, angular_speed / (*s.rot_drift_speed * 3.0));
+ alpha *= factor_rot * factor_rot;
+ last_rot_out = Slerp(last_rot_out, rotation, alpha);
+ }
+
+ last_pos_in = position;
+ last_rot_in = rotation;
+ std::copy(last_pos_out.v, last_pos_out.v + 3, output + TX);
+ QuatToYPR(last_rot_out, &output[Yaw]);
+}
+
+OPENTRACK_DECLARE_FILTER(filter_nm, dialog_nm, nmDll)
diff --git a/filter-nm/ftnoir_filter_nm.h b/filter-nm/ftnoir_filter_nm.h
new file mode 100644
index 00000000..84775207
--- /dev/null
+++ b/filter-nm/ftnoir_filter_nm.h
@@ -0,0 +1,81 @@
+/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net>
+ *
+ * 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.
+ */
+#pragma once
+
+#include "ui_ftnoir_nm_filtercontrols.h"
+
+#include "api/plugin-api.hpp"
+#include "compat/timer.hpp"
+#include "compat/hamilton-tools.h"
+#include "options/options.hpp"
+
+using namespace options;
+
+struct settings_nm : opts
+{
+ value<slider_value> pos_responsiveness;
+ value<slider_value> rot_responsiveness;
+ value<slider_value> pos_drift_speed;
+ value<slider_value> rot_drift_speed;
+
+ settings_nm() :
+ opts("nm-filter"),
+ pos_responsiveness(value<slider_value>(b, "pos-responsiveness", { 13.0, .0, 20.0 })),
+ rot_responsiveness(value<slider_value>(b, "rot-responsiveness", { 16.0, .0, 20.0 })),
+ pos_drift_speed(value<slider_value>(b, "pos-drift-speed", { 5.0, 0.1, 50.0 })),
+ rot_drift_speed(value<slider_value>(b, "rot-drift-speed", { 7.0, 0.1, 50.0 }))
+ {
+ }
+};
+
+struct filter_nm : IFilter
+{
+ filter_nm();
+ void filter(const double* input, double* output) override;
+ void center() override { first_run = true; }
+ module_status initialize() override { return status_ok(); }
+
+private:
+ tVector last_pos_in;
+ tQuat last_rot_in;
+ tVector last_pos_out;
+ tQuat last_rot_out;
+ tVector last_pos_speed;
+ tQuat last_rot_speed;
+ Timer t;
+ settings_nm s;
+ bool first_run = true;
+};
+
+class dialog_nm : public IFilterDialog
+{
+ Q_OBJECT
+public:
+ dialog_nm();
+ void register_filter(IFilter*) override {}
+ void unregister_filter() override {}
+ void save() override;
+ void reload() override;
+ bool embeddable() noexcept override { return true; }
+ void set_buttons_visible(bool x) override;
+
+private:
+ Ui::UICdialog_nm ui;
+ settings_nm s;
+
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class nmDll : public Metadata
+{
+ Q_OBJECT
+
+ QString name() override { return tr("NaturalMovement"); }
+ QIcon icon() override { return QIcon(":/images/filter-16.png"); }
+};
diff --git a/filter-nm/ftnoir_filter_nm_dialog.cpp b/filter-nm/ftnoir_filter_nm_dialog.cpp
new file mode 100644
index 00000000..f3626cae
--- /dev/null
+++ b/filter-nm/ftnoir_filter_nm_dialog.cpp
@@ -0,0 +1,56 @@
+/* Copyright (c) 2023 Tom Brazier <tom_github@firstsolo.net>
+ *
+ * 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 "ftnoir_filter_nm.h"
+
+using namespace options;
+
+dialog_nm::dialog_nm()
+{
+ ui.setupUi(this);
+
+ tie_setting(s.pos_responsiveness, ui.pos_responsiveness_slider);
+ tie_setting(s.pos_responsiveness, ui.pos_responsiveness, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+
+ tie_setting(s.rot_responsiveness, ui.rot_responsiveness_slider);
+ tie_setting(s.rot_responsiveness, ui.rot_responsiveness, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+
+ tie_setting(s.pos_drift_speed, ui.pos_drift_speed_slider);
+ tie_setting(s.pos_drift_speed, ui.pos_drift_speed, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+
+ tie_setting(s.rot_drift_speed, ui.rot_drift_speed_slider);
+ tie_setting(s.rot_drift_speed, ui.rot_drift_speed, [](double x)
+ { return QStringLiteral("%1").arg(x, 0, 'f', 2); });
+}
+
+void dialog_nm::doOK()
+{
+ save();
+ close();
+}
+
+void dialog_nm::doCancel()
+{
+ close();
+}
+
+void dialog_nm::save()
+{
+ s.b->save();
+}
+
+void dialog_nm::reload()
+{
+ s.b->reload();
+}
+
+void dialog_nm::set_buttons_visible(bool x)
+{
+ ui.buttonBox->setVisible(x);
+}
diff --git a/filter-nm/ftnoir_nm_filtercontrols.ui b/filter-nm/ftnoir_nm_filtercontrols.ui
new file mode 100644
index 00000000..7e317e74
--- /dev/null
+++ b/filter-nm/ftnoir_nm_filtercontrols.ui
@@ -0,0 +1,312 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UICdialog_nm</class>
+ <widget class="QDialog" name="UICdialog_nm">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>438</width>
+ <height>559</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Responsiveness</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Rotation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="pos_responsiveness">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>10.0</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="rot_responsiveness">
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>10.0</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSlider" name="rot_responsiveness_slider">
+ <property name="maximum">
+ <number>40</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSlider" name="pos_responsiveness_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>40</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Position</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Drift speeds</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>mm/s</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Position</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="pos_drift_speed">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>50</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QSlider" name="rot_drift_speed_slider">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Rotation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="rot_drift_speed">
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>100</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QLabel" name="label_24">
+ <property name="text">
+ <string>°/s</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QSlider" name="pos_drift_speed_slider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>500</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>pos_responsiveness_slider</tabstop>
+ <tabstop>rot_responsiveness_slider</tabstop>
+ <tabstop>pos_drift_speed_slider</tabstop>
+ <tabstop>rot_drift_speed_slider</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>UICdialog_nm</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>UICdialog_nm</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/filter-nm/lang/de_DE.ts b/filter-nm/lang/de_DE.ts
new file mode 100644
index 00000000..9cfb1a22
--- /dev/null
+++ b/filter-nm/lang/de_DE.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation>Dialog</translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation>Ansprechempfindlichkeit</translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation>Rotation</translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation>10,0</translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation>Position</translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation>Natural Movement Filter von Tom Brazier: Hebt hohes Frequenzrauschen auf und unterdrückt die natürlich Tendenz unserer Köpfe, zu driften, selbst wenn wir denken, stillzusitzen.</translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation>Drift-Geschwindigkeiten</translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation>mm/s</translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation>°/s</translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation>Anleitung: Setze alle Regler auf Minimum. Dann führe folgendes für jede Rotation und Position durch: Erhöhe zunächst die Ansprechempfindlichkeit bis der Filter geradeeben das Ruckeln bei schnellen Kopfbewegungen aufhebt. Als nächstes erhöhe die Driftgeschwindigkeit, bis der Filter knapp das Driften des Kopfes aufhebt, während du ihn nicht bewegst.</translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation>NaturalMovement</translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-nm/lang/nl_NL.ts b/filter-nm/lang/nl_NL.ts
new file mode 100644
index 00000000..540e2d51
--- /dev/null
+++ b/filter-nm/lang/nl_NL.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-nm/lang/ru_RU.ts b/filter-nm/lang/ru_RU.ts
new file mode 100644
index 00000000..31e5d5cc
--- /dev/null
+++ b/filter-nm/lang/ru_RU.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-nm/lang/stub.ts b/filter-nm/lang/stub.ts
new file mode 100644
index 00000000..3b960804
--- /dev/null
+++ b/filter-nm/lang/stub.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="stub">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/filter-nm/lang/zh_CN.ts b/filter-nm/lang/zh_CN.ts
new file mode 100644
index 00000000..341f4eee
--- /dev/null
+++ b/filter-nm/lang/zh_CN.ts
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+ <name>UICdialog_nm</name>
+ <message>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>°/s</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>mm/s</source>
+ <translation></translation>
+ </message>
+ <message>
+ <source>Responsiveness</source>
+ <translation>反应</translation>
+ </message>
+ <message>
+ <source>10.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Drift speeds</source>
+ <translation>漂移速度</translation>
+ </message>
+ <message>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Natural movement filter by Tom Brazier: Cancels higher frequency noise and the natural tendency for our heads to drift even when we think we are sitting still.</source>
+ <translation>Natural movement filter by Tom Brazier: 过滤高频噪声与我们头部的自然飘移, 即便我们认为我们保持不动.</translation>
+ </message>
+ <message>
+ <source>Rotation</source>
+ <translation>旋转</translation>
+ </message>
+ <message>
+ <source>Position</source>
+ <translation>位置</translation>
+ </message>
+ <message>
+ <source>Instructions: Set all sliders to minimum. Then for each of rotation and position: First, increase responsiveness until the filter only just cancels jerkiness for faster head movements. Second, increase drift speed until the filter only just cancels drift movement when your head is still.</source>
+ <translation>先将所有滑块最小化. 然后, 对于每个旋转和位置: 首先提高响应能力,直到过滤器只是刚好消除抖动(以获得更快的头部运动); 其次,增加漂移速度,直到过滤器只是在头部静止时刚好消除漂移. </translation>
+ </message>
+</context>
+<context>
+ <name>nmDll</name>
+ <message>
+ <source>NaturalMovement</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>