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.
This commit is contained in:
Bruno Herbelin
2022-03-07 00:23:24 +01:00
parent 83e77681d9
commit 39b61fe331
18 changed files with 689 additions and 478 deletions

View File

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

View File

@@ -102,6 +102,7 @@ public:
bool inputActive (uint id);
float inputValue (uint id);
float inputDelay (uint id);
static std::string inputLabel(uint id);
protected:

View File

@@ -247,6 +247,23 @@ void Metronome::executeAtPhase( std::function<void()> 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<float, std::milli> fp_ms = Metronome::manager().timeToPhase();
ret = fp_ms.count();
}
else if ( sync > Metronome::SYNC_NONE ) {
// SYNC TO BEAT
std::chrono::duration<float, std::milli> fp_ms = Metronome::manager().timeToBeat();
ret = fp_ms.count();
}
// SYNC NONE
return ret;
}
size_t Metronome::peers() const
{
return link_.numPeers();

View File

@@ -54,6 +54,10 @@ public:
std::chrono::microseconds timeToPhase();
void executeAtPhase( std::function<void()> f );
// get time to sync, in milisecond
float timeToSync(Synchronicity sync);
// get number of connected peers
size_t peers () const;
};

View File

@@ -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<uint, InputSourceCallback>::iterator added = input_callbacks_.emplace(input, InputSourceCallback() );
added->second.model_ = callback;
added->second.source_ = source;
}
}
void Session::swapSourceCallback(uint from, uint to)
{
std::multimap<uint, InputSourceCallback> 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<Source *, SourceCallback *> > Session::getSourceCallbacks(uint input)
{
std::list< std::pair<Source *, SourceCallback*> > 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<Source *, SourceCallback*>(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<uint> Session::assignedInputs()
{
std::list<uint> 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<Metronome::Synchronicity> Session::getInputSynchrony()
{
return input_sync_;
}
Metronome::Synchronicity Session::inputSynchrony(uint input)
{
return input_sync_[input];
}

View File

@@ -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<SourceIdList> getPlayGroups() { return play_groups_; }
// callbacks associated to inputs
void assignSourceCallback(uint input, Source *source, SourceCallback *callback);
std::list< std::pair<Source *, SourceCallback*> > getSourceCallbacks(uint input);
void deleteSourceCallback (SourceCallback *callback);
void deleteSourceCallbacks(Source *source);
void deleteSourceCallbacks(uint input);
void clearSourceCallbacks ();
std::list<uint> 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<Metronome::Synchronicity> getInputSynchrony();
Metronome::Synchronicity inputSynchrony(uint input);
protected:
bool active_;
float activation_threshold_;
@@ -186,7 +203,20 @@ protected:
};
Fading fading_;
// std::map<uint64_t,
struct InputSourceCallback {
bool active_;
SourceCallback *model_;
SourceCallback *reverse_;
Source *source_;
InputSourceCallback() {
active_ = false;
model_ = nullptr;
reverse_ = nullptr;
source_ = nullptr;
}
};
std::multimap<uint, InputSourceCallback> input_callbacks_;
std::vector<Metronome::Synchronicity> input_sync_;
};

View File

@@ -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;
}

View File

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

View File

@@ -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<uint> 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<Metronome::Synchronicity> 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<uint> 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<SourceCallback *> 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)
}

View File

@@ -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,

View File

@@ -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<uint, InputCallback>::iterator added = input_callbacks_.emplace(input, InputCallback() );
added->second.model_ = callback;
}
void Source::swapInputCallback(uint from, uint to)
{
std::multimap<uint, InputCallback> 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<SourceCallback *> Source::inputCallbacks(uint input)
{
std::list<SourceCallback *> 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<uint> Source::callbackInputs()
{
std::list<uint> 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

View File

@@ -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<SourceCallback *> inputCallbacks(uint input);
std::list<uint> 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<SourceCallback *> update_callbacks_;
std::mutex access_callbacks_;
struct InputCallback {
bool active_;
SourceCallback *model_;
SourceCallback *reverse_;
InputCallback() {
active_ = false;
model_ = nullptr;
reverse_ = nullptr;
}
};
std::multimap<uint, InputCallback> input_callbacks_;
void updateCallbacks(float dt);
// clones

View File

@@ -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)
{
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();
finished_ = true;
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,9 +173,13 @@ 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_){
SourceCallback::update(s, dt);
if (s->locked())
status_ = FINISHED;
// set start position on first time it is ready
if ( status_ == READY ){
// initial mixing view position
start_ = glm::vec2(s->group(View::MIXING)->translation_);
@@ -180,28 +210,27 @@ void SetAlpha::update(Source *s, float dt)
} while (glm::abs(delta) > DELTA_ALPHA);
}
initialized_ = true;
status_ = ACTIVE;
}
// time passed
progress_ += dt;
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;
SourceCallback::update(s, 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::MIXING)->translation_);
// step in direction of source translation if possible
if ( glm::length(pos_) > DELTA_ALPHA)
step_ = glm::normalize(pos_);
initialized_ = true;
status_ = ACTIVE;
}
// time passed
progress_ += dt;
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,16 +306,15 @@ 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_){
SourceCallback::update(s, 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;
progress_ = 0.f;
initialized_ = true;
status_ = ACTIVE;
}
// time passed
progress_ += dt;
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_) {
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_){
SourceCallback::update(s, dt);
if (s->locked())
status_ = FINISHED;
// set start position on first time it is ready
if ( status_ == READY ){
start_.copyTransform(s->group(View::GEOMETRY));
progress_ = 0.f;
initialized_ = true;
status_ = ACTIVE;
}
// time passed
progress_ += dt;
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,38 +540,36 @@ 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;
SourceCallback::update(s, dt);
if (s->locked())
status_ = FINISHED;
// set start on first time it is ready
if ( status_ == READY ) {
// initial position
start_ = glm::vec2(s->group(View::GEOMETRY)->translation_);
initialized_ = true;
pos_ = glm::vec2(s->group(View::GEOMETRY)->translation_);
status_ = ACTIVE;
}
// time passed
progress_ += dt;
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,38 +593,30 @@ 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,37 +640,36 @@ 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;
SourceCallback::update(s, dt);
if (s->locked())
status_ = FINISHED;
// set start on first time it is ready
if ( status_ == READY ){
// initial position
start_ = s->group(View::GEOMETRY)->rotation_.z;
initialized_ = true;
angle_ = s->group(View::GEOMETRY)->rotation_.z;
status_ = ACTIVE;
}
// calculate amplitude of movement
progress_ += dt;
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);

View File

@@ -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;
};

View File

@@ -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)
{

View File

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

View File

@@ -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<uint> inputs = (*sit)->callbackInputs();
for (auto k = inputs.begin(); k != inputs.end(); ++k) {
// add entry in input map
std::list<SourceCallback *> callbacks = (*sit)->inputCallbacks(*k);
for (auto c = callbacks.begin(); c != callbacks.end(); ++c )
input_sources_callbacks.emplace( *k, std::pair<Source *, SourceCallback*>(*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;

View File

@@ -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<Source *, SourceCallback*> > 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);