Changed mechanism of Source Callback and Input Mapping

Session stores list of all callback instances and reacts on input release by calling the reverse callback if it exists, or by finishing the ongoing callback. This means the behavior of Callbacks is different for those who are reversible (i.e. returns a non-null reverse) from those which do not have reverse. The reversible callbacks enforce to be exclusive while active (key pressed), others can be repeated and complementary (run in parallel).
This commit is contained in:
Bruno Herbelin
2023-02-09 23:18:24 +01:00
parent 7433772606
commit 128e8834e8
8 changed files with 103 additions and 165 deletions

View File

@@ -632,7 +632,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
else if ( attribute.compare(OSC_SOURCE_LOOM) == 0) {
float x = 1.f;
arguments >> x >> osc::EndMessage;
target->call( new Loom(x, 0.f), true );
target->call( new Loom(x, 0.f) );
// this will require to send feedback status about source
send_feedback = true;
}
@@ -662,7 +662,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
catch (osc::WrongArgumentTypeException &) {
}
arguments >> osc::EndMessage;
target->call( new Grab( x, y, 0.f), true );
target->call( new Grab( x, y, 0.f) );
}
/// e.g. '/vimix/current/position ff 10.0 2.2'
else if ( attribute.compare(OSC_SOURCE_POSITION) == 0) {
@@ -699,7 +699,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
catch (osc::WrongArgumentTypeException &) {
}
arguments >> osc::EndMessage;
target->call( new Resize( x, y, 0.f), true );
target->call( new Resize( x, y, 0.f) );
}
/// e.g. '/vimix/current/size ff 1.0 2.2'
else if ( attribute.compare(OSC_SOURCE_SIZE) == 0) {
@@ -730,7 +730,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut
arguments >> osc::EndMessage;
else
arguments >> t >> osc::EndMessage;
target->call( new Turn( x, t), true );
target->call( new Turn( x, t) );
}
/// e.g. '/vimix/current/angle f 3.1416'
else if ( attribute.compare(OSC_SOURCE_ANGLE) == 0) {

View File

@@ -76,13 +76,18 @@ Session::Session(uint64_t id) : id_(id), active_(true), activation_threshold_(MI
}
void Session::InputSourceCallback::clearReverse()
void Session::InputSourceCallback::clear()
{
for (auto rev = reverse_.begin(); rev != reverse_.end(); ++rev) {
if (rev->second != nullptr)
delete rev->second;
// go through all instances stored in InputSourceCallback
for (auto clb = instances_.begin(); clb != instances_.end(); ++clb) {
// finish all
if (clb->second.first != nullptr)
clb->second.first->finish();
if (clb->second.second != nullptr)
clb->second.second->finish();
}
reverse_.clear();
// do not keep references: will be deleted when terminated
instances_.clear();
}
Session::~Session()
@@ -105,7 +110,7 @@ Session::~Session()
iter = input_callbacks_.erase(iter)) {
if ( iter->second.model_ != nullptr)
delete iter->second.model_;
iter->second.clearReverse();
iter->second.clear();
}
delete config_[View::RENDERING];
@@ -151,13 +156,10 @@ void Session::update(float dt)
if ( k->second.model_ != nullptr && k->second.target_.index() > 0) {
// if the value referenced as pressed changed state
// or repeat key if there is no reverse callback
if ( input_active != k->second.active_ || k->second.reverse_.empty()) {
if ( input_active != k->second.active_ ) {
// ON PRESS
if (input_active) {
// delete the reverse if was not released
k->second.clearReverse();
// Add callback to the target(s)
// 1. Case of variant as Source pointer
@@ -165,15 +167,18 @@ void Session::update(float dt)
// verify variant value
if ( *v != nullptr ) {
// generate a new callback from the model
SourceCallback *C = k->second.model_->clone();
SourceCallback *forward = k->second.model_->clone();
// apply value multiplyer from input
C->multiply( Control::manager().inputValue(k->first) );
forward->multiply( Control::manager().inputValue(k->first) );
// add delay
C->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) );
forward->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) );
// add callback to source
(*v)->call( C, true );
// get the reverse if the callback, and remember it (can be null)
k->second.reverse_[(*v)->id()] = C->reverse(*v);
(*v)->call( forward );
// get the reverse of the callback (can be null)
SourceCallback *backward = forward->reverse(*v);;
// remember instances
k->second.instances_[(*v)->id()] = {forward, backward};
}
}
// 2. Case of variant as index of batch
@@ -186,15 +191,17 @@ void Session::update(float dt)
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(*sid));;
if ( sit != sources_.end()) {
// generate a new callback from the model
SourceCallback *C = k->second.model_->clone();
SourceCallback *forward = k->second.model_->clone();
// apply value multiplyer from input
C->multiply( Control::manager().inputValue(k->first) );
forward->multiply( Control::manager().inputValue(k->first) );
// add delay
C->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) );
forward->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) );
// add callback to source
(*sit)->call( C, true );
// get the reverse if the callback, and remember it (can be null)
k->second.reverse_[*sid] = C->reverse(*sit);
(*sit)->call( forward );
// get the reverse of the callback (can be null)
SourceCallback *backward = forward->reverse(*sit);;
// remember instances
k->second.instances_[*sid] = {forward, backward};
}
}
}
@@ -202,15 +209,22 @@ void Session::update(float dt)
}
// ON RELEASE
else {
// call all the reverse of sources (NB: invalid value tested in call)
for (auto rev = k->second.reverse_.begin(); rev != k->second.reverse_.end(); ++rev) {
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(rev->first));;
// go through all instances stored for that action
for (auto clb = k->second.instances_.begin(); clb != k->second.instances_.end(); ++clb) {
// find the source referenced by each instance
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(clb->first));;
// if the source is valid
if ( sit != sources_.end()) {
(*sit)->call( rev->second, true );
// either call the reverse if exists (stored as second element in pair)
if (clb->second.second != nullptr)
(*sit)->call( clb->second.second, true );
// or finish the called
else
(*sit)->finish( clb->second.first );
}
}
// do not keep reference to reverse: will be deleted when terminated
k->second.reverse_.clear();
// do not keep reference to instances: will be deleted when finished
k->second.instances_.clear();
}
// remember state of callback
@@ -779,7 +793,7 @@ void Session::assignInputCallback(uint input, Target target, SourceCallback *cal
if ( k->second.model_ == callback) {
k->second.target_ = target;
// reverse became invalid
k->second.clearReverse();
k->second.clear();
break;
}
}
@@ -837,7 +851,7 @@ void Session::deleteInputCallback(SourceCallback *callback)
{
if ( k->second.model_ == callback) {
delete callback;
k->second.clearReverse();
k->second.clear();
input_callbacks_.erase(k);
break;
}
@@ -851,7 +865,7 @@ void Session::deleteInputCallbacks(uint input)
if ( k->first == input) {
if (k->second.model_)
delete k->second.model_;
k->second.clearReverse();
k->second.clear();
k = input_callbacks_.erase(k);
}
else
@@ -866,7 +880,7 @@ void Session::deleteInputCallbacks(Target target)
if ( k->second.target_ == target) {
if (k->second.model_)
delete k->second.model_;
k->second.clearReverse();
k->second.clear();
k = input_callbacks_.erase(k);
}
else
@@ -881,7 +895,7 @@ void Session::clearInputCallbacks()
{
if (k->second.model_)
delete k->second.model_;
k->second.clearReverse();
k->second.clear();
k = input_callbacks_.erase(k);
}

View File

@@ -217,14 +217,14 @@ protected:
struct InputSourceCallback {
bool active_;
SourceCallback *model_;
std::map<uint64_t, SourceCallback *> reverse_;
std::map<uint64_t, std::pair< SourceCallback *, SourceCallback *> > instances_;
Target target_;
InputSourceCallback() {
active_ = false;
model_ = nullptr;
target_ = nullptr;
}
void clearReverse();
void clear();
};
std::multimap<uint, InputSourceCallback> input_callbacks_;
std::vector<Metronome::Synchronicity> input_sync_;

View File

@@ -655,38 +655,17 @@ void Source::call(SourceCallback *callback, bool override)
// lock access to callbacks list
access_callbacks_.lock();
bool add = true;
// look for callbacks of same type
for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); )
{
// Test if the new callback would overlap an existing one
SourceCallback *c = *iter;
if ( SourceCallback::overlap( callback, c) ) {
if (override) {
// either remove and delete all overlapping callbacks if override...
iter = update_callbacks_.erase(iter);
delete c;
}
else {
// ...or cancel adding overlapping callbacks if not override
add = false;
break;
}
// if operation should override previous callbacks of same type
if (override) {
// finish all callbacks of the same type
for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ++iter) {
if ( callback->type() == (*iter)->type() )
(*iter)->finish();
}
// iterate
else
++iter;
}
// we can add the callback : its either not overlapping or we override it
if (add) {
// add callback to callbacks list
update_callbacks_.push_back(callback);
}
// or delete it if couln't be added (overlapping but not override)
else
delete callback;
// allways add the given callback to list of callbacks
update_callbacks_.push_back(callback);
// release access to callbacks list
access_callbacks_.unlock();
@@ -694,10 +673,29 @@ void Source::call(SourceCallback *callback, bool override)
}
void Source::finish(SourceCallback *callback)
{
if (callback != nullptr) {
// lock access to callbacks list
access_callbacks_.lock();
// make sure the given pointer is a callback listed in this source
auto cb = std::find(update_callbacks_.begin(), update_callbacks_.end(), callback);
if (cb != update_callbacks_.end())
// set found callback to finish state
(*cb)->finish();
// release access to callbacks list
access_callbacks_.unlock();
}
}
void Source::updateCallbacks(float dt)
{
// lock access to callbacks list
access_callbacks_.lock();
// call callback functions
for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); )
{
@@ -716,9 +714,9 @@ void Source::updateCallbacks(float dt)
else
++iter;
}
// release access to callbacks list
access_callbacks_.unlock();
}
CloneSource *Source::clone(uint64_t id)

