mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Implementation of OSC settings and translator
Translations are in a config xml file in settings directory, and can be directly edited in text by the user. Settings UI allows changing Ports for incoming and outgoing UDP.
This commit is contained in:
@@ -24,12 +24,15 @@
|
||||
|
||||
#include "osc/OscOutboundPacketStream.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "Settings.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "Mixer.h"
|
||||
#include "Source.h"
|
||||
#include "ActionManager.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "tinyxml2Toolkit.h"
|
||||
|
||||
#include "ControlManager.h"
|
||||
|
||||
@@ -50,10 +53,11 @@ void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
|
||||
#ifdef CONTROL_DEBUG
|
||||
Log::Info(CONTROL_OSC_MSG "received '%s' from %s", FullMessage(m).c_str(), sender);
|
||||
#endif
|
||||
// TODO Preprocessing with Translator
|
||||
// Preprocessing with Translator
|
||||
std::string address_pattern = Control::manager().translate(m.AddressPattern());
|
||||
|
||||
// structured OSC address
|
||||
std::list<std::string> address = BaseToolkit::splitted(m.AddressPattern(), OSC_SEPARATOR);
|
||||
std::list<std::string> address = BaseToolkit::splitted(address_pattern, OSC_SEPARATOR);
|
||||
//
|
||||
// A wellformed OSC address is in the form '/vimix/target/attribute {arguments}'
|
||||
// First test: should have 3 elements and start with APP_NAME ('vimix')
|
||||
@@ -224,15 +228,6 @@ std::string Control::RequestListener::FullMessage( const osc::ReceivedMessage& m
|
||||
return message.str();
|
||||
}
|
||||
|
||||
void Control::listen()
|
||||
{
|
||||
#ifdef CONTROL_DEBUG
|
||||
Log::Info(CONTROL_OSC_MSG "Accepting messages on port %d", Settings::application.control.osc_port_receive);
|
||||
#endif
|
||||
if (Control::manager().receiver_)
|
||||
Control::manager().receiver_->Run();
|
||||
}
|
||||
|
||||
|
||||
Control::Control() : receiver_(nullptr)
|
||||
{
|
||||
@@ -240,37 +235,151 @@ Control::Control() : receiver_(nullptr)
|
||||
|
||||
Control::~Control()
|
||||
{
|
||||
if (receiver_!=nullptr) {
|
||||
receiver_->Break();
|
||||
delete receiver_;
|
||||
terminate();
|
||||
}
|
||||
|
||||
|
||||
std::string Control::translate (std::string addresspattern)
|
||||
{
|
||||
std::string translation = addresspattern;
|
||||
|
||||
auto it_translation = translation_.find(addresspattern);
|
||||
if ( it_translation != translation_.end() )
|
||||
translation = it_translation->second;
|
||||
|
||||
return translation;
|
||||
}
|
||||
|
||||
bool Control::configOscLoad()
|
||||
{
|
||||
// reset translations
|
||||
translation_.clear();
|
||||
|
||||
// load osc config file
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
tinyxml2::XMLError eResult = xmlDoc.LoadFile(Settings::application.control.osc_filename.c_str());
|
||||
|
||||
// found the file & managed to open it
|
||||
if (eResult == tinyxml2::XML_SUCCESS) {
|
||||
// parse all entries 'osc'
|
||||
tinyxml2::XMLElement* osc = xmlDoc.FirstChildElement("osc");
|
||||
for( ; osc ; osc=osc->NextSiblingElement()) {
|
||||
// get the 'from' entry
|
||||
tinyxml2::XMLElement* from = osc->FirstChildElement("from");
|
||||
const char *str_from = from->GetText();
|
||||
// get the 'to' entry
|
||||
tinyxml2::XMLElement* to = osc->FirstChildElement("to");
|
||||
const char *str_to = to->GetText();
|
||||
// if could get both, add to translator
|
||||
if (str_from && str_to)
|
||||
translation_[str_from] = str_to;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Control::configOscReset()
|
||||
{
|
||||
// create and save a new configOscFilename_
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
tinyxml2::XMLDeclaration *pDec = xmlDoc.NewDeclaration();
|
||||
xmlDoc.InsertFirstChild(pDec);
|
||||
tinyxml2::XMLComment *pComment = xmlDoc.NewComment("Complete the OSC message translator by adding as many <osc> blocs as you want.\n"
|
||||
"Each <osc> should contain one <from> osc address to translate into a <to> osc address.");
|
||||
xmlDoc.InsertEndChild(pComment);
|
||||
|
||||
tinyxml2::XMLElement *osc = xmlDoc.NewElement("osc");
|
||||
|
||||
tinyxml2::XMLElement *from = xmlDoc.NewElement( "from" );
|
||||
tinyxml2::XMLText *text = xmlDoc.NewText("/example/osc/message");
|
||||
from->InsertEndChild(text);
|
||||
osc->InsertEndChild(from);
|
||||
|
||||
tinyxml2::XMLElement *to = xmlDoc.NewElement( "to" );
|
||||
text = xmlDoc.NewText("/vimix/log/info");
|
||||
to->InsertEndChild(text);
|
||||
osc->InsertEndChild(to);
|
||||
|
||||
xmlDoc.InsertEndChild(osc);
|
||||
xmlDoc.SaveFile(Settings::application.control.osc_filename.c_str());
|
||||
|
||||
// reset and fill translation with default example
|
||||
translation_.clear();
|
||||
translation_["/example/osc/message"] = "/vimix/log/info";
|
||||
}
|
||||
|
||||
bool Control::init()
|
||||
{
|
||||
//
|
||||
// terminate before init (allows calling init() multiple times)
|
||||
//
|
||||
terminate();
|
||||
|
||||
//
|
||||
// load OSC Translator
|
||||
//
|
||||
if (configOscLoad())
|
||||
Log::Info(CONTROL_OSC_MSG "Loaded %d translations.", translation_.size());
|
||||
else
|
||||
configOscReset();
|
||||
|
||||
//
|
||||
// launch OSC listener
|
||||
//
|
||||
try {
|
||||
// try to create listenning socket
|
||||
// through exception runtime if fails
|
||||
receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS,
|
||||
Settings::application.control.osc_port_receive ), &listener_ );
|
||||
}
|
||||
catch (const std::runtime_error&) {
|
||||
// arg, the receiver could not be initialized
|
||||
// because the port was not available
|
||||
receiver_ = nullptr;
|
||||
}
|
||||
|
||||
// listen for answers
|
||||
// listen for answers in a separate thread
|
||||
std::thread(listen).detach();
|
||||
|
||||
// inform user
|
||||
IpEndpointName ip = receiver_->LocalEndpointFor( IpEndpointName( NetworkToolkit::hostname().c_str(),
|
||||
Settings::application.control.osc_port_receive ));
|
||||
char *addresseip = (char *)malloc(IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH);
|
||||
ip.AddressAndPortAsString(addresseip);
|
||||
|
||||
Log::Info(CONTROL_OSC_MSG "Listening to UDP on %s", addresseip);
|
||||
}
|
||||
catch (const std::runtime_error &e) {
|
||||
// arg, the receiver could not be initialized
|
||||
// (often because the port was not available)
|
||||
receiver_ = nullptr;
|
||||
Log::Warning(CONTROL_OSC_MSG "Failed to init listener on port %d; %s", Settings::application.control.osc_port_receive, e.what());
|
||||
}
|
||||
|
||||
return receiver_ != nullptr;
|
||||
}
|
||||
|
||||
void Control::listen()
|
||||
{
|
||||
if (Control::manager().receiver_)
|
||||
Control::manager().receiver_->Run();
|
||||
|
||||
Control::manager().receiver_end_.notify_all();
|
||||
}
|
||||
|
||||
void Control::terminate()
|
||||
{
|
||||
if (receiver_!=nullptr)
|
||||
if ( receiver_ != nullptr ) {
|
||||
|
||||
// request termination of receiver
|
||||
receiver_->AsynchronousBreak();
|
||||
|
||||
// wait for the receiver_end_ notification
|
||||
std::mutex mtx;
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
// if waited more than 2 seconds, its dead :(
|
||||
if ( receiver_end_.wait_for(lck,std::chrono::seconds(2)) == std::cv_status::timeout)
|
||||
Log::Warning(CONTROL_OSC_MSG "Failed to terminate.");
|
||||
|
||||
// delete receiver and ready to initialize
|
||||
delete receiver_;
|
||||
receiver_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef CONTROL_H
|
||||
#define CONTROL_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <condition_variable>
|
||||
#include "NetworkToolkit.h"
|
||||
|
||||
#define OSC_SYNC "/sync"
|
||||
@@ -61,7 +64,7 @@ public:
|
||||
bool init();
|
||||
void terminate();
|
||||
|
||||
// void setOscPort(int P);
|
||||
std::string translate (std::string addresspattern);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -89,8 +92,13 @@ private:
|
||||
|
||||
static void listen();
|
||||
RequestListener listener_;
|
||||
std::condition_variable receiver_end_;
|
||||
UdpListeningReceiveSocket *receiver_;
|
||||
|
||||
std::map<std::string, std::string> translation_;
|
||||
bool configOscLoad();
|
||||
void configOscReset();
|
||||
|
||||
};
|
||||
|
||||
#endif // CONTROL_H
|
||||
|
||||
24
Settings.cpp
24
Settings.cpp
@@ -260,6 +260,12 @@ void Settings::Save(uint64_t runtime)
|
||||
timerConfNode->SetAttribute("stopwatch_duration", application.timer.stopwatch_duration);
|
||||
pRoot->InsertEndChild(timerConfNode);
|
||||
|
||||
// Controller
|
||||
XMLElement *controlConfNode = xmlDoc.NewElement( "Control" );
|
||||
controlConfNode->SetAttribute("osc_port_receive", application.control.osc_port_receive);
|
||||
controlConfNode->SetAttribute("osc_port_send", application.control.osc_port_send);
|
||||
|
||||
|
||||
// First save : create filename
|
||||
if (settingsFilename.empty())
|
||||
settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS);
|
||||
@@ -302,9 +308,12 @@ void Settings::Load()
|
||||
// impose C locale for all app
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
XMLDocument xmlDoc;
|
||||
if (settingsFilename.empty())
|
||||
// set filenames from settings path
|
||||
application.control.osc_filename = SystemToolkit::full_filename(SystemToolkit::settings_path(), OSC_CONFIG_FILE);
|
||||
settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS);
|
||||
|
||||
// try to load settings file
|
||||
XMLDocument xmlDoc;
|
||||
XMLError eResult = xmlDoc.LoadFile(settingsFilename.c_str());
|
||||
|
||||
// do not warn if non existing file
|
||||
@@ -314,8 +323,10 @@ void Settings::Load()
|
||||
else if (XMLResultError(eResult))
|
||||
return;
|
||||
|
||||
// first element should be called by the application name
|
||||
XMLElement *pRoot = xmlDoc.FirstChildElement(application.name.c_str());
|
||||
if (pRoot == nullptr) return;
|
||||
if (pRoot == nullptr)
|
||||
return;
|
||||
|
||||
// cancel on different root name
|
||||
if (application.name.compare( string( pRoot->Value() ) ) != 0 )
|
||||
@@ -535,6 +546,13 @@ void Settings::Load()
|
||||
timerconfnode->QueryUnsigned64Attribute("stopwatch_duration", &application.timer.stopwatch_duration);
|
||||
}
|
||||
|
||||
// bloc Controller
|
||||
XMLElement *controlconfnode = pRoot->FirstChildElement("Control");
|
||||
if (controlconfnode != nullptr) {
|
||||
controlconfnode->QueryIntAttribute("osc_port_receive", &application.control.osc_port_receive);
|
||||
controlconfnode->QueryIntAttribute("osc_port_send", &application.control.osc_port_send);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Settings::History::push(const string &filename)
|
||||
|
||||
@@ -193,6 +193,7 @@ struct ControllerConfig
|
||||
{
|
||||
int osc_port_receive;
|
||||
int osc_port_send;
|
||||
std::string osc_filename;
|
||||
|
||||
ControllerConfig() {
|
||||
osc_port_receive = OSC_PORT_RECV_DEFAULT;
|
||||
|
||||
@@ -97,10 +97,10 @@ void Loom::update(Source *s, float dt)
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
// calculate amplitude of movement
|
||||
// time passed
|
||||
progress_ += dt;
|
||||
|
||||
// perform movement
|
||||
// move target by speed vector (in the direction of step_, amplitude of speed * time (in second))
|
||||
pos_ += step_ * ( speed_ * dt * 0.001f);
|
||||
|
||||
// apply alpha if valid in range [0 1]
|
||||
@@ -108,7 +108,7 @@ void Loom::update(Source *s, float dt)
|
||||
if ( alpha > DELTA_ALPHA && alpha < 1.0 - DELTA_ALPHA )
|
||||
s->group(View::MIXING)->translation_ = glm::vec3(pos_, s->group(View::MIXING)->translation_.z);
|
||||
|
||||
// timeout
|
||||
// time-out
|
||||
if ( progress_ > duration_ ) {
|
||||
// done
|
||||
finished_ = true;
|
||||
@@ -135,14 +135,14 @@ void SetDepth::update(Source *s, float dt)
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
// calculate amplitude of movement
|
||||
// time passed
|
||||
progress_ += dt;
|
||||
|
||||
// perform movement
|
||||
if ( ABS(duration_) > 0.f)
|
||||
s->group(View::LAYER)->translation_.z = start_ + (progress_/duration_) * (target_ - start_);
|
||||
|
||||
// end of movement
|
||||
// time-out
|
||||
if ( progress_ > duration_ ) {
|
||||
// apply depth to target
|
||||
s->group(View::LAYER)->translation_.z = target_;
|
||||
@@ -203,14 +203,14 @@ void Grab::update(Source *s, float dt)
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
// calculate amplitude of movement
|
||||
// time passed
|
||||
progress_ += dt;
|
||||
|
||||
// perform movement
|
||||
// move target by speed vector * time (in second)
|
||||
glm::vec2 pos = start_ + speed_ * ( dt * 0.001f);
|
||||
s->group(View::GEOMETRY)->translation_ = glm::vec3(pos, s->group(View::GEOMETRY)->translation_.z);
|
||||
|
||||
// timeout
|
||||
// time-out
|
||||
if ( progress_ > duration_ ) {
|
||||
// done
|
||||
finished_ = true;
|
||||
@@ -237,14 +237,14 @@ void Resize::update(Source *s, float dt)
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
// calculate amplitude of movement
|
||||
// time passed
|
||||
progress_ += dt;
|
||||
|
||||
// perform movement
|
||||
// move target by speed vector * time (in second)
|
||||
glm::vec2 scale = start_ + speed_ * ( dt * 0.001f);
|
||||
s->group(View::GEOMETRY)->scale_ = glm::vec3(scale, s->group(View::GEOMETRY)->scale_.z);
|
||||
|
||||
// timeout
|
||||
// time-out
|
||||
if ( progress_ > duration_ ) {
|
||||
// done
|
||||
finished_ = true;
|
||||
|
||||
@@ -62,6 +62,7 @@ using namespace std;
|
||||
#include "ImGuiToolkit.h"
|
||||
#include "ImGuiVisitor.h"
|
||||
#include "RenderingManager.h"
|
||||
#include "ControlManager.h"
|
||||
#include "Connection.h"
|
||||
#include "ActionManager.h"
|
||||
#include "Resource.h"
|
||||
@@ -5409,7 +5410,7 @@ void Navigator::RenderMainPannelSettings()
|
||||
// Recording preferences
|
||||
//
|
||||
ImGuiToolkit::Spacing();
|
||||
ImGui::Text("Recording");
|
||||
ImGui::Text("Record");
|
||||
|
||||
// select CODEC and FPS
|
||||
ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN);
|
||||
@@ -5436,22 +5437,62 @@ void Navigator::RenderMainPannelSettings()
|
||||
ImGui::SliderInt("Buffer", &Settings::application.record.buffering_mode, 0, IM_ARRAYSIZE(VideoRecorder::buffering_preset_name)-1,
|
||||
VideoRecorder::buffering_preset_name[Settings::application.record.buffering_mode]);
|
||||
|
||||
ImGuiToolkit::HelpMarker("Priority when buffer is full and recorder skips frames;\n"
|
||||
ICON_FA_CARET_RIGHT " Clock: variable framerate, correct duration.\n"
|
||||
ICON_FA_CARET_RIGHT " Framerate: correct framerate, shorter duration.");
|
||||
ImGuiToolkit::HelpMarker("Priority when buffer is full and recorder has to skip frames;\n"
|
||||
ICON_FA_CARET_RIGHT " Duration:\n Variable framerate, correct duration.\n"
|
||||
ICON_FA_CARET_RIGHT " Framerate:\n Correct framerate, shorter duration.");
|
||||
ImGui::SameLine(0);
|
||||
ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::Combo("Priority", &Settings::application.record.priority_mode, "Clock\0Framerate\0");
|
||||
ImGui::Combo("Priority", &Settings::application.record.priority_mode, "Duration\0Framerate\0");
|
||||
|
||||
//
|
||||
// OSC preferences
|
||||
//
|
||||
ImGuiToolkit::Spacing();
|
||||
ImGui::Text("OSC");
|
||||
|
||||
char msg[256];
|
||||
sprintf(msg, "You can send OSC messages via UDP to the IP address %s", NetworkToolkit::host_ips()[1].c_str());
|
||||
ImGuiToolkit::HelpMarker(msg, ICON_FA_INFO_CIRCLE);
|
||||
ImGui::SameLine(0);
|
||||
|
||||
ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
char bufreceive[7] = "";
|
||||
sprintf(bufreceive, "%d", Settings::application.control.osc_port_receive);
|
||||
ImGui::InputTextWithHint("Port in", "UDP Port to receive", bufreceive, 7, ImGuiInputTextFlags_CharsDecimal);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
if ( BaseToolkit::is_a_number(bufreceive, &Settings::application.control.osc_port_receive))
|
||||
Control::manager().init();
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
char bufsend[7] = "";
|
||||
sprintf(bufsend, "%d", Settings::application.control.osc_port_send);
|
||||
ImGui::InputTextWithHint("Port out", "UDP Port to send", bufsend, 7, ImGuiInputTextFlags_CharsDecimal);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
if ( BaseToolkit::is_a_number(bufsend, &Settings::application.control.osc_port_send))
|
||||
Control::manager().init();
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN);
|
||||
const float w = IMGUI_RIGHT_ALIGN - ImGui::GetFrameHeightWithSpacing();
|
||||
ImGuiToolkit::ButtonOpenUrl( "Edit", Settings::application.control.osc_filename.c_str(), ImVec2(w, 0) );
|
||||
ImGui::SameLine(0, 6);
|
||||
if ( ImGuiToolkit::IconButton(5, 15, "Reload") )
|
||||
Control::manager().init();
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Translator");
|
||||
|
||||
//
|
||||
// System preferences
|
||||
//
|
||||
ImGuiToolkit::Spacing();
|
||||
// ImGuiToolkit::HelpMarker("If you encounter some rendering issues on your machine, "
|
||||
// "you can try to disable some of the OpenGL optimizations below.");
|
||||
// ImGui::SameLine();
|
||||
ImGui::Text("System");
|
||||
ImGui::SameLine( ImGui::GetContentRegionAvailWidth() IMGUI_RIGHT_ALIGN * 0.8);
|
||||
ImGuiToolkit::HelpMarker("If you encounter some rendering issues on your machine, "
|
||||
"you can try to disable some of the OpenGL optimizations below.");
|
||||
|
||||
static bool need_restart = false;
|
||||
static bool vsync = (Settings::application.render.vsync > 0);
|
||||
@@ -5463,10 +5504,10 @@ void Navigator::RenderMainPannelSettings()
|
||||
change |= ImGuiToolkit::ButtonSwitch( "Blit framebuffer", &blit);
|
||||
change |= ImGuiToolkit::ButtonSwitch( "Antialiasing framebuffer", &multi);
|
||||
// hardware support deserves more explanation
|
||||
ImGuiToolkit::HelpMarker("If enabled, tries to find a platform adapted hardware accelerated "
|
||||
ImGuiToolkit::HelpMarker("If enabled, tries to find a platform adapted hardware-accelerated "
|
||||
"driver to decode (read) or encode (record) videos.", ICON_FA_MICROCHIP);
|
||||
ImGui::SameLine(0);
|
||||
change |= ImGuiToolkit::ButtonSwitch( "Hardware video de/encoding", &gpu);
|
||||
change |= ImGuiToolkit::ButtonSwitch( "Hardware video en/decoding", &gpu);
|
||||
|
||||
if (change) {
|
||||
need_restart = ( vsync != (Settings::application.render.vsync > 0) ||
|
||||
|
||||
Reference in New Issue
Block a user