From 3a9c6f56bf76e4b9f907edfc2475fedb3dfcc806 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sun, 19 Dec 2021 01:11:29 +0100 Subject: [PATCH] Work in progress OSC Control manager Support for log, output and source targets. Now needs to be developed for all attributes. --- ControlManager.cpp | 166 ++++++++++++++++++++++++++++++++++++++++++--- ControlManager.h | 39 +++++++++++ Mixer.cpp | 10 +++ Mixer.h | 1 + MixingView.cpp | 2 +- Settings.h | 6 +- defines.h | 3 +- rsc/osc/vimix.tosc | Bin 0 -> 863 bytes 8 files changed, 213 insertions(+), 14 deletions(-) create mode 100644 rsc/osc/vimix.tosc diff --git a/ControlManager.cpp b/ControlManager.cpp index 67fa2f3..9f5db54 100644 --- a/ControlManager.cpp +++ b/ControlManager.cpp @@ -19,12 +19,15 @@ #include #include +#include #include "osc/OscOutboundPacketStream.h" #include "Log.h" #include "Settings.h" #include "BaseToolkit.h" +#include "Mixer.h" +#include "Source.h" #include "ControlManager.h" @@ -32,31 +35,88 @@ #define CONTROL_DEBUG #endif +#define CONTROL_OSC_MSG "OSC: " void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m, - const IpEndpointName& remoteEndpoint ) + const IpEndpointName& remoteEndpoint ) { char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH]; remoteEndpoint.AddressAndPortAsString(sender); try{ - #ifdef CONTROL_DEBUG - Log::Info("%s sent an OSC message %s.", sender, m.AddressPattern()); + Log::Info(CONTROL_OSC_MSG "received '%s' from %s", m.AddressPattern(), sender); #endif + // TODO Preprocessing with Translator - std::list address = BaseToolkit::splitted(m.AddressPattern(), '/'); - + // structured OSC address + std::list address = BaseToolkit::splitted(m.AddressPattern(), 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') + // if (address.size() == 3 && address.front().compare(APP_NAME) == 0 ){ + // done with the first part of the OSC address address.pop_front(); - Log::Info("Wellformed vimix message %s.", address.front().c_str()); + // execute next part of the OSC message + std::string target = address.front(); + std::string attribute = address.back(); + // Log target + if ( target.compare(OSC_LOG) == 0 ) + { + if ( attribute.compare(OSC_LOG_INFO) == 0) + Log::Info(CONTROL_OSC_MSG "received '%s' from %s", m.AddressPattern(), sender); + } + // Output target + else if ( target.compare(OSC_OUTPUT) == 0 ) + { + Control::manager().setOutputAttribute(attribute, m.ArgumentStream()); + } + // Current source target + else if ( target.compare(OSC_CURRENT) == 0 ) + { + // attributes to change current + if ( attribute.compare(OSC_CURRENT_NONE) == 0) + Mixer::manager().unsetCurrentSource(); + else if ( attribute.compare(OSC_CURRENT_NEXT) == 0) + Mixer::manager().setCurrentNext(); + else if ( attribute.compare(OSC_CURRENT_PREVIOUS) == 0) + Mixer::manager().setCurrentPrevious(); + // all other attributes operate on current source + else + Control::manager().setSourceAttribute( Mixer::manager().currentSource(), attribute, m.ArgumentStream()); + } + // General case: try to identify the target + else { + // try to find source by name + Source *s = Mixer::manager().findSource(target); + // if failed, try to find source by index + if (s == nullptr) { + int N = -1; + try { + N = std::stoi(target); + } catch (const std::invalid_argument&) { + N = -1; + } + if (N>=0) + s = Mixer::manager().sourceAtIndex(N); + } + if (s) + Control::manager().setSourceAttribute(s, attribute, m.ArgumentStream()); + else + Log::Info(CONTROL_OSC_MSG "Unknown target '%s' requested by %s.", target.c_str(), sender); + } } - +#ifdef CONTROL_DEBUG + else { + Log::Info(CONTROL_OSC_MSG "Ignoring malformed message '%s' from %s.", m.AddressPattern(), sender); + } +#endif } catch( osc::Exception& e ){ // any parsing errors such as unexpected argument types, or // missing arguments get thrown as exceptions. - Log::Info("error while parsing message '%s' from %s : %s", m.AddressPattern(), sender, e.what()); + Log::Info(CONTROL_OSC_MSG "Ignoring error in message '%s' from %s : %s", m.AddressPattern(), sender, e.what()); } } @@ -64,7 +124,7 @@ void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m, void Control::listen() { #ifdef CONTROL_DEBUG - Log::Info("Accepting OSC messages on port %d", Settings::application.control.osc_port); + 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(); @@ -89,7 +149,7 @@ bool Control::init() // try to create listenning socket // through exception runtime if fails receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS, - Settings::application.control.osc_port ), &listener_ ); + Settings::application.control.osc_port_receive ), &listener_ ); } catch (const std::runtime_error&) { // arg, the receiver could not be initialized @@ -108,3 +168,89 @@ void Control::terminate() if (receiver_!=nullptr) receiver_->AsynchronousBreak(); } + +void Control::setOutputAttribute(const std::string &attribute, + osc::ReceivedMessageArgumentStream arguments) +{ + try { + /// '/vimix/output/enable' or '/vimix/output/enable T' or '/vimix/output/enable F' + if ( attribute.compare(OSC_OUTPUT_ENABLE) == 0) { + bool on = true; + if ( !arguments.Eos()) { + arguments >> on >> osc::EndMessage; + } + Settings::application.render.disabled = !on; + } + /// '/vimix/output/disable' or '/vimix/output/disable T' or '/vimix/output/disable F' + else if ( attribute.compare(OSC_OUTPUT_DISABLE) == 0) { + bool on = true; + if ( !arguments.Eos()) { + arguments >> on >> osc::EndMessage; + } + Settings::application.render.disabled = on; + } + /// '/vimix/output/fading f 0.2' + else if ( attribute.compare(OSC_OUTPUT_FADING) == 0) { + float fading = 0.f; + arguments >> fading >> osc::EndMessage; + Mixer::manager().session()->setFading(fading); // TODO move cursor when in Mixing view + } +#ifdef CONTROL_DEBUG + else { + Log::Info(CONTROL_OSC_MSG "Ignoring attribute '%s' for target 'output'", attribute.c_str()); + } +#endif + + } + catch (osc::MissingArgumentException &e) { + Log::Info(CONTROL_OSC_MSG "Missing argument for attribute '%s' for target 'output'", attribute.c_str()); + } + catch (osc::ExcessArgumentException &e) { + Log::Info(CONTROL_OSC_MSG "Too many arguments for attribute '%s' for target 'output'", attribute.c_str()); + } + catch (osc::WrongArgumentTypeException &e) { + Log::Info(CONTROL_OSC_MSG "Invalid argument for attribute '%s' for target 'output'", attribute.c_str()); + } +} + +void Control::setSourceAttribute(Source *target, const std::string &attribute, + osc::ReceivedMessageArgumentStream arguments) +{ + if (target == nullptr) + return; + + try { + /// '/vimix/current/play' or '/vimix/current/play T' or '/vimix/current/play F' + if ( attribute.compare(OSC_SOURCE_PLAY) == 0) { + bool on = true; + if ( !arguments.Eos()) { + arguments >> on >> osc::EndMessage; + } + target->play(on); + } + /// '/vimix/current/pause' or '/vimix/current/pause T' or '/vimix/current/pause F' + else if ( attribute.compare(OSC_SOURCE_PAUSE) == 0) { + bool on = true; + if ( !arguments.Eos()) { + arguments >> on >> osc::EndMessage; + } + target->play(!on); + } +#ifdef CONTROL_DEBUG + else { + Log::Info(CONTROL_OSC_MSG "Ignoring attribute '%s' for target %s.", attribute.c_str(), target->name().c_str()); + } +#endif + + } + catch (osc::MissingArgumentException &e) { + Log::Info(CONTROL_OSC_MSG "Missing argument for attribute '%s' for target %s.", attribute.c_str(), target->name().c_str()); + } + catch (osc::ExcessArgumentException &e) { + Log::Info(CONTROL_OSC_MSG "Too many arguments for attribute '%s' for target %s.", attribute.c_str(), target->name().c_str()); + } + catch (osc::WrongArgumentTypeException &e) { + Log::Info(CONTROL_OSC_MSG "Invalid argument for attribute '%s' for target %s.", attribute.c_str(), target->name().c_str()); + } +} + diff --git a/ControlManager.h b/ControlManager.h index 1d78df0..12b0656 100644 --- a/ControlManager.h +++ b/ControlManager.h @@ -3,6 +3,39 @@ #include "NetworkToolkit.h" +#define OSC_SEPARATOR '/' + +#define OSC_LOG "log" +#define OSC_LOG_INFO "info" + +#define OSC_OUTPUT "output" +#define OSC_OUTPUT_ENABLE "enable" +#define OSC_OUTPUT_DISABLE "disable" +#define OSC_OUTPUT_FADING "fading" + +#define OSC_CURRENT "current" +#define OSC_CURRENT_NONE "none" +#define OSC_CURRENT_NEXT "next" +#define OSC_CURRENT_PREVIOUS "previous" + +#define OSC_SOURCE_PLAY "play" +#define OSC_SOURCE_PAUSE "pause" +#define OSC_SOURCE_ALPHA "alpha" +#define OSC_SOURCE_ALPHA_XY "alphaXY" +#define OSC_SOURCE_ALPHA_X "alphaX" +#define OSC_SOURCE_ALPHA_Y "alphaY" +#define OSC_SOURCE_TRANSPARENT "transparency" +#define OSC_SOURCE_POSITION "position" +#define OSC_SOURCE_POSITION_X "positionX" +#define OSC_SOURCE_POSITION_Y "positionY" +#define OSC_SOURCE_SCALE "scale" +#define OSC_SOURCE_SCALE_X "scaleX" +#define OSC_SOURCE_SCALE_Y "scaleY" +#define OSC_SOURCE_ANGLE "angle" + +class Session; +class Source; + class Control { // Private Constructor @@ -33,6 +66,12 @@ protected: const IpEndpointName& remoteEndpoint ); }; + void setOutputAttribute(const std::string &attribute, + osc::ReceivedMessageArgumentStream arguments); + + void setSourceAttribute(Source *target, const std::string &attribute, + osc::ReceivedMessageArgumentStream arguments); + private: static void listen(); diff --git a/Mixer.cpp b/Mixer.cpp index 6477e3b..ddd74ac 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -831,6 +831,16 @@ void Mixer::setCurrentSource(Source *s) setCurrentSource( session_->find(s) ); } + +Source *Mixer::sourceAtIndex (int index) +{ + SourceList::iterator s = session_->at(index); + if (s!=session_->end()) + return *s; + else + return nullptr; +} + void Mixer::setCurrentIndex(int index) { setCurrentSource( session_->at(index) ); diff --git a/Mixer.h b/Mixer.h index 126759a..a22c354 100644 --- a/Mixer.h +++ b/Mixer.h @@ -77,6 +77,7 @@ public: void setCurrentPrevious (); void unsetCurrentSource (); + Source *sourceAtIndex (int index); void setCurrentIndex (int index); void moveIndex (int current_index, int target_index); int indexCurrentSource (); diff --git a/MixingView.cpp b/MixingView.cpp index 19a5c68..55f97dd 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -473,7 +473,7 @@ View::Cursor MixingView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pai // cursor feedback slider_->color = glm::vec4( COLOR_CIRCLE_OVER, 0.9f ); std::ostringstream info; - info << "Global opacity " << 100 - int(Mixer::manager().session()->fading() * 100.0) << " %"; + info << "Output " << 100 - int(Mixer::manager().session()->fading() * 100.0) << " %"; return Cursor(Cursor_Hand, info.str() ); } else if (pick.first == limbo_slider_) { diff --git a/Settings.h b/Settings.h index 7b699b7..2b6051b 100644 --- a/Settings.h +++ b/Settings.h @@ -191,10 +191,12 @@ struct TimerConfig struct ControllerConfig { - int osc_port; + int osc_port_receive; + int osc_port_send; ControllerConfig() { - osc_port = OSC_DEFAULT_PORT; + osc_port_receive = OSC_PORT_RECV_DEFAULT; + osc_port_send = OSC_PORT_SEND_DEFAULT; } }; diff --git a/defines.h b/defines.h index 7a80e1a..43462f9 100644 --- a/defines.h +++ b/defines.h @@ -105,6 +105,7 @@ #define USE_GST_APPSINK_CALLBACKS -#define OSC_DEFAULT_PORT 7000 +#define OSC_PORT_RECV_DEFAULT 7000 +#define OSC_PORT_SEND_DEFAULT 3000 #endif // VMIX_DEFINES_H diff --git a/rsc/osc/vimix.tosc b/rsc/osc/vimix.tosc new file mode 100644 index 0000000000000000000000000000000000000000..bce387c5778398d92076a12a46f9fdb070c4255d GIT binary patch literal 863 zcmV-l1EBnPoaI>2ZrU&u{T24a6H7u1rIK+)fp(QTKr~F7sy-OU1gjxNwnO3XXPYEI z!aAB})28x3uCL>BkL{CV0=QZ)g35|;L0Q$siADZDY3590)LOdjVj652NBFuRfaK_PstK0+%;ROsolbjw-fudO` z#wiIUl1kX6P-Bq{XqcPkDLbC$w#9moo1oCTm1yjt?ve0yMOShte zPV+N8ie>Fvi0KyTBXIyKS%l|1$gOhpOyeld^vDcFf$T<{D>>}?GkM@`+j~zX3dELj z+7$<*C74ojf)o|ALP-cb zB#~p*%mk)gPug`=A;rz8GAZ-mb_Q-z;b8xF+b=K*$X6O_+%%4}NFADuB&`}*Ng3d~ zL9hS)okf0E<6Gx`(mz^beR`4l#j!G}^sKGon80t5o&68XZi(bve=F-k!7o&yFycQH z;DbB98{8V$B$YkqtrcC+wWSWlWh5<+3Y3^=eB+xoI%Q^UnIi5-3*{*yOZs5@_vl>@ phxh%?;8n`&66oy_vy48holjZ%g#qfPHhF_cGUo3Z_!C-RF&6{Zsb&BG literal 0 HcmV?d00001