View File

@@ -149,6 +149,7 @@ public:
// add callback to each update
void call(SourceCallback *callback, bool override = false);
void finish(SourceCallback *callback);
// update mode
inline bool active () const { return active_; }

View File

@@ -22,9 +22,7 @@
#include "ImageProcessingShader.h"
#include "MediaSource.h"
#include "MediaPlayer.h"
#include "UpdateCallback.h"
#include "Visitor.h"
#include "Log.h"
#include "SourceCallback.h"
@@ -101,53 +99,6 @@ SourceCallback *SourceCallback::create(CallbackType type)
return loadedcallback;
}
bool SourceCallback::overlap( SourceCallback *a, SourceCallback *b)
{
bool ret = false;
if (a->type() == b->type()) {
// same type means overlap
ret = true;
// but there are some exceptions..
switch (a->type()) {
case SourceCallback::CALLBACK_GRAB:
{
const Grab *_a = static_cast<Grab*>(a);
const Grab *_b = static_cast<Grab*>(b);
if ( ABS_DIFF(_a->value().x, _b->value().x) > EPSILON || ABS_DIFF(_a->value().y, _b->value().y) > EPSILON )
ret = false;
// // there is no overlap if the X or Y of a vector is zero
// if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
// ret = false;
// else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
// ret = false;
}
break;
case SourceCallback::CALLBACK_RESIZE:
{
const Resize *_a = static_cast<Resize*>(a);
const Resize *_b = static_cast<Resize*>(b);
if ( ABS_DIFF(_a->value().x, _b->value().x) > EPSILON || ABS_DIFF(_a->value().y, _b->value().y) > EPSILON )
ret = false;
// if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
// ret = false;
// else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
// ret = false;
}
break;
default:
break;
}
}
return ret;
}
SourceCallback::SourceCallback(): status_(PENDING), delay_(0.f), elapsed_(0.f)
{
}
@@ -384,7 +335,6 @@ SourceCallback *Lock::clone() const
Loom::Loom(float speed, float ms) : SourceCallback(),
speed_(speed), duration_(ms)
{
pos_ = glm::vec2();
step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default
}
@@ -395,13 +345,14 @@ void Loom::update(Source *s, float dt)
if (s->locked())
status_ = FINISHED;
// current position
glm::vec2 pos = glm::vec2(s->group(View::MIXING)->translation_);
// set start on first time it is ready
if ( status_ == READY ){
// initial position
pos_ = glm::vec2(s->group(View::MIXING)->translation_);
// step in direction of source translation if possible
if ( glm::length(pos_) > DELTA_ALPHA)
step_ = glm::normalize(pos_);
if ( glm::length(pos) > DELTA_ALPHA)
step_ = glm::normalize(pos);
status_ = ACTIVE;
}
@@ -410,12 +361,12 @@ void Loom::update(Source *s, float dt)
float progress = elapsed_ - delay_;
// move target by speed vector (in the direction of step_, amplitude of speed * time (in second))
pos_ -= step_ * ( speed_ * dt * 0.001f );
pos -= step_ * ( speed_ * dt * 0.001f );
// apply alpha if pos in range [0 MIXING_MIN_THRESHOLD]
float l = glm::length( pos_ );
if ( (l > 0.01f && speed_ > 0.f ) || (l < MIXING_MIN_THRESHOLD && speed_ < 0.f ) )
s->group(View::MIXING)->translation_ = glm::vec3(pos_, s->group(View::MIXING)->translation_.z);
// apply alpha if pos in range [0 MIXING_MAX_THRESHOLD]
float l = glm::length( pos );
if ( (l > 0.01f && speed_ > 0.f ) || (l < MIXING_MAX_THRESHOLD && speed_ < 0.f ) )
s->group(View::MIXING)->translation_ = glm::vec3(pos, s->group(View::MIXING)->translation_.z);
else
status_ = FINISHED;
@@ -436,11 +387,6 @@ SourceCallback *Loom::clone() const
return new Loom(speed_, duration_);
}
SourceCallback *Loom::reverse(Source *) const
{
return new Loom(speed_, 0.f);
}
void Loom::accept(Visitor& v)
{
SourceCallback::accept(v);
@@ -696,15 +642,15 @@ void Grab::update(Source *s, float dt)
// set start on first time it is ready
if ( status_ == READY ) {
// initial position
pos_ = glm::vec2(s->group(View::GEOMETRY)->translation_);
status_ = ACTIVE;
}
if ( status_ == ACTIVE ) {
// move target by speed vector * time (in second)
pos_ += speed_ * ( dt * 0.001f);
s->group(View::GEOMETRY)->translation_ = glm::vec3(pos_, s->group(View::GEOMETRY)->translation_.z);
glm::vec2 pos = glm::vec2(s->group(View::GEOMETRY)->translation_);
pos += speed_ * ( dt * 0.001f);
s->group(View::GEOMETRY)->translation_ = glm::vec3(pos, s->group(View::GEOMETRY)->translation_.z);
// time-out
if ( (elapsed_ - delay_) > duration_ )
@@ -723,11 +669,6 @@ SourceCallback *Grab::clone() const
return new Grab(speed_.x, speed_.y, duration_);
}
SourceCallback *Grab::reverse(Source *) const
{
return new Grab(speed_.x, speed_.y, 0.f);
}
void Grab::accept(Visitor& v)
{
SourceCallback::accept(v);
@@ -770,11 +711,6 @@ SourceCallback *Resize::clone() const
return new Resize(speed_.x, speed_.y, duration_);
}
SourceCallback *Resize::reverse(Source *) const
{
return new Resize(speed_.x, speed_.y, 0.f);
}
void Resize::accept(Visitor& v)
{
SourceCallback::accept(v);
@@ -795,16 +731,17 @@ void Turn::update(Source *s, float dt)
// set start on first time it is ready
if ( status_ == READY ){
// initial position
angle_ = s->group(View::GEOMETRY)->rotation_.z;
status_ = ACTIVE;
}
if ( status_ == ACTIVE ) {
// current position
float angle = s->group(View::GEOMETRY)->rotation_.z;
// perform movement
angle_ -= speed_ * ( dt * 0.001f );
s->group(View::GEOMETRY)->rotation_.z = angle_;
angle -= speed_ * ( dt * 0.001f );
s->group(View::GEOMETRY)->rotation_.z = angle;
// timeout
if ( (elapsed_ - delay_) > duration_ )
@@ -823,11 +760,6 @@ SourceCallback *Turn::clone() const
return new Turn(speed_, duration_);
}
SourceCallback *Turn::reverse(Source *) const
{
return new Turn(speed_, 0.f);
}
void Turn::accept(Visitor& v)
{
SourceCallback::accept(v);

View File

@@ -50,7 +50,6 @@ public:
} CallbackType;
static SourceCallback *create(CallbackType type);
static bool overlap(SourceCallback *a, SourceCallback *b);
SourceCallback();
virtual ~SourceCallback() {}
@@ -62,8 +61,9 @@ public:
virtual CallbackType type () const { return CALLBACK_GENERIC; }
virtual void accept (Visitor& v);
inline bool finished () const { return status_ > ACTIVE; }
inline void reset () { status_ = PENDING; }
inline void finish () { status_ = FINISHED; }
inline bool finished () const { return status_ > ACTIVE; }
inline void delay (float milisec) { delay_ = milisec;}
protected:
@@ -146,7 +146,6 @@ public:
class Loom : public SourceCallback
{
float speed_;
glm::vec2 pos_;
glm::vec2 step_;
float duration_;
@@ -161,7 +160,6 @@ public:
void update (Source *s, float) override;
void multiply (float factor) override;
SourceCallback *clone() const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_LOOM; }
void accept (Visitor& v) override;
};
@@ -273,7 +271,6 @@ public:
class Grab : public SourceCallback
{
glm::vec2 speed_;
glm::vec2 pos_;
float duration_;
public:
@@ -287,7 +284,6 @@ public:
void update (Source *s, float) override;
void multiply (float factor) override;
SourceCallback *clone () const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_GRAB; }
void accept (Visitor& v) override;
};
@@ -308,7 +304,6 @@ public:
void update (Source *s, float) override;
void multiply (float factor) override;
SourceCallback *clone () const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_RESIZE; }
void accept (Visitor& v) override;
};
@@ -316,7 +311,6 @@ public:
class Turn : public SourceCallback
{
float speed_;
float angle_;
float duration_;
public:
@@ -330,7 +324,6 @@ public:
void update (Source *s, float) override;
void multiply (float factor) override;
SourceCallback *clone () const override;
SourceCallback *reverse(Source *) const override;
CallbackType type () const override { return CALLBACK_TURN; }
void accept (Visitor& v) override;
};

View File

@@ -1207,7 +1207,7 @@ void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode)
float v = s->alpha();
ImGui::SetNextItemWidth(rightalign);
if ( ImGui::DragFloat("Alpha", &v, 0.01f, 0.f, 1.f) )
s->call(new SetAlpha(v));
s->call(new SetAlpha(v), true);
if ( ImGui::IsItemDeactivatedAfterEdit() ) {
info << "Alpha " << std::fixed << std::setprecision(3) << v;
Action::manager().store(info.str());