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);