diff --git a/ControlManager.cpp b/ControlManager.cpp index 1d57e7f..8b8d856 100644 --- a/ControlManager.cpp +++ b/ControlManager.cpp @@ -1,4 +1,4 @@ -/* +/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2019-2022 Bruno Herbelin @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "osc/OscOutboundPacketStream.h" @@ -30,6 +31,7 @@ #include "BaseToolkit.h" #include "Mixer.h" #include "Source.h" +#include "SourceCallback.h" #include "ActionManager.h" #include "SystemToolkit.h" #include "tinyxml2Toolkit.h" @@ -238,6 +240,7 @@ std::string Control::RequestListener::FullMessage( const osc::ReceivedMessage& m Control::Control() : receiver_(nullptr) { + } Control::~Control() @@ -245,7 +248,6 @@ Control::~Control() terminate(); } - std::string Control::translate (std::string addresspattern) { auto it_translation = translation_.find(addresspattern); @@ -363,7 +365,7 @@ bool Control::init() // 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()); + Log::Warning(CONTROL_OSC_MSG "The port %d is already used by another program; %s", Settings::application.control.osc_port_receive, e.what()); } return receiver_ != nullptr; @@ -511,7 +513,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut else if ( attribute.compare(OSC_SOURCE_ALPHA) == 0) { float x = 1.f; arguments >> x >> osc::EndMessage; - target->call( new SetAlpha(x), true ); + target->call( new GotoAlpha(x), true ); } /// e.g. '/vimix/current/alpha f 0.3' else if ( attribute.compare(OSC_SOURCE_LOOM) == 0) { @@ -525,13 +527,13 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut else if ( attribute.compare(OSC_SOURCE_TRANSPARENCY) == 0) { float x = 0.f; arguments >> x >> osc::EndMessage; - target->call( new SetAlpha(1.f - x), true ); + target->call( new GotoAlpha(1.f - x), true ); } /// e.g. '/vimix/current/depth f 5.0' else if ( attribute.compare(OSC_SOURCE_DEPTH) == 0) { float x = 0.f; arguments >> x >> osc::EndMessage; - target->call( new SetDepth(x), true ); + target->call( new GotoDepth(x), true ); } /// e.g. '/vimix/current/translation ff 10.0 2.2' else if ( attribute.compare(OSC_SOURCE_GRAB) == 0) { diff --git a/ControlManager.h b/ControlManager.h index 3f6a6ee..10576c2 100644 --- a/ControlManager.h +++ b/ControlManager.h @@ -2,8 +2,12 @@ #define CONTROL_H #include +#include #include #include + +#include "SourceList.h" +#include "BaseToolkit.h" #include "NetworkToolkit.h" #define OSC_SYNC "/sync" @@ -44,6 +48,7 @@ class Session; class Source; +class SourceCallback; class Control { @@ -65,10 +70,12 @@ public: bool init(); void terminate(); - std::string translate (std::string addresspattern); + // OSC translation + std::string translate (std::string addresspqattern); protected: + // OSC management class RequestListener : public osc::OscPacketListener { protected: virtual void ProcessMessage( const osc::ReceivedMessage& m, @@ -78,15 +85,14 @@ protected: bool receiveOutputAttribute(const std::string &attribute, osc::ReceivedMessageArgumentStream arguments); - bool receiveSourceAttribute(Source *target, const std::string &attribute, osc::ReceivedMessageArgumentStream arguments); - bool receiveSessionAttribute(const std::string &attribute, osc::ReceivedMessageArgumentStream arguments); - - void sendSourceAttibutes(const IpEndpointName& remoteEndpoint, std::string target, Source *s = nullptr); - void sendSourcesStatus(const IpEndpointName& remoteEndpoint, osc::ReceivedMessageArgumentStream arguments); + void sendSourceAttibutes(const IpEndpointName& remoteEndpoint, + std::string target, Source *s = nullptr); + void sendSourcesStatus(const IpEndpointName& remoteEndpoint, + osc::ReceivedMessageArgumentStream arguments); void sendOutputStatus(const IpEndpointName& remoteEndpoint); private: diff --git a/LayerView.cpp b/LayerView.cpp index c4be552..ce74967 100644 --- a/LayerView.cpp +++ b/LayerView.cpp @@ -32,6 +32,7 @@ #include "Mixer.h" #include "defines.h" #include "Source.h" +#include "SourceCallback.h" #include "Settings.h" #include "Decorations.h" #include "UserInterfaceManager.h" @@ -136,7 +137,7 @@ void LayerView::draw() float depth_inc = (dsl.back()->depth() - depth) / static_cast(Mixer::selection().size()-1); for (++it; it != dsl.end(); ++it) { depth += depth_inc; - (*it)->call( new SetDepth(depth, 80.f) ); + (*it)->call( new GotoDepth(depth, 80.f) ); } Action::manager().store(std::string("Selection: Layer Distribute")); } @@ -146,7 +147,7 @@ void LayerView::draw() float depth = (*it)->depth(); for (++it; it != dsl.end(); ++it) { depth += LAYER_STEP; - (*it)->call( new SetDepth(depth, 80.f) ); + (*it)->call( new GotoDepth(depth, 80.f) ); } Action::manager().store(std::string("Selection: Layer Compress")); } @@ -155,7 +156,7 @@ void LayerView::draw() SourceList::iterator it = dsl.begin(); SourceList::reverse_iterator rit = dsl.rbegin(); for (; it != dsl.end(); ++it, ++rit) { - (*it)->call( new SetDepth((*rit)->depth(), 80.f) ); + (*it)->call( new GotoDepth((*rit)->depth(), 80.f) ); } Action::manager().store(std::string("Selection: Layer Reverse order")); } diff --git a/Mixer.cpp b/Mixer.cpp index 1065be9..2482d25 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -51,6 +51,7 @@ #include "StreamSource.h" #include "NetworkSource.h" #include "SrtReceiverSource.h" +#include "SourceCallback.h" #include "ActionManager.h" #include "MixingGroup.h" @@ -1174,7 +1175,7 @@ void Mixer::merge(SessionSource *source) SourceList::iterator it = to_be_moved.begin(); for (; it != to_be_moved.end(); ++it) { float scale_depth = (MAX_DEPTH-(*it)->depth()) / (MAX_DEPTH-next_depth); - (*it)->call( new SetDepth( (*it)->depth() + scale_depth ) ); + (*it)->call( new GotoDepth( (*it)->depth() + scale_depth ) ); } } } @@ -1186,10 +1187,10 @@ void Mixer::merge(SessionSource *source) renameSource(s); // scale alpha - s->call( new SetAlpha(s->alpha() * source->alpha())); + s->call( new GotoAlpha(s->alpha() * source->alpha())); // set depth (proportional to depth of s, adjusted by needed space) - s->call( new SetDepth( target_depth + ( (s->depth()-start_depth)/ need_depth) ) ); + s->call( new GotoDepth( target_depth + ( (s->depth()-start_depth)/ need_depth) ) ); // set location // a. transform of node to import diff --git a/MixingView.cpp b/MixingView.cpp index 2b2bfdc..49f7e61 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -32,6 +32,7 @@ #include "Mixer.h" #include "defines.h" #include "Source.h" +#include "SourceCallback.h" #include "Settings.h" #include "Decorations.h" #include "UserInterfaceManager.h" @@ -278,14 +279,14 @@ void MixingView::draw() if (ImGui::Selectable( ICON_FA_CLOUD_SUN " Expand & hide" )){ SourceList::iterator it = Mixer::selection().begin(); for (; it != Mixer::selection().end(); ++it) { - (*it)->call( new SetAlpha(0.f) ); + (*it)->call( new GotoAlpha(0.f) ); } Action::manager().store(std::string("Selection: Mixing Expand & hide")); } if (ImGui::Selectable( ICON_FA_SUN " Compress & show" )){ SourceList::iterator it = Mixer::selection().begin(); for (; it != Mixer::selection().end(); ++it) { - (*it)->call( new SetAlpha(0.999f) ); + (*it)->call( new GotoAlpha(0.999f) ); } Action::manager().store(std::string("Selection: Mixing Compress & show")); } diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 9ea4e27..f009fff 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -25,6 +25,7 @@ #include "Primitives.h" #include "Mesh.h" #include "Source.h" +#include "SourceCallback.h" #include "CloneSource.h" #include "MediaSource.h" #include "SessionSource.h" @@ -899,6 +900,50 @@ void SessionLoader::visit (Source& s) groups_sources_id_.push_back(idlist); } + XMLElement* callbacksNode = sourceNode->FirstChildElement("Callbacks"); + if (callbacksNode) { + // Loop over 'Callback' nodes + xmlCurrent_ = callbacksNode->FirstChildElement("Callback"); + for ( ; xmlCurrent_ ; xmlCurrent_ = xmlCurrent_->NextSiblingElement()) { + // what key triggers the callback ? + int key = 0; + xmlCurrent_->QueryIntAttribute("key", &key); + // what type is the callback ? + uint type = 0; + xmlCurrent_->QueryUnsignedAttribute("type", &type); + // instanciate the callback of that type + SourceCallback *loadedcallback = nullptr; + switch (type) { + case SourceCallback::CALLBACK_ALPHA: + loadedcallback = new GotoAlpha; + break; + case SourceCallback::CALLBACK_LOOM: + loadedcallback = new Loom; + break; + case SourceCallback::CALLBACK_DEPTH: + loadedcallback = new GotoDepth; + break; + case SourceCallback::CALLBACK_GRAB: + loadedcallback = new Grab; + break; + case SourceCallback::CALLBACK_RESIZE: + loadedcallback = new Resize; + break; + case SourceCallback::CALLBACK_TURN: + loadedcallback = new Turn; + break; + } + // successfully created a callback of saved type + if (loadedcallback) { + // apply specific parameters + loadedcallback->accept(*this); + // add callback to source + s.setKeyCallback(key, loadedcallback); + } + } + + } + // restore current xmlCurrent_ = sourceNode; } @@ -1121,3 +1166,74 @@ void SessionLoader::visit (CloneSource& s) s.setDelay(d); } + +void SessionLoader::visit (SourceCallback &) +{ +} + +void SessionLoader::visit (GotoAlpha &c) +{ + float a = 0.f; + xmlCurrent_->QueryFloatAttribute("alpha", &a); + c.setAlpha(a); +} + +void SessionLoader::visit (GotoDepth &c) +{ + float d = 0.f; + xmlCurrent_->QueryFloatAttribute("depth", &d); + c.setDepth(d); + + d = 0.f; + xmlCurrent_->QueryFloatAttribute("duration", &d); + c.setDuration(d); +} + +void SessionLoader::visit (Loom &c) +{ + float d = 0.f; + xmlCurrent_->QueryFloatAttribute("delta", &d); + c.setDelta(d); + + d = 0.f; + xmlCurrent_->QueryFloatAttribute("duration", &d); + c.setDuration(d); +} + +void SessionLoader::visit (Grab &c) +{ + float dx = 0.f, dy = 0.f; + xmlCurrent_->QueryFloatAttribute("delta.x", &dx); + xmlCurrent_->QueryFloatAttribute("delta.y", &dy); + c.setDelta( glm::vec2(dx, dy) ); + + float d = 0.f; + xmlCurrent_->QueryFloatAttribute("duration", &d); + c.setDuration(d); +} + +void SessionLoader::visit (Resize &c) +{ + float dx = 0.f, dy = 0.f; + xmlCurrent_->QueryFloatAttribute("delta.x", &dx); + xmlCurrent_->QueryFloatAttribute("delta.y", &dy); + c.setDelta( glm::vec2(dx, dy) ); + + float d = 0.f; + xmlCurrent_->QueryFloatAttribute("duration", &d); + c.setDuration(d); +} + +void SessionLoader::visit (Turn &c) +{ + float d = 0.f; + xmlCurrent_->QueryFloatAttribute("delta", &d); + c.setDelta(d); + + d = 0.f; + xmlCurrent_->QueryFloatAttribute("duration", &d); + c.setDuration(d); +} + + + diff --git a/SessionCreator.h b/SessionCreator.h index 4a470db..b693fb0 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -63,6 +63,15 @@ public: void visit (GenericStreamSource& s) override; void visit (SrtReceiverSource& s) override; + // callbacks + void visit (SourceCallback&) override; + void visit (GotoAlpha&) override; + void visit (GotoDepth&) override; + void visit (Loom&) override; + void visit (Grab&) override; + void visit (Resize&) override; + void visit (Turn&) override; + static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n); static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s); static FrameBufferImage *XMLToImage(const tinyxml2::XMLElement *xml); diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index adcda41..407026b 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -28,6 +28,7 @@ using namespace tinyxml2; #include "Scene.h" #include "Decorations.h" #include "Source.h" +#include "SourceCallback.h" #include "CloneSource.h" #include "RenderSource.h" #include "MediaSource.h" @@ -579,6 +580,19 @@ void SessionVisitor::visit (Source& s) s.mixingGroup()->accept(*this); } + std::vector keys = s.callbackKeys(); + if (!keys.empty()) { + // list of callbacks + XMLElement *callbackselement = xmlDoc_->NewElement( "Callbacks" ); + sourceNode->InsertEndChild(callbackselement); + for (auto k = keys.begin(); k != keys.end(); ++k) { + xmlCurrent_ = xmlDoc_->NewElement( "Callback" ); + xmlCurrent_->SetAttribute("key", *k); + s.keyCallback(*k)->accept(*this); + callbackselement->InsertEndChild(xmlCurrent_); + } + } + xmlCurrent_ = sourceNode; // parent for next visits (other subtypes of Source) } @@ -828,3 +842,48 @@ std::string SessionVisitor::getClipboard(ImageProcessingShader * const s) return x; } + +void SessionVisitor::visit (SourceCallback &c) +{ + xmlCurrent_->SetAttribute("type", (uint) c.type()); +} + +void SessionVisitor::visit (GotoAlpha &c) +{ + xmlCurrent_->SetAttribute("alpha", c.alpha()); +} + +void SessionVisitor::visit (GotoDepth &c) +{ + xmlCurrent_->SetAttribute("depth", c.depth()); + xmlCurrent_->SetAttribute("duration", c.duration()); +} + +void SessionVisitor::visit (Loom &c) +{ + xmlCurrent_->SetAttribute("delta", c.delta()); + xmlCurrent_->SetAttribute("duration", c.duration()); +} + +void SessionVisitor::visit (Grab &c) +{ + xmlCurrent_->SetAttribute("delta.x", c.delta().x); + xmlCurrent_->SetAttribute("delta.y", c.delta().y); + xmlCurrent_->SetAttribute("duration", c.duration()); +} + +void SessionVisitor::visit (Resize &c) +{ + xmlCurrent_->SetAttribute("delta.x", c.delta().x); + xmlCurrent_->SetAttribute("delta.y", c.delta().y); + xmlCurrent_->SetAttribute("duration", c.duration()); +} + +void SessionVisitor::visit (Turn &c) +{ + xmlCurrent_->SetAttribute("delta", c.delta()); + xmlCurrent_->SetAttribute("duration", c.duration()); +} + + + diff --git a/SessionVisitor.h b/SessionVisitor.h index bbb543a..99aff9f 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -69,6 +69,15 @@ public: void visit (GenericStreamSource& s) override; void visit (SrtReceiverSource& s) override; + // callbacks + void visit (SourceCallback&) override; + void visit (GotoAlpha&) override; + void visit (GotoDepth&) override; + void visit (Loom&) override; + void visit (Grab&) override; + void visit (Resize&) override; + void visit (Turn&) override; + static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc); static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc); }; diff --git a/Source.cpp b/Source.cpp index d9790d9..a937889 100644 --- a/Source.cpp +++ b/Source.cpp @@ -34,6 +34,7 @@ #include "BaseToolkit.h" #include "SystemToolkit.h" #include "MixingGroup.h" +#include "SourceCallback.h" #include "CloneSource.h" #include "Source.h" @@ -356,6 +357,16 @@ Source::~Source() iter = update_callbacks_.erase(iter); delete callback; } + + // clear and delete key_callbacks + for (auto iter = key_callbacks_.begin(); iter != key_callbacks_.end(); + iter = key_callbacks_.erase(iter)) { + if ( iter->second.model_ != nullptr) + delete iter->second.model_; + if ( iter->second.reverse_ != nullptr) + delete iter->second.reverse_; + } + } void Source::setName (const std::string &name) @@ -625,30 +636,122 @@ void Source::call(SourceCallback *callback, bool override) // lock access to callbacks list access_callbacks_.lock(); - // remove similar callbacks if override - if (override) { - for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) - { - // remove and delete all callbacks of same type - SourceCallback *c = *iter; - if (callback->type() == c->type() ) { + bool add = true; + + // look for callbacks of same type + for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) + { + // there can be only one callback of same type in a source + SourceCallback *c = *iter; + if (callback->type() == c->type() ) { + if (override) { + // either remove and delete all callbacks of same type if override iter = update_callbacks_.erase(iter); delete c; } - // iterate - else - ++iter; + else { + // or cancel adding callbacks of same type if not override + add = false; + break; + } } + // iterate over other types + else + ++iter; } - // add callback to callbacks list - update_callbacks_.push_back(callback); + // we can add the callback as there is none of that type or we override + if (add) { + // add callback to callbacks list + update_callbacks_.push_back(callback); + } + // or delete it if couln't be added (not override) + else + delete callback; // release access to callbacks list access_callbacks_.unlock(); } + } +void Source::setKeyCallback(int key, SourceCallback *callback) +{ + // delete and reset reverse callback + if ( key_callbacks_[key].reverse_ != nullptr ) { + delete key_callbacks_[key].reverse_; + key_callbacks_[key].reverse_ = nullptr; + } + + // delete and replace callback + if ( key_callbacks_[key].model_ != nullptr ) + delete key_callbacks_[key].model_; + + key_callbacks_[key].model_ = callback; +} + +SourceCallback *Source::keyCallback(int key) +{ + // return pointer to callback cloned for this key, can be nullptr + if ( key_callbacks_.count(key) > 0 ) + return key_callbacks_[key].model_; + else + return nullptr; +} + + +std::vector Source::callbackKeys() +{ + std::vector keys; + keys.reserve(key_callbacks_.size()); + for(const auto& [key, value] : key_callbacks_) { + keys.push_back(key); + } + return keys; +} + +void Source::updateCallbacks(bool *keys_status) +{ + // add callbacks from listener + for (auto k = key_callbacks_.begin(); k != key_callbacks_.end(); ++k) + { + // if the Reaction is valid + if ( k->second.model_ != nullptr) { + + // if the value referenced as pressed changed state + // or repeat key if there is no reverse callback + if ( keys_status[k->first] != k->second.was_pressed_ || k->second.reverse_ == nullptr) { + + // ON PRESS + if (keys_status[k->first]) { + + // delete the reverse if was not released + if (k->second.reverse_ != nullptr) + delete k->second.reverse_; + + // generate a new callback from the model + SourceCallback *C = k->second.model_->clone(); + // add callback to the source + call( C, true ); + + // get the reverse if the callback, and remember it (can be null) + k->second.reverse_ = C->reverse(this); + + } + // ON RELEASE + else { + // call the reverse (NB: invalid value tested in call) + call( k->second.reverse_, true ); + // do not keep reference to reverse: will be deleted when terminated + k->second.reverse_ = nullptr; + } + + k->second.was_pressed_ = keys_status[k->first]; + } + } + } + +} CloneSource *Source::clone(uint64_t id) { @@ -659,7 +762,6 @@ CloneSource *Source::clone(uint64_t id) return s; } - void Source::update(float dt) { // keep delta-t @@ -670,11 +772,10 @@ void Source::update(float dt) { // lock access to callbacks list access_callbacks_.lock(); - // call all callbacks + // call callback functions for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) { SourceCallback *callback = *iter; - // call update for active callbacks if (callback->active()) { callback->update(this, dt); diff --git a/Source.h b/Source.h index 1a53d78..8c90ddd 100644 --- a/Source.h +++ b/Source.h @@ -8,7 +8,6 @@ #include #include "View.h" -#include "SourceCallback.h" #define DEFAULT_MIXING_TRANSLATION -1.f, 1.f @@ -27,6 +26,7 @@ #define ICON_SOURCE_SRT 11, 8 #define ICON_SOURCE 13, 11 +class Visitor; class SourceCallback; class ImageShader; class MaskShader; @@ -147,6 +147,10 @@ public: // add callback to each update void call(SourceCallback *callback, bool override = false); + void setKeyCallback(int key, SourceCallback *callback); + SourceCallback *keyCallback(int key); + std::vector callbackKeys(); + void updateCallbacks(bool *keys_status); // update mode inline bool active () const { return active_; } @@ -313,8 +317,21 @@ protected: bool need_update_; float dt_; Workspace workspace_; + + // callbacks std::list update_callbacks_; std::mutex access_callbacks_; + struct CallbackListener { + bool was_pressed_; + SourceCallback *model_; + SourceCallback *reverse_; + CallbackListener() { + was_pressed_ = false; + model_ = nullptr; + reverse_ = nullptr; + } + }; + std::map key_callbacks_; // clones CloneList clones_; diff --git a/SourceCallback.cpp b/SourceCallback.cpp index cbd1f68..03fb638 100644 --- a/SourceCallback.cpp +++ b/SourceCallback.cpp @@ -19,6 +19,7 @@ #include "defines.h" #include "Source.h" +#include "Visitor.h" #include "Log.h" #include "SourceCallback.h" @@ -27,6 +28,11 @@ SourceCallback::SourceCallback(): active_(true), finished_(false), initialized_( { } +void SourceCallback::accept(Visitor& v) +{ + v.visit(*this); +} + void ResetGeometry::update(Source *s, float) { s->group(View::GEOMETRY)->scale_ = glm::vec3(1.f); @@ -37,13 +43,23 @@ void ResetGeometry::update(Source *s, float) finished_ = true; } -SetAlpha::SetAlpha(float alpha) : SourceCallback(), alpha_(CLAMP(alpha, 0.f, 1.f)) +SourceCallback *ResetGeometry::clone() const +{ + return new ResetGeometry; +} + +GotoAlpha::GotoAlpha() : SourceCallback(), alpha_(0.f) { pos_ = glm::vec2(); step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default } -void SetAlpha::update(Source *s, float) +GotoAlpha::GotoAlpha(float alpha) : GotoAlpha() +{ + alpha_ = CLAMP(alpha, 0.f, 1.f); +} + +void GotoAlpha::update(Source *s, float) { if (s && !s->locked()) { // set start position on first run or upon call of reset() @@ -65,14 +81,31 @@ void SetAlpha::update(Source *s, float) pos_ += step_ * (delta / 2.f); s->group(View::MIXING)->translation_ = glm::vec3(pos_, s->group(View::MIXING)->translation_.z); } - // done - else + // reached alpha target + else { finished_ = true; + } } else finished_ = true; } +SourceCallback *GotoAlpha::clone() const +{ + return new GotoAlpha(alpha_); +} + +SourceCallback *GotoAlpha::reverse(Source *s) const +{ + return new GotoAlpha(s->alpha()); +} + +void GotoAlpha::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} + SetLock::SetLock(bool on) : SourceCallback(), lock_(on) { } @@ -85,9 +118,21 @@ void SetLock::update(Source *s, float) finished_ = true; } -Loom::Loom(float da, float duration) : SourceCallback(), speed_(da), - duration_(duration), progress_(0.f) +SourceCallback *SetLock::clone() const { + return new SetLock(lock_); +} + +Loom::Loom() : SourceCallback(), speed_(0), duration_(0), progress_(0.f) +{ + pos_ = glm::vec2(); + step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default +} + +Loom::Loom(float d, float duration) : Loom() +{ + speed_ = d; + duration_ = duration; pos_ = glm::vec2(); step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default } @@ -129,14 +174,30 @@ void Loom::update(Source *s, float dt) finished_ = true; } +SourceCallback *Loom::clone() const +{ + return new Loom(speed_, duration_); +} + +void Loom::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} -SetDepth::SetDepth(float target, float duration) : SourceCallback(), - duration_(duration), progress_(0.f), start_(0.f), target_(CLAMP(target, MIN_DEPTH, MAX_DEPTH)) +GotoDepth::GotoDepth() : SourceCallback(), + duration_(0), progress_(0.f), start_(0.f), target_(MIN_DEPTH) { } -void SetDepth::update(Source *s, float dt) +GotoDepth::GotoDepth(float target, float duration) : GotoDepth() +{ + target_ = CLAMP(target, MIN_DEPTH, MAX_DEPTH); + duration_ = duration; +} + +void GotoDepth::update(Source *s, float dt) { if (s && !s->locked()) { // set start position on first run or upon call of reset() @@ -167,6 +228,21 @@ void SetDepth::update(Source *s, float dt) finished_ = true; } +SourceCallback *GotoDepth::clone() const +{ + return new GotoDepth(target_, duration_); +} + +SourceCallback *GotoDepth::reverse(Source *s) const +{ + return new GotoDepth(s->depth(), duration_); +} + +void GotoDepth::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} SetPlay::SetPlay(bool on) : SourceCallback(), play_(on) { @@ -182,6 +258,11 @@ void SetPlay::update(Source *s, float) finished_ = true; } +SourceCallback *SetPlay::clone() const +{ + return new SetPlay(play_); +} + RePlay::RePlay() : SourceCallback() { } @@ -196,10 +277,19 @@ void RePlay::update(Source *s, float) finished_ = true; } - -Grab::Grab(float dx, float dy, float duration) : SourceCallback(), speed_(glm::vec2(dx,dy)), - duration_(duration), progress_(0.f) +SourceCallback *RePlay::clone() const { + return new RePlay; +} + +Grab::Grab() : SourceCallback(), speed_(glm::vec2(0.f)), duration_(0.f), progress_(0.f) +{ +} + +Grab::Grab(float dx, float dy, float duration) : Grab() +{ + speed_ = glm::vec2(dx,dy); + duration_ = duration; } void Grab::update(Source *s, float dt) @@ -231,9 +321,25 @@ void Grab::update(Source *s, float dt) finished_ = true; } -Resize::Resize(float dx, float dy, float duration) : SourceCallback(), speed_(glm::vec2(dx,dy)), - duration_(duration), progress_(0.f) +SourceCallback *Grab::clone() const { + return new Grab(speed_.x, speed_.y, duration_); +} + +void Grab::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} + +Resize::Resize() : SourceCallback(), speed_(glm::vec2(0.f)), duration_(0.f), progress_(0.f) +{ +} + +Resize::Resize(float dx, float dy, float duration) : Resize() +{ + speed_ = glm::vec2(dx,dy); + duration_ = duration; } void Resize::update(Source *s, float dt) @@ -265,9 +371,25 @@ void Resize::update(Source *s, float dt) finished_ = true; } -Turn::Turn(float da, float duration) : SourceCallback(), speed_(da), - duration_(duration), progress_(0.f) +SourceCallback *Resize::clone() const { + return new Resize(speed_.x, speed_.y, duration_); +} + +void Resize::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} + +Turn::Turn() : SourceCallback(), speed_(0.f), duration_(0.f), progress_(0.f) +{ +} + +Turn::Turn(float da, float duration) : Turn() +{ + speed_ = da; + duration_ = duration; } void Turn::update(Source *s, float dt) @@ -297,3 +419,14 @@ void Turn::update(Source *s, float dt) else finished_ = true; } + +SourceCallback *Turn::clone() const +{ + return new Turn(speed_, duration_); +} + +void Turn::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} diff --git a/SourceCallback.h b/SourceCallback.h index e5a3038..7f3ea05 100644 --- a/SourceCallback.h +++ b/SourceCallback.h @@ -3,6 +3,7 @@ #include +class Visitor; class Source; class SourceCallback @@ -25,12 +26,15 @@ public: SourceCallback(); virtual ~SourceCallback() {} - virtual void update(Source *, float) = 0; + virtual void update (Source *, float) = 0; + virtual SourceCallback *clone () const = 0; + virtual SourceCallback *reverse (Source *) const { return nullptr; } virtual CallbackType type () { return CALLBACK_GENERIC; } + virtual void accept (Visitor& v); - inline bool finished() const { return finished_; } - inline bool active() const { return active_; } - inline void reset() { initialized_ = false; } + inline bool finished () const { return finished_; } + inline bool active () const { return active_; } + inline void reset () { initialized_ = false; } protected: bool active_; @@ -38,24 +42,33 @@ protected: bool initialized_; }; + class ResetGeometry : public SourceCallback { - public: - ResetGeometry() : SourceCallback() {} - void update(Source *s, float) override; + ResetGeometry () : SourceCallback() {} + void update (Source *s, float) override; + SourceCallback *clone () const override; }; -class SetAlpha : public SourceCallback +class GotoAlpha : public SourceCallback { float alpha_; glm::vec2 pos_; glm::vec2 step_; public: - SetAlpha(float alpha); - void update(Source *s, float) override; + GotoAlpha (); + GotoAlpha (float alpha); + + float alpha () const { return alpha_;} + void setAlpha (float a) { alpha_ = a; } + + void update (Source *s, float) override; + SourceCallback *clone () const override; + SourceCallback *reverse(Source *s) const override; CallbackType type () override { return CALLBACK_ALPHA; } + void accept (Visitor& v) override; }; class Loom : public SourceCallback @@ -67,9 +80,18 @@ class Loom : public SourceCallback float progress_; public: - Loom(float da, float duration = 0.f); - void update(Source *s, float) override; + Loom (); + Loom (float d, float duration = 0.f); + + float delta () const { return speed_;} + void setDelta (float d) { speed_ = d; } + float duration () const { return duration_;} + void setDuration (float d) { duration_ = d; } + + void update (Source *s, float) override; + SourceCallback *clone() const override; CallbackType type () override { return CALLBACK_LOOM; } + void accept (Visitor& v) override; }; class SetLock : public SourceCallback @@ -77,12 +99,14 @@ class SetLock : public SourceCallback bool lock_; public: - SetLock(bool on); - void update(Source *s, float) override; + SetLock (bool on); + + void update (Source *s, float) override; + SourceCallback *clone() const override; CallbackType type () override { return CALLBACK_LOCK; } }; -class SetDepth : public SourceCallback +class GotoDepth : public SourceCallback { float duration_; float progress_; @@ -90,9 +114,19 @@ class SetDepth : public SourceCallback float target_; public: - SetDepth(float depth, float duration = 0.f); - void update(Source *s, float dt) override; + GotoDepth (); + GotoDepth (float depth, float duration = 0.f); + + float depth () const { return target_;} + void setDepth (float d) { target_ = d; } + float duration () const { return duration_;} + void setDuration (float d) { duration_ = d; } + + void update (Source *s, float dt) override; + SourceCallback *clone () const override; + SourceCallback *reverse(Source *s) const override; CallbackType type () override { return CALLBACK_DEPTH; } + void accept (Visitor& v) override; }; class SetPlay : public SourceCallback @@ -100,17 +134,21 @@ class SetPlay : public SourceCallback bool play_; public: - SetPlay(bool on); - void update(Source *s, float) override; + SetPlay (bool on); + bool play () const { return play_;} + + void update (Source *s, float) override; + SourceCallback *clone() const override; CallbackType type () override { return CALLBACK_PLAY; } }; class RePlay : public SourceCallback { - public: RePlay(); + void update(Source *s, float) override; + SourceCallback *clone() const override; CallbackType type () override { return CALLBACK_REPLAY; } }; @@ -122,9 +160,18 @@ class Grab : public SourceCallback float progress_; public: + Grab(); Grab(float dx, float dy, float duration = 0.f); - void update(Source *s, float) override; + + glm::vec2 delta () { return speed_;} + void setDelta (glm::vec2 d) { speed_ = d; } + float duration () const { return duration_;} + void setDuration (float d) { duration_ = d; } + + void update (Source *s, float) override; + SourceCallback *clone () const override; CallbackType type () override { return CALLBACK_GRAB; } + void accept (Visitor& v) override; }; class Resize : public SourceCallback @@ -135,9 +182,18 @@ class Resize : public SourceCallback float progress_; public: + Resize(); Resize(float dx, float dy, float duration = 0.f); - void update(Source *s, float) override; + + glm::vec2 delta () { return speed_;} + void setDelta (glm::vec2 d) { speed_ = d; } + float duration () const { return duration_;} + void setDuration (float d) { duration_ = d; } + + void update (Source *s, float) override; + SourceCallback *clone () const override; CallbackType type () override { return CALLBACK_RESIZE; } + void accept (Visitor& v) override; }; class Turn : public SourceCallback @@ -148,9 +204,18 @@ class Turn : public SourceCallback float progress_; public: + Turn(); Turn(float da, float duration = 0.f); - void update(Source *s, float) override; + + float delta () { return speed_;} + void setDelta (float d) { speed_ = d; } + float duration () const { return duration_;} + void setDuration (float d) { duration_ = d; } + + void update (Source *s, float) override; + SourceCallback *clone () const override; CallbackType type () override { return CALLBACK_TURN; } + void accept (Visitor& v) override; }; diff --git a/SourceList.cpp b/SourceList.cpp index 9923c1a..0597194 100644 --- a/SourceList.cpp +++ b/SourceList.cpp @@ -158,6 +158,15 @@ SourceLink::SourceLink(Source *s): host_(nullptr), target_(nullptr), id_(0) connect(s); } +SourceLink::SourceLink(const SourceLink &l) +{ + if (l.target_ != nullptr) + connect(l.target_); + else + host_ = l.host_; + id_ = l.id_; +} + void SourceLink::connect(uint64_t id, Session *se) { if (connected()) @@ -220,7 +229,7 @@ Source *SourceLink::source() } -SourceList validate (const SourceLinkList &list) +SourceList validateLinkList (const SourceLinkList &list) { SourceList sourcelist; @@ -237,3 +246,23 @@ SourceList validate (const SourceLinkList &list) return sourcelist; } + + +SourceLinkList getLinkList (const SourceList &list) +{ + SourceLinkList linklist; + + for( auto sit = list.begin(); sit != list.end(); ++sit) + linklist.push_back( new SourceLink(*sit) ); + + return linklist; +} + + +void clearLinkList (SourceLinkList list) +{ + for( auto sit = list.begin(); sit != list.end(); ) { + delete (*sit); + sit = list.erase(sit); + } +} diff --git a/SourceList.h b/SourceList.h index 29e7da6..4810a6a 100644 --- a/SourceList.h +++ b/SourceList.h @@ -33,6 +33,7 @@ class SourceLink { public: SourceLink(): host_(nullptr), target_(nullptr), id_(0) { } + SourceLink(const SourceLink &); SourceLink(Source *s); ~SourceLink(); @@ -51,6 +52,8 @@ protected: }; typedef std::list SourceLinkList; -SourceList validate (const SourceLinkList &list); +SourceLinkList getLinkList (const SourceList &list); +void clearLinkList (SourceLinkList list); +SourceList validateLinkList (const SourceLinkList &list); #endif // SOURCELIST_H diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 436ad0d..12d195e 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -76,6 +76,7 @@ using namespace std; #include "Selection.h" #include "FrameBuffer.h" #include "MediaPlayer.h" +#include "SourceCallback.h" #include "CloneSource.h" #include "RenderSource.h" #include "MediaSource.h" @@ -389,6 +390,17 @@ void UserInterface::handleKeyboard() // Space bar to toggle play / pause sourcecontrol.Play(); + // keys for source callbacks + else + { + ImGuiContext& g = *GImGui; + for( auto sit = Mixer::manager().session()->begin(); + sit != Mixer::manager().session()->end(); ++sit) { + (*sit)->updateCallbacks(g.IO.KeysDown); + } + } + + // normal keys in workspace // make sure no entry / window box is active if ( !ImGui::IsAnyWindowFocused() ) { @@ -1251,7 +1263,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 GotoAlpha(v)); if ( ImGui::IsItemDeactivatedAfterEdit() ) { info << "Alpha " << std::fixed << std::setprecision(3) << v; Action::manager().store(info.str()); diff --git a/Visitor.h b/Visitor.h index 03a82a1..fcee6f2 100644 --- a/Visitor.h +++ b/Visitor.h @@ -1,4 +1,4 @@ -#ifndef VISITOR_H +#ifndef VISITOR_H #define VISITOR_H #include @@ -26,6 +26,7 @@ class Shader; class ImageShader; class MaskShader; class ImageProcessingShader; + class Source; class MediaSource; class PatternSource; @@ -40,6 +41,15 @@ class NetworkSource; class MixingGroup; class MultiFileSource; +class SourceCallback; +class GotoAlpha; +class GotoDepth; +class Loom; +class Grab; +class Resize; +class Turn; + + // Declares the interface for the visitors class Visitor { @@ -84,6 +94,13 @@ public: virtual void visit (CloneSource&) {} virtual void visit (MultiFileSource&) {} + virtual void visit (SourceCallback&) {} + virtual void visit (GotoAlpha&) {} + virtual void visit (GotoDepth&) {} + virtual void visit (Loom&) {} + virtual void visit (Grab&) {} + virtual void visit (Resize&) {} + virtual void visit (Turn&) {} };