From 39b61fe3312aaa6aaf005f92ff7b87034eb72dcc Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Mon, 7 Mar 2022 00:23:24 +0100 Subject: [PATCH] Reimplementation of SourceInputCallbacks into Session Session should be the object holding the list of inputs parameters (e.g. synchrony) and the list of source callbacks. This also avoids mixing input when copying sources. Code could be improved but is operational. --- ControlManager.cpp | 14 +- ControlManager.h | 1 + Metronome.cpp | 17 ++ Metronome.h | 4 + Session.cpp | 217 ++++++++++++++++++++++ Session.h | 32 +++- SessionCreator.cpp | 83 ++++++--- SessionCreator.h | 1 + SessionVisitor.cpp | 60 ++++-- SessionVisitor.h | 1 + Source.cpp | 131 +------------ Source.h | 19 -- SourceCallback.cpp | 387 ++++++++++++++++++++++----------------- SourceCallback.h | 50 ++--- SourceList.cpp | 5 +- SourceList.h | 1 + UserInterfaceManager.cpp | 139 ++++++-------- UserInterfaceManager.h | 5 - 18 files changed, 689 insertions(+), 478 deletions(-) diff --git a/ControlManager.cpp b/ControlManager.cpp index 8dcd2ce..c4ab487 100644 --- a/ControlManager.cpp +++ b/ControlManager.cpp @@ -35,6 +35,7 @@ #include "ActionManager.h" #include "SystemToolkit.h" #include "tinyxml2Toolkit.h" +#include "Metronome.h" #include "UserInterfaceManager.h" #include "RenderingManager.h" @@ -588,7 +589,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), true ); + target->call( new Loom(x, 0.f), true ); // this will require to send feedback status about source send_feedback = true; } @@ -608,13 +609,13 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut else if ( attribute.compare(OSC_SOURCE_GRAB) == 0) { float x = 0.f, y = 0.f; arguments >> x >> y >> osc::EndMessage; - target->call( new Grab( x, y), true ); + target->call( new Grab( x, y, 0.f), true ); } /// e.g. '/vimix/current/scale ff 10.0 2.2' else if ( attribute.compare(OSC_SOURCE_RESIZE) == 0) { float x = 0.f, y = 0.f; arguments >> x >> y >> osc::EndMessage; - target->call( new Resize( x, y), true ); + target->call( new Resize( x, y, 0.f), true ); } /// e.g. '/vimix/current/turn f 1.0' else if ( attribute.compare(OSC_SOURCE_TURN) == 0) { @@ -624,7 +625,7 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut arguments >> osc::EndMessage; else // ignore second argument arguments >> y >> osc::EndMessage; - target->call( new Turn( x ), true ); + target->call( new Turn( x, 0.f), true ); } /// e.g. '/vimix/current/reset' else if ( attribute.compare(OSC_SOURCE_RESET) == 0) { @@ -871,6 +872,7 @@ void Control::sendOutputStatus(const IpEndpointName &remoteEndpoint) socket.Send( p.Data(), p.Size() ); } + void Control::keyboardCalback(GLFWwindow* window, int key, int, int action, int mods) { if (UserInterface::manager().keyboardAvailable() && !mods ) @@ -894,7 +896,7 @@ void Control::keyboardCalback(GLFWwindow* window, int key, int, int action, int } } -bool Control::inputActive(uint id) +bool Control::inputActive (uint id) { input_access_.lock(); const bool ret = input_active[MIN(id,INPUT_MAX)]; @@ -903,7 +905,7 @@ bool Control::inputActive(uint id) return ret && !Settings::application.mapping.disabled; } -float Control::inputValue(uint id) +float Control::inputValue (uint id) { input_access_.lock(); const float ret = input_values[MIN(id,INPUT_MAX)]; diff --git a/ControlManager.h b/ControlManager.h index 23e6384..67f6a42 100644 --- a/ControlManager.h +++ b/ControlManager.h @@ -102,6 +102,7 @@ public: bool inputActive (uint id); float inputValue (uint id); + float inputDelay (uint id); static std::string inputLabel(uint id); protected: diff --git a/Metronome.cpp b/Metronome.cpp index 9136fba..4706b9f 100644 --- a/Metronome.cpp +++ b/Metronome.cpp @@ -247,6 +247,23 @@ void Metronome::executeAtPhase( std::function f ) std::thread( delay, f, timeToPhase() ).detach(); } +float Metronome::timeToSync(Synchronicity sync) +{ + float ret = 0.f; + if ( sync > Metronome::SYNC_BEAT ) { + // SYNC TO PHASE + std::chrono::duration fp_ms = Metronome::manager().timeToPhase(); + ret = fp_ms.count(); + } + else if ( sync > Metronome::SYNC_NONE ) { + // SYNC TO BEAT + std::chrono::duration fp_ms = Metronome::manager().timeToBeat(); + ret = fp_ms.count(); + } + // SYNC NONE + return ret; +} + size_t Metronome::peers() const { return link_.numPeers(); diff --git a/Metronome.h b/Metronome.h index c452232..829f34d 100644 --- a/Metronome.h +++ b/Metronome.h @@ -54,6 +54,10 @@ public: std::chrono::microseconds timeToPhase(); void executeAtPhase( std::function f ); + // get time to sync, in milisecond + float timeToSync(Synchronicity sync); + + // get number of connected peers size_t peers () const; }; diff --git a/Session.cpp b/Session.cpp index 8615141..80c10fd 100644 --- a/Session.cpp +++ b/Session.cpp @@ -31,6 +31,8 @@ #include "SessionSource.h" #include "RenderSource.h" #include "MixingGroup.h" +#include "ControlManager.h" +#include "SourceCallback.h" #include "Log.h" #include "Session.h" @@ -62,6 +64,8 @@ Session::Session() : active_(true), activation_threshold_(MIXING_MIN_THRESHOLD), config_[View::TEXTURE]->scale_ = Settings::application.views[View::TEXTURE].default_scale; config_[View::TEXTURE]->translation_ = Settings::application.views[View::TEXTURE].default_translation; + input_sync_.resize(INPUT_MAX, Metronome::SYNC_NONE); + snapshots_.xmlDoc_ = new tinyxml2::XMLDocument; start_time_ = gst_util_get_timestamp (); } @@ -82,6 +86,15 @@ Session::~Session() it = deleteSource(*it); } + // delete all callbacks + for (auto iter = input_callbacks_.begin(); iter != input_callbacks_.end(); + iter = input_callbacks_.erase(iter)) { + if ( iter->second.model_ != nullptr) + delete iter->second.model_; + if ( iter->second.reverse_ != nullptr) + delete iter->second.reverse_; + } + delete config_[View::RENDERING]; delete config_[View::GEOMETRY]; delete config_[View::LAYER]; @@ -114,6 +127,50 @@ void Session::update(float dt) if ( render_.frame() == nullptr ) return; + // listen to inputs + for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ++k) + { + // get if the input is activated (e.g. key pressed) + bool input_active = Control::manager().inputActive(k->first); + + // get the callback model for each input + if ( k->second.model_ != nullptr) { + + // 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_ == nullptr) { + + // ON PRESS + if (input_active) { + // 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(); + // apply value multiplyer from input + C->multiply( Control::manager().inputValue(k->first) ); + // add delay + C->delay( Metronome::manager().timeToSync( (Metronome::Synchronicity) input_sync_[k->first] ) ); + // add callback to the source (force override) + k->second.source_->call( C, true ); + // get the reverse if the callback, and remember it (can be null) + k->second.reverse_ = C->reverse(k->second.source_); + } + // ON RELEASE + else { + // call the reverse (NB: invalid value tested in call) + k->second.source_->call( k->second.reverse_, true ); + // do not keep reference to reverse: will be deleted when terminated + k->second.reverse_ = nullptr; + } + + // remember state of callback + k->second.active_ = input_active; + } + } + } + // pre-render all sources failedSource_ = nullptr; bool ready = true; @@ -132,6 +189,8 @@ void Session::update(float dt) else { if ( !(*it)->ready() ) ready = false; + // set inputs + // update the source (*it)->setActive(activation_threshold_); (*it)->update(dt); @@ -640,3 +699,161 @@ std::string Session::save(const std::string& filename, Session *session, const s return ret; } + +void Session::assignSourceCallback(uint input, Source *source, SourceCallback *callback) +{ + // find if this callback is already assigned + auto k = input_callbacks_.begin(); + for (; k != input_callbacks_.end(); ++k) + { + // yes, then just change the source pointer + if ( k->second.model_ == callback) { + if (k->second.reverse_) + delete k->second.reverse_; + k->second.source_ = source; + break; + } + } + + // if this callback is not assigned yet (looped until end) + if ( k == input_callbacks_.end() ) { + // create new entry + std::multimap::iterator added = input_callbacks_.emplace(input, InputSourceCallback() ); + added->second.model_ = callback; + added->second.source_ = source; + } +} + +void Session::swapSourceCallback(uint from, uint to) +{ + std::multimap swapped_callbacks_; + + for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ++k) + { + if ( k->first == from ) + swapped_callbacks_.emplace( to, k->second); + else + swapped_callbacks_.emplace( k->first, k->second); + } + + input_callbacks_.swap(swapped_callbacks_); +} + +void Session::copySourceCallback(uint from, uint to) +{ + if ( input_callbacks_.count(from) > 0 ) { + auto result = input_callbacks_.equal_range(from); + for (auto it = result.first; it != result.second; ++it) { + assignSourceCallback(to, it->second.source_, it->second.model_->clone() ); + } + } +} + +std::list< std::pair > Session::getSourceCallbacks(uint input) +{ + std::list< std::pair > ret; + + if ( input_callbacks_.count(input) > 0 ) { + auto result = input_callbacks_.equal_range(input); + for (auto it = result.first; it != result.second; ++it) + ret.push_back( std::pair(it->second.source_, it->second.model_) ); + } + + return ret; +} + +void Session::deleteSourceCallback(SourceCallback *callback) +{ + for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ++k) + { + if ( k->second.model_ == callback) { + delete callback; + if (k->second.reverse_) + delete k->second.reverse_; + input_callbacks_.erase(k); + break; + } + } +} + +void Session::deleteSourceCallbacks(uint input) +{ + for (auto k = input_callbacks_.begin(); k != input_callbacks_.end();) + { + if ( k->first == input) { + if (k->second.model_) + delete k->second.model_; + if (k->second.reverse_) + delete k->second.reverse_; + input_callbacks_.erase(k); + } + else + ++k; + } +} + +void Session::deleteSourceCallbacks(Source *source) +{ + for (auto k = input_callbacks_.begin(); k != input_callbacks_.end();) + { + if ( k->second.source_ == source) { + if (k->second.model_) + delete k->second.model_; + if (k->second.reverse_) + delete k->second.reverse_; + input_callbacks_.erase(k); + } + else + ++k; + } +} + + +void Session::clearSourceCallbacks() +{ + for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ) + { + if (k->second.model_) + delete k->second.model_; + if (k->second.reverse_) + delete k->second.reverse_; + + k = input_callbacks_.erase(k); + } +} + +std::list Session::assignedInputs() +{ + std::list inputs; + + // fill with list of keys + for(const auto& [key, value] : input_callbacks_) { + inputs.push_back(key); + } + + // remove duplicates + inputs.unique(); + + return inputs; +} + +bool Session::inputAssigned(uint input) +{ + return input_callbacks_.find(input) != input_callbacks_.end(); +} + +void Session::setInputSynchrony(uint input, Metronome::Synchronicity sync) +{ + input_sync_[input] = sync; +} + +std::vector Session::getInputSynchrony() +{ + return input_sync_; +} + +Metronome::Synchronicity Session::inputSynchrony(uint input) +{ + return input_sync_[input]; +} + diff --git a/Session.h b/Session.h index 956644d..0a3f705 100644 --- a/Session.h +++ b/Session.h @@ -5,6 +5,7 @@ #include "SourceList.h" #include "RenderView.h" +#include "Metronome.h" namespace tinyxml2 { class XMLDocument; @@ -151,6 +152,22 @@ public: void removeFromPlayGroup(size_t i, Source *s); std::vector getPlayGroups() { return play_groups_; } + // callbacks associated to inputs + void assignSourceCallback(uint input, Source *source, SourceCallback *callback); + std::list< std::pair > getSourceCallbacks(uint input); + void deleteSourceCallback (SourceCallback *callback); + void deleteSourceCallbacks(Source *source); + void deleteSourceCallbacks(uint input); + void clearSourceCallbacks (); + std::list assignedInputs(); + bool inputAssigned(uint input); + void swapSourceCallback(uint from, uint to); + void copySourceCallback(uint from, uint to); + + void setInputSynchrony(uint input, Metronome::Synchronicity sync); + std::vector getInputSynchrony(); + Metronome::Synchronicity inputSynchrony(uint input); + protected: bool active_; float activation_threshold_; @@ -186,7 +203,20 @@ protected: }; Fading fading_; -// std::map input_callbacks_; + std::vector input_sync_; }; diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 239c74d..0330675 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -149,6 +149,9 @@ void SessionCreator::load(const std::string& filename) // load playlists loadPlayGroups( xmlDoc_.FirstChildElement("PlayGroups") ); + // load input callbacks + loadInputCallbacks( xmlDoc_.FirstChildElement("InputCallbacks") ); + // thumbnail const XMLElement *thumbnailelement = sessionNode->FirstChildElement("Thumbnail"); // if there is a user-defined thumbnail, get it @@ -193,6 +196,62 @@ void SessionCreator::loadSnapshots(XMLElement *snapshotsNode) } } + +void SessionCreator::loadInputCallbacks(tinyxml2::XMLElement *inputsNode) +{ + if (inputsNode != nullptr && session_ != nullptr) { + + // read all 'Callback' nodes + xmlCurrent_ = inputsNode->FirstChildElement("Callback"); + for ( ; xmlCurrent_ ; xmlCurrent_ = xmlCurrent_->NextSiblingElement()) { + // what key triggers the callback ? + uint input = 0; + xmlCurrent_->QueryUnsignedAttribute("input", &input); + if (input > 0) { + // what id is the source ? + uint64_t id = 0; + xmlCurrent_->QueryUnsigned64Attribute("id", &id); + if (id > 0) { + // what type is the callback ? + uint type = 0; + xmlCurrent_->QueryUnsignedAttribute("type", &type); + // instanciate the callback of that type + SourceCallback *loadedcallback = SourceCallback::create((SourceCallback::CallbackType)type); + // successfully created a callback of saved type + if (loadedcallback) { + // apply specific parameters + loadedcallback->accept(*this); + // find the source with the given id + SourceList::iterator sit = session_->find(id); + if (sit != session_->end()) { + // assign to source in session + session_->assignSourceCallback(input, *sit, loadedcallback); + } + } + } + } + } + + // read array of synchronyzation mode for all inputs (CSV) + xmlCurrent_ = inputsNode->FirstChildElement("Synchrony"); + if (xmlCurrent_) { + const char *text = xmlCurrent_->GetText(); + if (text) { + std::istringstream iss(text); + std::string token; + uint i = 0; + while(std::getline(iss, token, ';')) { + if (token.compare("1") == 0) + session_->setInputSynchrony(i, Metronome::SYNC_BEAT); + else if (token.compare("2") == 0) + session_->setInputSynchrony(i, Metronome::SYNC_PHASE); + ++i; + } + } + } + } +} + void SessionCreator::loadNotes(XMLElement *notesNode) { if (notesNode != nullptr && session_ != nullptr) { @@ -900,30 +959,6 @@ 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 ? - uint input = 0; - xmlCurrent_->QueryUnsignedAttribute("input", &input); - // what type is the callback ? - uint type = 0; - xmlCurrent_->QueryUnsignedAttribute("type", &type); - // instanciate the callback of that type - SourceCallback *loadedcallback = SourceCallback::create((SourceCallback::CallbackType)type); - // successfully created a callback of saved type - if (loadedcallback) { - // apply specific parameters - loadedcallback->accept(*this); - // add callback to source - s.addInputCallback(input, loadedcallback); - } - } - - } - // restore current xmlCurrent_ = sourceNode; } diff --git a/SessionCreator.h b/SessionCreator.h index 1d8e8fa..892a33e 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -112,6 +112,7 @@ class SessionCreator : public SessionLoader { void loadNotes(tinyxml2::XMLElement *notesNode); void loadPlayGroups(tinyxml2::XMLElement *playlistsNode); void loadSnapshots(tinyxml2::XMLElement *snapshotNode); + void loadInputCallbacks(tinyxml2::XMLElement *inputsNode); public: SessionCreator(int recursion = 0); diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 65d6062..118748e 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -110,6 +110,9 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session) // 5. optional playlists savePlayGroups( &xmlDoc, session ); + // 5. optional playlists + saveInputCallbacks( &xmlDoc, session ); + // save file to disk return ( XMLSaveDoc(&xmlDoc, filename) ); } @@ -202,6 +205,47 @@ void SessionVisitor::savePlayGroups(tinyxml2::XMLDocument *doc, Session *session } } +void SessionVisitor::saveInputCallbacks(tinyxml2::XMLDocument *doc, Session *session) +{ + // invalid inputs + if (doc != nullptr && session != nullptr) + { + // create node + XMLElement *inputsNode = doc->NewElement("InputCallbacks"); + doc->InsertEndChild(inputsNode); + + // list of inputs assigned in the session + std::list inputs = session->assignedInputs(); + if (!inputs.empty()) { + // loop over list of inputs + for (auto i = inputs.begin(); i != inputs.end(); ++i) { + // get all callbacks for this input + auto result = session->getSourceCallbacks(*i); + for (auto kit = result.cbegin(); kit != result.cend(); ++kit) { + // create node for this callback + XMLElement *cbNode = doc->NewElement("Callback"); + cbNode->SetAttribute("input", *i); + cbNode->SetAttribute("id", kit->first->id()); + inputsNode->InsertEndChild(cbNode); + // launch visitor to complete attributes + SessionVisitor sv(doc, cbNode); + kit->second->accept(sv); + } + } + } + + // save array of synchronyzation mode for all inputs (CSV) + std::ostringstream oss; + std::vector synch = session->getInputSynchrony(); + for (auto token = synch.begin(); token != synch.end(); ++token) + oss << (int) *token << ';'; + XMLElement *syncNode = doc->NewElement("Synchrony"); + XMLText *text = doc->NewText( oss.str().c_str() ); + syncNode->InsertEndChild(text); + inputsNode->InsertEndChild(syncNode); + } +} + SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc, tinyxml2::XMLElement *root, bool recursive) : Visitor(), recursive_(recursive), xmlCurrent_(root) @@ -580,22 +624,6 @@ void SessionVisitor::visit (Source& s) s.mixingGroup()->accept(*this); } - std::list inputs = s.callbackInputs(); - if (!inputs.empty()) { - // list of callbacks - XMLElement *callbackselement = xmlDoc_->NewElement( "Callbacks" ); - sourceNode->InsertEndChild(callbackselement); - for (auto i = inputs.begin(); i != inputs.end(); ++i) { - std::list callbacks = s.inputCallbacks(*i); - for (auto c = callbacks.begin(); c != callbacks.end(); ++c) { - xmlCurrent_ = xmlDoc_->NewElement( "Callback" ); - callbackselement->InsertEndChild(xmlCurrent_); - xmlCurrent_->SetAttribute("input", *i); - (*c)->accept(*this); - } - } - } - xmlCurrent_ = sourceNode; // parent for next visits (other subtypes of Source) } diff --git a/SessionVisitor.h b/SessionVisitor.h index ea4cdef..4e02047 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -19,6 +19,7 @@ class SessionVisitor : public Visitor { static void saveSnapshots(tinyxml2::XMLDocument *doc, Session *session); static void saveNotes(tinyxml2::XMLDocument *doc, Session *session); static void savePlayGroups(tinyxml2::XMLDocument *doc, Session *session); + static void saveInputCallbacks(tinyxml2::XMLDocument *doc, Session *session); public: SessionVisitor(tinyxml2::XMLDocument *doc = nullptr, diff --git a/Source.cpp b/Source.cpp index 0ce3e8f..80257d9 100644 --- a/Source.cpp +++ b/Source.cpp @@ -34,6 +34,7 @@ #include "BaseToolkit.h" #include "SystemToolkit.h" #include "MixingGroup.h" +#include "Metronome.h" #include "ControlManager.h" #include "SourceCallback.h" @@ -360,16 +361,6 @@ Source::~Source() delete callback; } access_callbacks_.unlock(); - - // clear and delete key_callbacks - for (auto iter = input_callbacks_.begin(); iter != input_callbacks_.end(); - iter = input_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) @@ -678,125 +669,8 @@ void Source::call(SourceCallback *callback, bool override) } -void Source::addInputCallback(uint input, SourceCallback *callback) -{ - std::multimap::iterator added = input_callbacks_.emplace(input, InputCallback() ); - added->second.model_ = callback; -} - -void Source::swapInputCallback(uint from, uint to) -{ - std::multimap swapped_callbacks_; - - for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ++k) - { - if ( k->first == from ) - swapped_callbacks_.emplace( to, k->second); - else - swapped_callbacks_.emplace( k->first, k->second); - } - - input_callbacks_.swap(swapped_callbacks_); -} - - -void Source::removeInputCallback(SourceCallback *callback) -{ - for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ++k) - { - if ( k->second.model_ == callback) { - delete callback; - if (k->second.reverse_) - delete k->second.reverse_; - input_callbacks_.erase(k); - break; - } - } -} - -void Source::clearInputCallbacks() -{ - for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ) - { - if (k->second.model_) - delete k->second.model_; - if (k->second.reverse_) - delete k->second.reverse_; - - k = input_callbacks_.erase(k); - } -} - -std::list Source::inputCallbacks(uint input) -{ - std::list ret; - - if ( input_callbacks_.count(input) > 0 ) { - auto result = input_callbacks_.equal_range(input); - for (auto it = result.first; it != result.second; ++it) - ret.push_back( it->second.model_ ); - } - - return ret; -} - - -std::list Source::callbackInputs() -{ - std::list inputs; - - // fill with list of keys - for(const auto& [key, value] : input_callbacks_) { - inputs.push_back(key); - } - - // remove duplicates - inputs.unique(); - - return inputs; -} - void Source::updateCallbacks(float dt) { - // add callbacks from listener - for (auto k = input_callbacks_.begin(); k != input_callbacks_.end(); ++k) - { - // if the Reaction is valid - if ( k->second.model_ != nullptr) { - - bool activate = Control::manager().inputActive(k->first); - - // if the value referenced as pressed changed state - // or repeat key if there is no reverse callback - if ( activate != k->second.active_ || k->second.reverse_ == nullptr) { - - // ON PRESS - if (activate) { - // generate a new callback from the model - SourceCallback *C = k->second.model_->clone(); - // apply value multiplyer from input - C->multiply( Control::manager().inputValue(k->first) ); - // add callback to the source (force override) - call( C, true ); - // delete the reverse if was not released - if (k->second.reverse_ != nullptr) - delete k->second.reverse_; - // 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.active_ = activate; - } - } - } - // lock access to callbacks list access_callbacks_.lock(); // call callback functions @@ -819,6 +693,7 @@ void Source::updateCallbacks(float dt) } // release access to callbacks list access_callbacks_.unlock(); + } CloneSource *Source::clone(uint64_t id) @@ -838,7 +713,7 @@ void Source::update(float dt) // if update is possible if (renderbuffer_ && mixingsurface_ && maskbuffer_) { - // react to input and call active callbacks + // call active callbacks updateCallbacks(dt); // update nodes if needed diff --git a/Source.h b/Source.h index 6675ee0..8504495 100644 --- a/Source.h +++ b/Source.h @@ -148,14 +148,6 @@ public: // add callback to each update void call(SourceCallback *callback, bool override = false); - // callbacks associated to keys - void addInputCallback(uint input, SourceCallback *callback); - void removeInputCallback(SourceCallback *callback); - std::list inputCallbacks(uint input); - std::list callbackInputs(); - void swapInputCallback(uint from, uint to); - void clearInputCallbacks(); - // update mode inline bool active () const { return active_; } virtual void setActive (bool on); @@ -325,17 +317,6 @@ protected: // callbacks std::list update_callbacks_; std::mutex access_callbacks_; - struct InputCallback { - bool active_; - SourceCallback *model_; - SourceCallback *reverse_; - InputCallback() { - active_ = false; - model_ = nullptr; - reverse_ = nullptr; - } - }; - std::multimap input_callbacks_; void updateCallbacks(float dt); // clones diff --git a/SourceCallback.cpp b/SourceCallback.cpp index 79d45cf..cb4ae34 100644 --- a/SourceCallback.cpp +++ b/SourceCallback.cpp @@ -113,7 +113,7 @@ bool SourceCallback::overlap( SourceCallback *a, SourceCallback *b) return ret; } -SourceCallback::SourceCallback(): finished_(false), initialized_(false) +SourceCallback::SourceCallback(): status_(PENDING), delay_(0.f), elapsed_(0.f) { } @@ -122,14 +122,39 @@ void SourceCallback::accept(Visitor& v) v.visit(*this); } -void ResetGeometry::update(Source *s, float) +void SourceCallback::update (Source *s, float dt) { - s->group(View::GEOMETRY)->scale_ = glm::vec3(1.f); - s->group(View::GEOMETRY)->rotation_.z = 0; - s->group(View::GEOMETRY)->crop_ = glm::vec3(1.f); - s->group(View::GEOMETRY)->translation_ = glm::vec3(0.f); - s->touch(); - finished_ = true; + if (s != nullptr) { + // time passed + elapsed_ += dt; + // wait for delay to start + if ( status_ == PENDING && elapsed_ > delay_ ) + status_ = READY; + } + // invalid + else + status_ = FINISHED; + +} + +void ResetGeometry::update(Source *s, float dt) +{ + SourceCallback::update(s, dt); + + if (s->locked()) + status_ = FINISHED; + + // apply when ready + if ( status_ == READY ){ + + s->group(View::GEOMETRY)->scale_ = glm::vec3(1.f); + s->group(View::GEOMETRY)->rotation_.z = 0; + s->group(View::GEOMETRY)->crop_ = glm::vec3(1.f); + s->group(View::GEOMETRY)->translation_ = glm::vec3(0.f); + s->touch(); + + status_ = FINISHED; + } } SourceCallback *ResetGeometry::clone() const @@ -137,8 +162,9 @@ SourceCallback *ResetGeometry::clone() const return new ResetGeometry; } + SetAlpha::SetAlpha(float alpha, float ms, bool revert) : SourceCallback(), - duration_(ms), progress_(0.f), alpha_(alpha), bidirectional_(revert) + duration_(ms), alpha_(alpha), bidirectional_(revert) { alpha_ = CLAMP(alpha_, 0.f, 1.f); start_ = glm::vec2(); @@ -147,60 +173,63 @@ SetAlpha::SetAlpha(float alpha, float ms, bool revert) : SourceCallback(), void SetAlpha::update(Source *s, float dt) { - if (s && !s->locked()) { - // set start position on first run or upon call of reset() - if (!initialized_){ - // initial mixing view position - start_ = glm::vec2(s->group(View::MIXING)->translation_); + SourceCallback::update(s, dt); - // step in diagonal by default - glm::vec2 step = glm::normalize(glm::vec2(1.f, 1.f)); - // step in direction of source translation if possible - if ( glm::length(start_) > DELTA_ALPHA) - step = glm::normalize(start_); - // - // target mixing view position - // - // special case Alpha = 0 - if (alpha_ < DELTA_ALPHA) { - target_ = step; - } - // special case Alpha = 1 - else if (alpha_ > 1.f - DELTA_ALPHA) { - target_ = step * 0.005f; - } - // general case - else { - // converge to reduce the difference of alpha using dichotomic algorithm - target_ = start_; - float delta = 1.f; - do { - target_ += step * (delta / 2.f); - delta = SourceCore::alphaFromCordinates(target_.x, target_.y) - alpha_; - } while (glm::abs(delta) > DELTA_ALPHA); - } + if (s->locked()) + status_ = FINISHED; - initialized_ = true; + // set start position on first time it is ready + if ( status_ == READY ){ + // initial mixing view position + start_ = glm::vec2(s->group(View::MIXING)->translation_); + + // step in diagonal by default + glm::vec2 step = glm::normalize(glm::vec2(1.f, 1.f)); + // step in direction of source translation if possible + if ( glm::length(start_) > DELTA_ALPHA) + step = glm::normalize(start_); + // + // target mixing view position + // + // special case Alpha = 0 + if (alpha_ < DELTA_ALPHA) { + target_ = step; + } + // special case Alpha = 1 + else if (alpha_ > 1.f - DELTA_ALPHA) { + target_ = step * 0.005f; + } + // general case + else { + // converge to reduce the difference of alpha using dichotomic algorithm + target_ = start_; + float delta = 1.f; + do { + target_ += step * (delta / 2.f); + delta = SourceCore::alphaFromCordinates(target_.x, target_.y) - alpha_; + } while (glm::abs(delta) > DELTA_ALPHA); } - // time passed - progress_ += dt; + status_ = ACTIVE; + } + + if ( status_ == ACTIVE ) { + // time passed since start + float progress = elapsed_ - delay_; // perform movement if ( ABS(duration_) > 0.f) - s->group(View::MIXING)->translation_ = glm::vec3(start_ + (progress_/duration_)*(target_ - start_), s->group(View::MIXING)->translation_.z); + s->group(View::MIXING)->translation_ = glm::vec3(start_ + (progress/duration_)*(target_ - start_), s->group(View::MIXING)->translation_.z); // time-out - if ( progress_ > duration_ ) { + if ( progress > duration_ ) { // apply alpha to target s->group(View::MIXING)->translation_ = glm::vec3(target_, s->group(View::MIXING)->translation_.z); // done - finished_ = true; + status_ = FINISHED; } - } - else - finished_ = true; + } void SetAlpha::multiply (float factor) @@ -233,7 +262,7 @@ void Lock::update(Source *s, float) if (s) s->setLocked(lock_); - finished_ = true; + status_ = FINISHED; } SourceCallback *Lock::clone() const @@ -243,7 +272,7 @@ SourceCallback *Lock::clone() const Loom::Loom(float speed, float ms) : SourceCallback(), - speed_(speed), duration_(ms), progress_(0.f) + speed_(speed), duration_(ms) { pos_ = glm::vec2(); step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default @@ -251,22 +280,24 @@ Loom::Loom(float speed, float ms) : SourceCallback(), void Loom::update(Source *s, float dt) { - if (s && !s->locked()) { - // reset on first run or upon call of reset() - if (!initialized_){ - // start animation - progress_ = 0.f; - // 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_); + SourceCallback::update(s, dt); - initialized_ = true; - } + if (s->locked()) + status_ = FINISHED; - // time passed - progress_ += dt; + // 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_); + status_ = ACTIVE; + } + + if ( status_ == ACTIVE ) { + // time passed since start + 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 ); @@ -275,15 +306,14 @@ void Loom::update(Source *s, float dt) 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); + else + status_ = FINISHED; // time-out - if ( progress_ > duration_ ) { + if ( progress > duration_ ) // done - finished_ = true; - } + status_ = FINISHED; } - else - finished_ = true; } void Loom::multiply (float factor) @@ -296,6 +326,11 @@ 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); @@ -303,42 +338,43 @@ void Loom::accept(Visitor& v) } - SetDepth::SetDepth(float target, float ms, bool revert) : SourceCallback(), - duration_(ms), progress_(0.f), start_(0.f), target_(target), bidirectional_(revert) + duration_(ms), start_(0.f), target_(target), bidirectional_(revert) { target_ = CLAMP(target_, MIN_DEPTH, MAX_DEPTH); } void SetDepth::update(Source *s, float dt) { - if (s && !s->locked()) { - // set start position on first run or upon call of reset() - if (!initialized_){ - start_ = s->group(View::LAYER)->translation_.z; - progress_ = 0.f; - initialized_ = true; - } + SourceCallback::update(s, dt); - // time passed - progress_ += dt; + if (s->locked()) + status_ = FINISHED; + + // set start position on first time it is ready + if ( status_ == READY ){ + start_ = s->group(View::LAYER)->translation_.z; + status_ = ACTIVE; + } + + if ( status_ == ACTIVE ) { + // time passed since start + float progress = elapsed_ - delay_; // perform movement if ( ABS(duration_) > 0.f) - s->group(View::LAYER)->translation_.z = start_ + (progress_/duration_) * (target_ - start_); + s->group(View::LAYER)->translation_.z = start_ + (progress/duration_) * (target_ - start_); // time-out - if ( progress_ > duration_ ) { + if ( progress > duration_ ) { // apply depth to target s->group(View::LAYER)->translation_.z = target_; // ensure reordering of view ++View::need_deep_update_; // done - finished_ = true; + status_ = FINISHED; } } - else - finished_ = true; } void SetDepth::multiply (float factor) @@ -366,14 +402,20 @@ Play::Play(bool on, bool revert) : SourceCallback(), play_(on), bidirectional_(r { } -void Play::update(Source *s, float) +void Play::update(Source *s, float dt) { - if (s && s->playing() != play_) { - // call play function - s->play(play_); + SourceCallback::update(s, dt); + + // toggle play status when ready + if ( status_ == READY ){ + + if (s->playing() != play_) + // call play function + s->play(play_); + + status_ = FINISHED; } - finished_ = true; } SourceCallback *Play::clone() const @@ -396,14 +438,18 @@ RePlay::RePlay() : SourceCallback() { } -void RePlay::update(Source *s, float) +void RePlay::update(Source *s, float dt) { - if (s) { + SourceCallback::update(s, dt); + + // apply when ready + if ( status_ == READY ){ + // call replay function s->replay(); - } - finished_ = true; + status_ = FINISHED; + } } SourceCallback *RePlay::clone() const @@ -413,7 +459,7 @@ SourceCallback *RePlay::clone() const SetGeometry::SetGeometry(const Group *g, float ms, bool revert) : SourceCallback(), - duration_(ms), progress_(0.f), bidirectional_(revert) + duration_(ms), bidirectional_(revert) { setTarget(g); } @@ -432,20 +478,24 @@ void SetGeometry::setTarget (const Group *g) void SetGeometry::update(Source *s, float dt) { - if (s && !s->locked()) { - // set start position on first run or upon call of reset() - if (!initialized_){ - start_.copyTransform(s->group(View::GEOMETRY)); - progress_ = 0.f; - initialized_ = true; - } + SourceCallback::update(s, dt); - // time passed - progress_ += dt; + if (s->locked()) + status_ = FINISHED; + + // set start position on first time it is ready + if ( status_ == READY ){ + start_.copyTransform(s->group(View::GEOMETRY)); + status_ = ACTIVE; + } + + if ( status_ == ACTIVE ) { + // time passed since start + float progress = elapsed_ - delay_; // perform movement if ( ABS(duration_) > 0.f){ - float ratio = progress_ / duration_; + float ratio = progress / duration_; Group intermediate; intermediate.translation_ = (1.f - ratio) * start_.translation_ + ratio * target_.translation_; intermediate.scale_ = (1.f - ratio) * start_.scale_ + ratio * target_.scale_; @@ -456,22 +506,19 @@ void SetGeometry::update(Source *s, float dt) } // time-out - if ( progress_ > duration_ ) { + if ( progress > duration_ ) { // apply target s->group(View::GEOMETRY)->copyTransform(&target_); s->touch(); // done - finished_ = true; + status_ = FINISHED; } - } - else - finished_ = true; } void SetGeometry::multiply (float factor) { - + // TODO } SourceCallback *SetGeometry::clone() const @@ -493,37 +540,35 @@ void SetGeometry::accept(Visitor& v) Grab::Grab(float dx, float dy, float ms) : SourceCallback(), - speed_(glm::vec2(dx, dy)), duration_(ms), progress_(0.f) + speed_(glm::vec2(dx, dy)), duration_(ms) { } void Grab::update(Source *s, float dt) { - if (s && !s->locked()) { - // reset on first run or upon call of reset() - if (!initialized_){ - // start animation - progress_ = 0.f; - // initial position - start_ = glm::vec2(s->group(View::GEOMETRY)->translation_); - initialized_ = true; - } + SourceCallback::update(s, dt); - // time passed - progress_ += dt; + if (s->locked()) + status_ = FINISHED; + + // 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) - glm::vec2 pos = start_ + speed_ * ( dt * 0.001f); - s->group(View::GEOMETRY)->translation_ = glm::vec3(pos, s->group(View::GEOMETRY)->translation_.z); + pos_ += speed_ * ( dt * 0.001f); + s->group(View::GEOMETRY)->translation_ = glm::vec3(pos_, s->group(View::GEOMETRY)->translation_.z); // time-out - if ( progress_ > duration_ ) { + if ( (elapsed_ - delay_) > duration_ ) // done - finished_ = true; - } + status_ = FINISHED; } - else - finished_ = true; } void Grab::multiply (float factor) @@ -536,6 +581,11 @@ 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); @@ -543,37 +593,29 @@ void Grab::accept(Visitor& v) } Resize::Resize(float dx, float dy, float ms) : SourceCallback(), - speed_(glm::vec2(dx, dy)), duration_(ms), progress_(0.f) + speed_(glm::vec2(dx, dy)), duration_(ms) { } void Resize::update(Source *s, float dt) { - if (s && !s->locked()) { - // reset on first run or upon call of reset() - if (!initialized_){ - // start animation - progress_ = 0.f; - // initial position - start_ = glm::vec2(s->group(View::GEOMETRY)->scale_); - initialized_ = true; - } + SourceCallback::update(s, dt); - // time passed - progress_ += dt; + if (s->locked()) + status_ = FINISHED; + if ( status_ == READY ) + status_ = ACTIVE; + + if ( status_ == ACTIVE ) { // move target by speed vector * time (in second) - glm::vec2 scale = start_ + speed_ * ( dt * 0.001f); + glm::vec2 scale = glm::vec2(s->group(View::GEOMETRY)->scale_) + speed_ * ( dt * 0.001f ); s->group(View::GEOMETRY)->scale_ = glm::vec3(scale, s->group(View::GEOMETRY)->scale_.z); // time-out - if ( progress_ > duration_ ) { - // done - finished_ = true; - } + if ( (elapsed_ - delay_) > duration_ ) + status_ = FINISHED; } - else - finished_ = true; } void Resize::multiply (float factor) @@ -586,6 +628,11 @@ 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); @@ -593,36 +640,35 @@ void Resize::accept(Visitor& v) } Turn::Turn(float speed, float ms) : SourceCallback(), - speed_(speed), duration_(ms), progress_(0.f) + speed_(speed), duration_(ms) { } void Turn::update(Source *s, float dt) { - if (s && !s->locked()) { - // reset on first run or upon call of reset() - if (!initialized_){ - // start animation - progress_ = 0.f; - // initial position - start_ = s->group(View::GEOMETRY)->rotation_.z; - initialized_ = true; - } + SourceCallback::update(s, dt); - // calculate amplitude of movement - progress_ += dt; + if (s->locked()) + status_ = FINISHED; + + // 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 ) { // perform movement - s->group(View::GEOMETRY)->rotation_.z = start_ - speed_ * ( dt * 0.001f ); + angle_ -= speed_ * ( dt * 0.001f ); + s->group(View::GEOMETRY)->rotation_.z = angle_; // timeout - if ( progress_ > duration_ ) { + if ( (elapsed_ - delay_) > duration_ ) // done - finished_ = true; - } + status_ = FINISHED; } - else - finished_ = true; } void Turn::multiply (float factor) @@ -635,6 +681,11 @@ 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/SourceCallback.h b/SourceCallback.h index b87b386..ec93545 100644 --- a/SourceCallback.h +++ b/SourceCallback.h @@ -33,25 +33,34 @@ public: SourceCallback(); virtual ~SourceCallback() {} - virtual void update (Source *, float) = 0; + virtual void update (Source *, float); virtual void multiply (float) {}; virtual SourceCallback *clone () const = 0; virtual SourceCallback *reverse (Source *) const { return nullptr; } virtual CallbackType type () const { return CALLBACK_GENERIC; } virtual void accept (Visitor& v); - inline bool finished () const { return finished_; } - inline void reset () { initialized_ = false; } + inline bool finished () const { return status_ > ACTIVE; } + inline void reset () { status_ = PENDING; } + inline void delay (float milisec) { delay_ = milisec;} protected: - bool finished_; - bool initialized_; + + typedef enum { + PENDING = 0, + READY, + ACTIVE, + FINISHED + } state; + + state status_; + float delay_; + float elapsed_; }; class SetAlpha : public SourceCallback { float duration_; - float progress_; float alpha_; glm::vec2 start_; glm::vec2 target_; @@ -81,10 +90,9 @@ class Loom : public SourceCallback glm::vec2 pos_; glm::vec2 step_; float duration_; - float progress_; public: - Loom (float speed = 0.f, float ms = 0.f); + Loom (float speed = 0.f, float ms = FLT_MAX); float value () const { return speed_; } void setValue (float d) { speed_ = d; } @@ -94,6 +102,7 @@ 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; }; @@ -116,7 +125,6 @@ public: class SetDepth : public SourceCallback { float duration_; - float progress_; float start_; float target_; bool bidirectional_; @@ -152,7 +160,7 @@ public: bool bidirectional () const { return bidirectional_;} void setBidirectional (bool on) { bidirectional_ = on; } - void update (Source *s, float) override; + void update (Source *s, float dt) override; SourceCallback *clone() const override; SourceCallback *reverse(Source *s) const override; CallbackType type () const override { return CALLBACK_PLAY; } @@ -164,7 +172,7 @@ class RePlay : public SourceCallback public: RePlay(); - void update(Source *s, float) override; + void update(Source *s, float dt) override; SourceCallback *clone() const override; CallbackType type () const override { return CALLBACK_REPLAY; } }; @@ -173,7 +181,7 @@ class ResetGeometry : public SourceCallback { public: ResetGeometry () : SourceCallback() {} - void update (Source *s, float) override; + void update (Source *s, float dt) override; SourceCallback *clone () const override; CallbackType type () const override { return CALLBACK_RESETGEO; } }; @@ -181,7 +189,6 @@ public: class SetGeometry : public SourceCallback { float duration_; - float progress_; Group start_; Group target_; bool bidirectional_; @@ -207,12 +214,11 @@ public: class Grab : public SourceCallback { glm::vec2 speed_; - glm::vec2 start_; + glm::vec2 pos_; float duration_; - float progress_; public: - Grab(float dx = 0.f, float dy = 0.f, float ms = 0.f); + Grab(float dx = 0.f, float dy = 0.f, float ms = FLT_MAX); glm::vec2 value () const { return speed_; } void setValue (glm::vec2 d) { speed_ = d; } @@ -222,6 +228,7 @@ 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; }; @@ -229,12 +236,10 @@ public: class Resize : public SourceCallback { glm::vec2 speed_; - glm::vec2 start_; float duration_; - float progress_; public: - Resize(float dx = 0.f, float dy = 0.f, float ms = 0.f); + Resize(float dx = 0.f, float dy = 0.f, float ms = FLT_MAX); glm::vec2 value () const { return speed_; } void setValue (glm::vec2 d) { speed_ = d; } @@ -244,6 +249,7 @@ 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; }; @@ -251,12 +257,11 @@ public: class Turn : public SourceCallback { float speed_; - float start_; + float angle_; float duration_; - float progress_; public: - Turn(float speed = 0.f, float ms = 0.f); + Turn(float speed = 0.f, float ms = FLT_MAX); float value () const { return speed_; } void setValue (float d) { speed_ = d; } @@ -266,6 +271,7 @@ 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/SourceList.cpp b/SourceList.cpp index 0597194..7a7b4ab 100644 --- a/SourceList.cpp +++ b/SourceList.cpp @@ -151,7 +151,10 @@ SourceList join (const SourceList &first, const SourceList &second) return l; } - +SourceLink::SourceLink(uint64_t id, Session *se): host_(nullptr), target_(nullptr), id_(0) +{ + connect(id, se); +} SourceLink::SourceLink(Source *s): host_(nullptr), target_(nullptr), id_(0) { diff --git a/SourceList.h b/SourceList.h index 4810a6a..69c1460 100644 --- a/SourceList.h +++ b/SourceList.h @@ -34,6 +34,7 @@ class SourceLink { public: SourceLink(): host_(nullptr), target_(nullptr), id_(0) { } SourceLink(const SourceLink &); + SourceLink(uint64_t id, Session *se); SourceLink(Source *s); ~SourceLink(); diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 2785121..93669be 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -4587,26 +4587,6 @@ void InputMappingInterface::SliderParametersCallback(SourceCallback *callback, S } } -void InputMappingInterface::readInputSource() -{ - // clear - input_sources_callbacks.clear(); - memset(input_assigned, 0, sizeof(input_assigned)); - - // loop over sources of the session - Session *ses = Mixer::manager().session(); - for (auto sit = ses->begin(); sit != ses->end(); ++sit) { - // loop over registered keys - std::list inputs = (*sit)->callbackInputs(); - for (auto k = inputs.begin(); k != inputs.end(); ++k) { - // add entry in input map - std::list callbacks = (*sit)->inputCallbacks(*k); - for (auto c = callbacks.begin(); c != callbacks.end(); ++c ) - input_sources_callbacks.emplace( *k, std::pair(*sit, *c) ); - input_assigned[*k] = true; - } - } -} void InputMappingInterface::Render() { @@ -4630,8 +4610,8 @@ void InputMappingInterface::Render() return; } - // Update internal data structures - readInputSource(); + // short variable to refer to session + Session *S = Mixer::manager().session(); // menu (no title bar) if (ImGui::BeginMenuBar()) @@ -4645,11 +4625,8 @@ void InputMappingInterface::Render() ImGui::MenuItem( ICON_FA_BAN " Disable", nullptr, &Settings::application.mapping.disabled); // Clear all - if ( ImGui::MenuItem( ICON_FA_BACKSPACE " Clear all" ) ){ - Session *ses = Mixer::manager().session(); - for (auto sit = ses->begin(); sit != ses->end(); ++sit) - (*sit)->clearInputCallbacks(); - } + if ( ImGui::MenuItem( ICON_FA_BACKSPACE " Clear all" ) ) + S->clearSourceCallbacks(); // output manager menu ImGui::Separator(); @@ -4684,29 +4661,24 @@ void InputMappingInterface::Render() const std::string keymenu = ICON_FA_HAND_POINT_RIGHT " Input " + Control::manager().inputLabel(current_input_); if (ImGui::BeginMenu(keymenu.c_str()) ) { - if ( ImGui::MenuItem( ICON_FA_WINDOW_CLOSE " Reset mapping", NULL, false, input_assigned[current_input_] ) ){ + if ( ImGui::MenuItem( ICON_FA_WINDOW_CLOSE " Reset mapping", NULL, false, S->inputAssigned(current_input_) ) ) // remove all source callback of this input - auto current_source_callback = input_sources_callbacks.equal_range(current_input_); - for (auto kit = current_source_callback.first; kit != current_source_callback.second; ++kit) - kit->second.first->removeInputCallback(kit->second.second); - // internal data structure changed - readInputSource(); - } + S->deleteSourceCallbacks(current_input_); - if (ImGui::BeginMenu(ICON_FA_CLOCK " Metronome", input_assigned[current_input_])) + if (ImGui::BeginMenu(ICON_FA_CLOCK " Metronome", S->inputAssigned(current_input_))) { - Metronome::Synchronicity sync = Metronome::SYNC_NONE; + Metronome::Synchronicity sync = S->inputSynchrony(current_input_); bool active = sync == Metronome::SYNC_NONE; if (ImGuiToolkit::MenuItemIcon(5, 13, " Not synchronized", active )){ - + S->setInputSynchrony(current_input_, Metronome::SYNC_NONE); } active = sync == Metronome::SYNC_BEAT; if (ImGuiToolkit::MenuItemIcon(6, 13, " Sync to beat", active )){ - + S->setInputSynchrony(current_input_, Metronome::SYNC_BEAT); } active = sync == Metronome::SYNC_PHASE; if (ImGuiToolkit::MenuItemIcon(7, 13, " Sync to phase", active )){ - + S->setInputSynchrony(current_input_, Metronome::SYNC_PHASE); } ImGui::EndMenu(); } @@ -4716,17 +4688,13 @@ void InputMappingInterface::Render() // static var for the copy-paste mechanism static uint _copy_current_input = INPUT_UNDEFINED; // 2) Copy (if there are callbacks assigned) - if (ImGui::MenuItem("Copy", NULL, false, input_assigned[current_input_] )) + if (ImGui::MenuItem("Copy", NULL, false, S->inputAssigned(current_input_) )) // Remember the index of the input to copy _copy_current_input = current_input_; // 3) Paste (if copied input index is assigned) - if (ImGui::MenuItem("Paste", NULL, false, input_assigned[_copy_current_input] )) { - // create source callbacks at current input cloning those of the copied index - auto copy_source_callback = input_sources_callbacks.equal_range(_copy_current_input); - for (auto kit = copy_source_callback.first; kit != copy_source_callback.second; ++kit) - kit->second.first->addInputCallback(current_input_, kit->second.second->clone()); - // internal data structure changed - readInputSource(); + if (ImGui::MenuItem("Paste", NULL, false, S->inputAssigned(current_input_) )) { + // copy source callbacks from rememberd index to current input index + S->copySourceCallback(_copy_current_input, current_input_); } ImGui::EndMenu(); } @@ -4768,13 +4736,14 @@ void InputMappingInterface::Render() } // draw key button ImGui::PushID(ik); - if (ImGui::Selectable(Control::manager().inputLabel(ik).c_str(), input_assigned[ik], 0, keyLetterIconSize)) { + if (ImGui::Selectable(Control::manager().inputLabel(ik).c_str(), S->inputAssigned(ik), 0, keyLetterIconSize)) { current_input_ = ik; + // TODO SET VAR current input assigned?? } ImGui::PopID(); // if user clics and drags an assigned key icon... - if (input_assigned[ik] && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + if (S->inputAssigned(ik) && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { ImGui::SetDragDropPayload("DND_KEYBOARD", &ik, sizeof(uint)); ImGui::Text( ICON_FA_CUBE " %s ", Control::manager().inputLabel(ik).c_str()); ImGui::EndDragDropSource(); @@ -4785,10 +4754,8 @@ void InputMappingInterface::Render() if ( payload->DataSize == sizeof(uint) ) { // drop means change key of input callbacks uint previous_input_key = *(const int*)payload->Data; - // index of current source changed; - auto result = input_sources_callbacks.equal_range(previous_input_key); - for (auto kit = result.first; kit != result.second; ++kit) - kit->second.first->swapInputCallback(previous_input_key, ik); + // swap + S->swapSourceCallback(previous_input_key, ik); // switch to this key current_input_ = ik; } @@ -4848,12 +4815,12 @@ void InputMappingInterface::Render() } // draw key button ImGui::PushID(ik); - if (ImGui::Selectable(Control::manager().inputLabel(ik).c_str(), input_assigned[ik], 0, iconsize)) { + if (ImGui::Selectable(Control::manager().inputLabel(ik).c_str(), S->inputAssigned(ik), 0, iconsize)) { current_input_ = ik; } ImGui::PopID(); // if user clics and drags an assigned key icon... - if (input_assigned[ik] && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + if (S->inputAssigned(ik) && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { ImGui::SetDragDropPayload("DND_NUMPAD", &ik, sizeof(uint)); ImGui::Text( ICON_FA_CUBE " %s ", Control::manager().inputLabel(ik).c_str()); ImGui::EndDragDropSource(); @@ -4864,10 +4831,8 @@ void InputMappingInterface::Render() if ( payload->DataSize == sizeof(uint) ) { // drop means change key of input callbacks uint previous_input_key = *(const int*)payload->Data; - // index of current source changed; - auto result = input_sources_callbacks.equal_range(previous_input_key); - for (auto kit = result.first; kit != result.second; ++kit) - kit->second.first->swapInputCallback(previous_input_key, ik); + // swap + S->swapSourceCallback(previous_input_key, ik); // switch to this key current_input_ = ik; } @@ -4924,10 +4889,12 @@ void InputMappingInterface::Render() // draw key button ImGui::PushID(it); - if (ImGui::Selectable(" ", input_assigned[it], 0, keyNumpadIconSize)) + if (ImGui::Selectable(" ", S->inputAssigned(it), 0, keyNumpadIconSize)) current_input_ = it; ImGui::PopID(); + // TODO DragN DROP + // 4 elements in a row if ((t % 4) < 3) ImGui::SameLine(); @@ -4997,12 +4964,12 @@ void InputMappingInterface::Render() } // draw key button ImGui::PushID(ig); - if (ImGui::Selectable(gamepad_labels[b].c_str(), input_assigned[ig], 0, keyLetterIconSize)) + if (ImGui::Selectable(gamepad_labels[b].c_str(), S->inputAssigned(ig), 0, keyLetterIconSize)) current_input_ = ig; ImGui::PopID(); // if user clics and drags an assigned key icon... - if (input_assigned[ig] && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + if (S->inputAssigned(ig) && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { ImGui::SetDragDropPayload("DND_GAMEPAD", &ig, sizeof(uint)); ImGui::Text( ICON_FA_CUBE " %s ", Control::manager().inputLabel(ig).c_str()); ImGui::EndDragDropSource(); @@ -5013,10 +4980,8 @@ void InputMappingInterface::Render() if ( payload->DataSize == sizeof(uint) ) { // drop means change key of input callbacks uint previous_input_key = *(const int*)payload->Data; - // index of current source changed; - auto result = input_sources_callbacks.equal_range(previous_input_key); - for (auto kit = result.first; kit != result.second; ++kit) - kit->second.first->swapInputCallback(previous_input_key, ig); + // swap + S->swapSourceCallback(previous_input_key, ig); // switch to this key current_input_ = ig; } @@ -5054,7 +5019,7 @@ void InputMappingInterface::Render() ImGuiToolkit::ValueBar(Control::manager().inputValue(INPUT_JOYSTICK_FIRST_AXIS), axis_bar_size); // Draw button to assign the axis to an action ImGui::SetCursorScreenPos( pos ); - if (ImGui::Selectable("LX", input_assigned[INPUT_JOYSTICK_FIRST_AXIS], 0, axis_icon_size)) + if (ImGui::Selectable("LX", S->inputAssigned(INPUT_JOYSTICK_FIRST_AXIS), 0, axis_icon_size)) current_input_ = INPUT_JOYSTICK_FIRST_AXIS; // Draw frame around current gamepad axis if (current_input_ == INPUT_JOYSTICK_FIRST_AXIS) @@ -5064,7 +5029,7 @@ void InputMappingInterface::Render() ImGui::SetCursorScreenPos( pos + axis_bar_pos); ImGuiToolkit::ValueBar(Control::manager().inputValue(INPUT_JOYSTICK_FIRST_AXIS+1), axis_bar_size); ImGui::SetCursorScreenPos( pos ); - if (ImGui::Selectable("LY", input_assigned[INPUT_JOYSTICK_FIRST_AXIS+1], 0, axis_icon_size)) + if (ImGui::Selectable("LY", S->inputAssigned(INPUT_JOYSTICK_FIRST_AXIS+1), 0, axis_icon_size)) current_input_ = INPUT_JOYSTICK_FIRST_AXIS+1; if (current_input_ == INPUT_JOYSTICK_FIRST_AXIS+1) draw_list->AddRect(pos, pos + axis_icon_size, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f); @@ -5073,7 +5038,7 @@ void InputMappingInterface::Render() ImGui::SetCursorScreenPos( pos + axis_bar_pos); ImGuiToolkit::ValueBar(Control::manager().inputValue(INPUT_JOYSTICK_FIRST_AXIS+2), axis_bar_size); ImGui::SetCursorScreenPos( pos ); - if (ImGui::Selectable("L2", input_assigned[INPUT_JOYSTICK_FIRST_AXIS+2], 0, axis_icon_size)) + if (ImGui::Selectable("L2", S->inputAssigned(INPUT_JOYSTICK_FIRST_AXIS+2), 0, axis_icon_size)) current_input_ = INPUT_JOYSTICK_FIRST_AXIS+2; if (current_input_ == INPUT_JOYSTICK_FIRST_AXIS+2) draw_list->AddRect(pos, pos + axis_icon_size, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f); @@ -5088,7 +5053,7 @@ void InputMappingInterface::Render() ImGui::SetCursorScreenPos( pos + axis_bar_pos); ImGuiToolkit::ValueBar(Control::manager().inputValue(INPUT_JOYSTICK_FIRST_AXIS+3), axis_bar_size); ImGui::SetCursorScreenPos( pos ); - if (ImGui::Selectable("RX", input_assigned[INPUT_JOYSTICK_FIRST_AXIS+3], 0, axis_icon_size)) + if (ImGui::Selectable("RX", S->inputAssigned(INPUT_JOYSTICK_FIRST_AXIS+3), 0, axis_icon_size)) current_input_ = INPUT_JOYSTICK_FIRST_AXIS+3; if (current_input_ == INPUT_JOYSTICK_FIRST_AXIS+3) draw_list->AddRect(pos, pos + axis_icon_size, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f); @@ -5097,7 +5062,7 @@ void InputMappingInterface::Render() ImGui::SetCursorScreenPos( pos + axis_bar_pos); ImGuiToolkit::ValueBar(Control::manager().inputValue(INPUT_JOYSTICK_FIRST_AXIS+4), axis_bar_size); ImGui::SetCursorScreenPos( pos ); - if (ImGui::Selectable("RY", input_assigned[INPUT_JOYSTICK_FIRST_AXIS+4], 0, axis_icon_size)) + if (ImGui::Selectable("RY", S->inputAssigned(INPUT_JOYSTICK_FIRST_AXIS+4), 0, axis_icon_size)) current_input_ = INPUT_JOYSTICK_FIRST_AXIS+4; if (current_input_ == INPUT_JOYSTICK_FIRST_AXIS+4) draw_list->AddRect(pos, pos + axis_icon_size, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f); @@ -5106,7 +5071,7 @@ void InputMappingInterface::Render() ImGui::SetCursorScreenPos( pos + axis_bar_pos); ImGuiToolkit::ValueBar(Control::manager().inputValue(INPUT_JOYSTICK_FIRST_AXIS+5), axis_bar_size); ImGui::SetCursorScreenPos( pos ); - if (ImGui::Selectable("R2", input_assigned[INPUT_JOYSTICK_FIRST_AXIS+5], 0, axis_icon_size)) + if (ImGui::Selectable("R2", S->inputAssigned(INPUT_JOYSTICK_FIRST_AXIS+5), 0, axis_icon_size)) current_input_ = INPUT_JOYSTICK_FIRST_AXIS+5; if (current_input_ == INPUT_JOYSTICK_FIRST_AXIS+5) draw_list->AddRect(pos, pos + axis_icon_size, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f); @@ -5126,20 +5091,20 @@ void InputMappingInterface::Render() float w = ImGui::GetWindowWidth() *0.25f; - if (input_assigned[current_input_]) { + if (S->inputAssigned(current_input_)) { - auto result = input_sources_callbacks.equal_range(current_input_); - for (auto kit = result.first; kit != result.second; ++kit) { + auto result = S->getSourceCallbacks(current_input_); + for (auto kit = result.cbegin(); kit != result.cend(); ++kit) { - Source *source = kit->second.first; - SourceCallback *callback = kit->second.second; + Source *source = kit->first; + SourceCallback *callback = kit->second; // push ID because we repeat the same UI ImGui::PushID( (void*) callback); // Delete interface if (ImGuiToolkit::IconButton(ICON_FA_MINUS, "Remove") ){ - source->removeInputCallback(callback); + S->deleteSourceCallback(callback); // reload ImGui::PopID(); break; @@ -5148,12 +5113,10 @@ void InputMappingInterface::Render() // Select Source ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::SetNextItemWidth(w); - Source *select = ComboSelectSource(source); - if (select != nullptr) { - // copy callback to other source - select->addInputCallback(current_input_, callback->clone()); - // remove previous callback - source->removeInputCallback(callback); + Source *selected_source = ComboSelectSource(source); + if (selected_source != nullptr) { + // reassign the callback to newly selected source + S->assignSourceCallback(current_input_, selected_source, callback); // reload ImGui::PopID(); break; @@ -5165,9 +5128,9 @@ void InputMappingInterface::Render() uint type = ComboSelectCallback( callback->type() ); if (type > 0) { // remove previous callback - source->removeInputCallback(callback); - // add callback - source->addInputCallback(current_input_, SourceCallback::create((SourceCallback::CallbackType)type) ); + S->deleteSourceCallback(callback); + // assign callback + S->assignSourceCallback(current_input_, source, SourceCallback::create((SourceCallback::CallbackType)type) ); // reload ImGui::PopID(); break; @@ -5222,7 +5185,7 @@ void InputMappingInterface::Render() // user selected a callback type if (temp_new_callback > 0) { // step 4 : create new callback and add it to source - temp_new_source->addInputCallback(current_input_, SourceCallback::create((SourceCallback::CallbackType)temp_new_callback) ); + S->assignSourceCallback(current_input_, temp_new_source, SourceCallback::create((SourceCallback::CallbackType)temp_new_callback) ); // done temp_new_source = nullptr; temp_new_callback = 0; diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index 4aad7bd..c8fbefc 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -368,13 +368,8 @@ class InputMappingInterface : public WorkspaceWindow { std::array< std::string, 4 > input_mode; std::array< uint, 4 > current_input_for_mode; - - // data structures more adapted for display - std::multimap< uint, std::pair > input_sources_callbacks; - bool input_assigned[100]{}; uint current_input_; - void readInputSource(); Source *ComboSelectSource(Source *current = nullptr); uint ComboSelectCallback(uint current); void SliderParametersCallback(SourceCallback *callback, Source *source);