Work in progress OSC Control manager

Support for log, output and source targets. Now needs to be developed for all attributes.
This commit is contained in:
Bruno Herbelin
2021-12-19 01:11:29 +01:00
parent a612154123
commit 3a9c6f56bf
8 changed files with 213 additions and 14 deletions

View File

@@ -19,12 +19,15 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <sstream>
#include "osc/OscOutboundPacketStream.h" #include "osc/OscOutboundPacketStream.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
#include "BaseToolkit.h" #include "BaseToolkit.h"
#include "Mixer.h"
#include "Source.h"
#include "ControlManager.h" #include "ControlManager.h"
@@ -32,31 +35,88 @@
#define CONTROL_DEBUG #define CONTROL_DEBUG
#endif #endif
#define CONTROL_OSC_MSG "OSC: "
void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m, void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint ) const IpEndpointName& remoteEndpoint )
{ {
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH]; char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];
remoteEndpoint.AddressAndPortAsString(sender); remoteEndpoint.AddressAndPortAsString(sender);
try{ try{
#ifdef CONTROL_DEBUG #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 #endif
// TODO Preprocessing with Translator
std::list<std::string> address = BaseToolkit::splitted(m.AddressPattern(), '/'); // structured OSC address
std::list<std::string> 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 ){ if (address.size() == 3 && address.front().compare(APP_NAME) == 0 ){
// done with the first part of the OSC address
address.pop_front(); 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 ){ catch( osc::Exception& e ){
// any parsing errors such as unexpected argument types, or // any parsing errors such as unexpected argument types, or
// missing arguments get thrown as exceptions. // 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() void Control::listen()
{ {
#ifdef CONTROL_DEBUG #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 #endif
if (Control::manager().receiver_) if (Control::manager().receiver_)
Control::manager().receiver_->Run(); Control::manager().receiver_->Run();
@@ -89,7 +149,7 @@ bool Control::init()
// try to create listenning socket // try to create listenning socket
// through exception runtime if fails // through exception runtime if fails
receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS, 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&) { catch (const std::runtime_error&) {
// arg, the receiver could not be initialized // arg, the receiver could not be initialized
@@ -108,3 +168,89 @@ void Control::terminate()
if (receiver_!=nullptr) if (receiver_!=nullptr)
receiver_->AsynchronousBreak(); 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());
}
}

View File

@@ -3,6 +3,39 @@
#include "NetworkToolkit.h" #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 class Control
{ {
// Private Constructor // Private Constructor
@@ -33,6 +66,12 @@ protected:
const IpEndpointName& remoteEndpoint ); const IpEndpointName& remoteEndpoint );
}; };
void setOutputAttribute(const std::string &attribute,
osc::ReceivedMessageArgumentStream arguments);
void setSourceAttribute(Source *target, const std::string &attribute,
osc::ReceivedMessageArgumentStream arguments);
private: private:
static void listen(); static void listen();

View File

@@ -831,6 +831,16 @@ void Mixer::setCurrentSource(Source *s)
setCurrentSource( session_->find(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) void Mixer::setCurrentIndex(int index)
{ {
setCurrentSource( session_->at(index) ); setCurrentSource( session_->at(index) );

View File

@@ -77,6 +77,7 @@ public:
void setCurrentPrevious (); void setCurrentPrevious ();
void unsetCurrentSource (); void unsetCurrentSource ();
Source *sourceAtIndex (int index);
void setCurrentIndex (int index); void setCurrentIndex (int index);
void moveIndex (int current_index, int target_index); void moveIndex (int current_index, int target_index);
int indexCurrentSource (); int indexCurrentSource ();

View File

@@ -473,7 +473,7 @@ View::Cursor MixingView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pai
// cursor feedback // cursor feedback
slider_->color = glm::vec4( COLOR_CIRCLE_OVER, 0.9f ); slider_->color = glm::vec4( COLOR_CIRCLE_OVER, 0.9f );
std::ostringstream info; 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() ); return Cursor(Cursor_Hand, info.str() );
} }
else if (pick.first == limbo_slider_) { else if (pick.first == limbo_slider_) {

View File

@@ -191,10 +191,12 @@ struct TimerConfig
struct ControllerConfig struct ControllerConfig
{ {
int osc_port; int osc_port_receive;
int osc_port_send;
ControllerConfig() { ControllerConfig() {
osc_port = OSC_DEFAULT_PORT; osc_port_receive = OSC_PORT_RECV_DEFAULT;
osc_port_send = OSC_PORT_SEND_DEFAULT;
} }
}; };

View File

@@ -105,6 +105,7 @@
#define USE_GST_APPSINK_CALLBACKS #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 #endif // VMIX_DEFINES_H

BIN
rsc/osc/vimix.tosc Normal file

Binary file not shown.