From 128e8834e8800d69d18f9586011eb385fde1c019 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Thu, 9 Feb 2023 23:18:24 +0100 Subject: [PATCH] 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). --- src/ControlManager.cpp | 8 +-- src/Session.cpp | 80 ++++++++++++++++----------- src/Session.h | 4 +- src/Source.cpp | 58 ++++++++++--------- src/Source.h | 1 + src/SourceCallback.cpp | 104 ++++++----------------------------- src/SourceCallback.h | 11 +--- src/UserInterfaceManager.cpp | 2 +- 8 files changed, 103 insertions(+), 165 deletions(-) diff --git a/src/ControlManager.cpp b/src/ControlManager.cpp index 121f319..18f868f 100644 --- a/src/ControlManager.cpp +++ b/src/ControlManager.cpp @@ -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) { diff --git a/src/Session.cpp b/src/Session.cpp index 528741e..ae6cfcf 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -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); } diff --git a/src/Session.h b/src/Session.h index f4c20d2..7797eaa 100644 --- a/src/Session.h +++ b/src/Session.h @@ -217,14 +217,14 @@ protected: struct InputSourceCallback { bool active_; SourceCallback *model_; - std::map reverse_; + std::map > instances_; Target target_; InputSourceCallback() { active_ = false; model_ = nullptr; target_ = nullptr; } - void clearReverse(); + void clear(); }; std::multimap input_callbacks_; std::vector input_sync_; diff --git a/src/Source.cpp b/src/Source.cpp index ee8f354..1eea993 100644 --- a/src/Source.cpp +++ b/src/Source.cpp @@ -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) diff --git a/src/Source.h b/src/Source.h index 9fb31fe..f7ed9cd 100644 --- a/src/Source.h +++ b/src/Source.h @@ -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_; } diff --git a/src/SourceCallback.cpp b/src/SourceCallback.cpp index ba1ce0b..1b0e659 100644 --- a/src/SourceCallback.cpp +++ b/src/SourceCallback.cpp @@ -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(a); - const Grab *_b = static_cast(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(a); - const Resize *_b = static_cast(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); diff --git a/src/SourceCallback.h b/src/SourceCallback.h index 12cfb49..fe7b788 100644 --- a/src/SourceCallback.h +++ b/src/SourceCallback.h @@ -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; }; diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index 6e8dbe8..546853d 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -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());