mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-16 20:59:59 +01:00
Input Mapping for Batch of Sources
Session contains a set of 'Batch' that are created in the Player (renamed from PlayGroups). Session InputCallback can now target either a Source or a Batch, using std::variant (new type Target). Input Mapping reacts to input to create callbacks to a target, either a single source (as before) or to a Batch (multiple sources).
This commit is contained in:
@@ -142,7 +142,7 @@ void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
|
||||
if ( BaseToolkit::is_a_number(num, &i)){
|
||||
// confirmed : the target is a Player selection (e.g. 'selection#2')
|
||||
// loop over this selection of sources
|
||||
SourceList _selection = Mixer::manager().session()->playGroup(i);
|
||||
SourceList _selection = Mixer::manager().session()->getBatch(i);
|
||||
for (SourceList::iterator it = _selection.begin(); it != _selection.end(); ++it) {
|
||||
// apply attributes
|
||||
if ( Control::manager().receiveSourceAttribute( *it, attribute, m.ArgumentStream()) && Mixer::manager().currentSource() == *it)
|
||||
|
||||
151
src/Session.cpp
151
src/Session.cpp
@@ -26,11 +26,9 @@
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "FrameGrabber.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "ActionManager.h"
|
||||
#include "SessionSource.h"
|
||||
#include "RenderSource.h"
|
||||
#include "MixingGroup.h"
|
||||
#include "ControlManager.h"
|
||||
@@ -78,6 +76,15 @@ Session::Session(uint64_t id) : id_(id), active_(true), activation_threshold_(MI
|
||||
}
|
||||
|
||||
|
||||
void Session::InputSourceCallback::clearReverse()
|
||||
{
|
||||
for (auto rev = reverse_.begin(); rev != reverse_.end(); ++rev) {
|
||||
if (rev->second != nullptr)
|
||||
delete rev->second;
|
||||
}
|
||||
reverse_.clear();
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
// TODO delete all mixing groups?
|
||||
@@ -98,8 +105,7 @@ Session::~Session()
|
||||
iter = input_callbacks_.erase(iter)) {
|
||||
if ( iter->second.model_ != nullptr)
|
||||
delete iter->second.model_;
|
||||
if ( iter->second.reverse_ != nullptr)
|
||||
delete iter->second.reverse_;
|
||||
iter->second.clearReverse();
|
||||
}
|
||||
|
||||
delete config_[View::RENDERING];
|
||||
@@ -141,35 +147,70 @@ void Session::update(float dt)
|
||||
bool input_active = Control::manager().inputActive(k->first);
|
||||
|
||||
// get the callback model for each input
|
||||
if ( k->second.model_ != nullptr) {
|
||||
// if the model is valid and the target variant was filled
|
||||
if ( k->second.model_ != nullptr && k->second.target_.index() > 0) {
|
||||
|
||||
// if the value referenced as pressed changed state
|
||||
// or repeat key if there is no reverse callback
|
||||
if ( input_active != k->second.active_ || k->second.reverse_ == nullptr) {
|
||||
if ( input_active != k->second.active_ || k->second.reverse_.empty()) {
|
||||
|
||||
// ON PRESS
|
||||
if (input_active) {
|
||||
// delete the reverse if was not released
|
||||
if (k->second.reverse_ != nullptr)
|
||||
delete k->second.reverse_;
|
||||
k->second.clearReverse();
|
||||
|
||||
// Add callback to the target(s)
|
||||
// 1. Case of variant as Source pointer
|
||||
if (Source * const* v = std::get_if<Source *>(&k->second.target_)) {
|
||||
// verify variant value
|
||||
if ( *v != nullptr ) {
|
||||
// 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 );
|
||||
// add callback to source
|
||||
(*v)->call( C, true );
|
||||
// get the reverse if the callback, and remember it (can be null)
|
||||
k->second.reverse_ = C->reverse(k->second.source_);
|
||||
k->second.reverse_[(*v)->id()] = C->reverse(*v);
|
||||
}
|
||||
}
|
||||
// 2. Case of variant as index of batch
|
||||
else if ( const size_t* v = std::get_if<size_t>(&k->second.target_)) {
|
||||
// verify variant value
|
||||
if ( *v < batch_.size() )
|
||||
{
|
||||
// loop over all sources in Batch
|
||||
for (auto sid = batch_[*v].begin(); sid != batch_[*v].end(); ++sid){
|
||||
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(*sid));;
|
||||
if ( sit != sources_.end()) {
|
||||
// generate a new callback from the model
|
||||
SourceCallback *C = k->second.model_->clone();
|
||||
// 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 source
|
||||
(*sit)->call( C, true );
|
||||
// get the reverse if the callback, and remember it (can be null)
|
||||
k->second.reverse_[*sid] = C->reverse(*sit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ON RELEASE
|
||||
else {
|
||||
// call the reverse (NB: invalid value tested in call)
|
||||
k->second.source_->call( k->second.reverse_, true );
|
||||
// call all the reverse of sources (NB: invalid value tested in call)
|
||||
for (auto rev = k->second.reverse_.begin(); rev != k->second.reverse_.end(); ++rev) {
|
||||
SourceList::const_iterator sit = std::find_if(sources_.begin(), sources_.end(), Source::hasId(rev->first));;
|
||||
if ( sit != sources_.end()) {
|
||||
(*sit)->call( rev->second, true );
|
||||
}
|
||||
}
|
||||
// do not keep reference to reverse: will be deleted when terminated
|
||||
k->second.reverse_ = nullptr;
|
||||
k->second.reverse_.clear();
|
||||
}
|
||||
|
||||
// remember state of callback
|
||||
@@ -629,47 +670,47 @@ std::list<MixingGroup *>::iterator Session::endMixingGroup()
|
||||
}
|
||||
|
||||
|
||||
size_t Session::numPlayGroups() const
|
||||
size_t Session::numBatch() const
|
||||
{
|
||||
return play_groups_.size();
|
||||
return batch_.size();
|
||||
}
|
||||
|
||||
void Session::addPlayGroup(const SourceIdList &ids)
|
||||
void Session::addBatch(const SourceIdList &ids)
|
||||
{
|
||||
play_groups_.push_back( ids );
|
||||
batch_.push_back( ids );
|
||||
}
|
||||
|
||||
void Session::addToPlayGroup(size_t i, Source *s)
|
||||
void Session::addSourceToBatch(size_t i, Source *s)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
if (i < batch_.size() )
|
||||
{
|
||||
if ( std::find(play_groups_[i].begin(), play_groups_[i].end(), s->id()) == play_groups_[i].end() )
|
||||
play_groups_[i].push_back(s->id());
|
||||
if ( std::find(batch_[i].begin(), batch_[i].end(), s->id()) == batch_[i].end() )
|
||||
batch_[i].push_back(s->id());
|
||||
}
|
||||
}
|
||||
|
||||
void Session::removeFromPlayGroup(size_t i, Source *s)
|
||||
void Session::removeSourceFromBatch(size_t i, Source *s)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
if (i < batch_.size() )
|
||||
{
|
||||
if ( std::find(play_groups_[i].begin(), play_groups_[i].end(), s->id()) != play_groups_[i].end() )
|
||||
play_groups_[i].remove( s->id() );
|
||||
if ( std::find(batch_[i].begin(), batch_[i].end(), s->id()) != batch_[i].end() )
|
||||
batch_[i].remove( s->id() );
|
||||
}
|
||||
}
|
||||
|
||||
void Session::deletePlayGroup(size_t i)
|
||||
void Session::deleteBatch(size_t i)
|
||||
{
|
||||
if (i < play_groups_.size() )
|
||||
play_groups_.erase( play_groups_.begin() + i);
|
||||
if (i < batch_.size() )
|
||||
batch_.erase( batch_.begin() + i);
|
||||
}
|
||||
|
||||
SourceList Session::playGroup(size_t i) const
|
||||
SourceList Session::getBatch(size_t i) const
|
||||
{
|
||||
SourceList list;
|
||||
|
||||
if (i < play_groups_.size() )
|
||||
if (i < batch_.size() )
|
||||
{
|
||||
for (auto sid = play_groups_[i].begin(); sid != play_groups_[i].end(); ++sid){
|
||||
for (auto sid = batch_[i].begin(); sid != batch_[i].end(); ++sid){
|
||||
|
||||
SourceList::const_iterator it = std::find_if(sources_.begin(), sources_.end(), Source::hasId( *sid));;
|
||||
if ( it != sources_.end())
|
||||
@@ -728,17 +769,17 @@ std::string Session::save(const std::string& filename, Session *session, const s
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Session::assignSourceCallback(uint input, Source *source, SourceCallback *callback)
|
||||
void Session::assignInputCallback(uint input, Target target, 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
|
||||
// yes, then just change the target
|
||||
if ( k->second.model_ == callback) {
|
||||
if (k->second.reverse_)
|
||||
delete k->second.reverse_;
|
||||
k->second.source_ = source;
|
||||
k->second.target_ = target;
|
||||
// reverse became invalid
|
||||
k->second.clearReverse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -748,11 +789,11 @@ void Session::assignSourceCallback(uint input, Source *source, SourceCallback *c
|
||||
// create new entry
|
||||
std::multimap<uint, InputSourceCallback>::iterator added = input_callbacks_.emplace(input, InputSourceCallback() );
|
||||
added->second.model_ = callback;
|
||||
added->second.source_ = source;
|
||||
added->second.target_ = target;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::swapSourceCallback(uint from, uint to)
|
||||
void Session::swapInputCallback(uint from, uint to)
|
||||
{
|
||||
std::multimap<uint, InputSourceCallback> swapped_callbacks_;
|
||||
|
||||
@@ -767,52 +808,50 @@ void Session::swapSourceCallback(uint from, uint to)
|
||||
input_callbacks_.swap(swapped_callbacks_);
|
||||
}
|
||||
|
||||
void Session::copySourceCallback(uint from, uint to)
|
||||
void Session::copyInputCallback(uint from, uint to)
|
||||
{
|
||||
if ( input_callbacks_.count(from) > 0 ) {
|
||||
auto from_callbacks = getSourceCallbacks(from);
|
||||
for (auto it = from_callbacks.cbegin(); it != from_callbacks.cend(); ++it){
|
||||
assignSourceCallback(to, it->first, it->second->clone() );
|
||||
assignInputCallback(to, it->first, it->second->clone() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::list< std::pair<Source *, SourceCallback *> > Session::getSourceCallbacks(uint input)
|
||||
std::list<std::pair<Target, SourceCallback *> > Session::getSourceCallbacks(uint input)
|
||||
{
|
||||
std::list< std::pair<Source *, SourceCallback*> > ret;
|
||||
std::list< std::pair< Target, 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_) );
|
||||
ret.push_back( std::pair< Target, SourceCallback*>(it->second.target_, it->second.model_) );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Session::deleteSourceCallback(SourceCallback *callback)
|
||||
void Session::deleteInputCallback(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_;
|
||||
k->second.clearReverse();
|
||||
input_callbacks_.erase(k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::deleteSourceCallbacks(uint input)
|
||||
void Session::deleteInputCallbacks(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_;
|
||||
k->second.clearReverse();
|
||||
k = input_callbacks_.erase(k);
|
||||
}
|
||||
else
|
||||
@@ -820,15 +859,14 @@ void Session::deleteSourceCallbacks(uint input)
|
||||
}
|
||||
}
|
||||
|
||||
void Session::deleteSourceCallbacks(Source *source)
|
||||
void Session::deleteInputCallbacks(Target target)
|
||||
{
|
||||
for (auto k = input_callbacks_.begin(); k != input_callbacks_.end();)
|
||||
{
|
||||
if ( k->second.source_ == source) {
|
||||
if ( k->second.target_ == target) {
|
||||
if (k->second.model_)
|
||||
delete k->second.model_;
|
||||
if (k->second.reverse_)
|
||||
delete k->second.reverse_;
|
||||
k->second.clearReverse();
|
||||
k = input_callbacks_.erase(k);
|
||||
}
|
||||
else
|
||||
@@ -837,14 +875,13 @@ void Session::deleteSourceCallbacks(Source *source)
|
||||
}
|
||||
|
||||
|
||||
void Session::clearSourceCallbacks()
|
||||
void Session::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->second.clearReverse();
|
||||
|
||||
k = input_callbacks_.erase(k);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SESSION_H
|
||||
|
||||
#include <mutex>
|
||||
#include <variant>
|
||||
|
||||
#include "SourceList.h"
|
||||
#include "RenderView.h"
|
||||
@@ -153,25 +154,25 @@ public:
|
||||
void applySnapshot(uint64_t key);
|
||||
|
||||
// playlists
|
||||
void addPlayGroup(const SourceIdList &ids);
|
||||
void deletePlayGroup(size_t i);
|
||||
size_t numPlayGroups() const;
|
||||
SourceList playGroup(size_t i) const;
|
||||
void addToPlayGroup(size_t i, Source *s);
|
||||
void removeFromPlayGroup(size_t i, Source *s);
|
||||
std::vector<SourceIdList> getPlayGroups() { return play_groups_; }
|
||||
void addBatch(const SourceIdList &ids);
|
||||
void deleteBatch(size_t i);
|
||||
size_t numBatch() const;
|
||||
SourceList getBatch(size_t i) const;
|
||||
void addSourceToBatch(size_t i, Source *s);
|
||||
void removeSourceFromBatch(size_t i, Source *s);
|
||||
std::vector<SourceIdList> getAllBatch() { return batch_; }
|
||||
|
||||
// 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 ();
|
||||
void assignInputCallback(uint input, Target target, SourceCallback *callback);
|
||||
std::list< std::pair<Target, SourceCallback*> > getSourceCallbacks(uint input);
|
||||
void deleteInputCallback (SourceCallback *callback);
|
||||
void deleteInputCallbacks(Target target);
|
||||
void deleteInputCallbacks(uint input);
|
||||
void clearInputCallbacks ();
|
||||
std::list<uint> assignedInputs();
|
||||
bool inputAssigned(uint input);
|
||||
void swapSourceCallback(uint from, uint to);
|
||||
void copySourceCallback(uint from, uint to);
|
||||
void swapInputCallback(uint from, uint to);
|
||||
void copyInputCallback(uint from, uint to);
|
||||
|
||||
void setInputSynchrony(uint input, Metronome::Synchronicity sync);
|
||||
std::vector<Metronome::Synchronicity> getInputSynchrony();
|
||||
@@ -190,7 +191,7 @@ protected:
|
||||
std::list<MixingGroup *> mixing_groups_;
|
||||
std::map<View::Mode, Group*> config_;
|
||||
SessionSnapshots snapshots_;
|
||||
std::vector<SourceIdList> play_groups_;
|
||||
std::vector<SourceIdList> batch_;
|
||||
std::mutex access_;
|
||||
FrameBufferImage *thumbnail_;
|
||||
uint64_t start_time_;
|
||||
@@ -216,14 +217,14 @@ protected:
|
||||
struct InputSourceCallback {
|
||||
bool active_;
|
||||
SourceCallback *model_;
|
||||
SourceCallback *reverse_;
|
||||
Source *source_;
|
||||
std::map<uint64_t, SourceCallback *> reverse_;
|
||||
Target target_;
|
||||
InputSourceCallback() {
|
||||
active_ = false;
|
||||
model_ = nullptr;
|
||||
reverse_ = nullptr;
|
||||
source_ = nullptr;
|
||||
target_ = nullptr;
|
||||
}
|
||||
void clearReverse();
|
||||
};
|
||||
std::multimap<uint, InputSourceCallback> input_callbacks_;
|
||||
std::vector<Metronome::Synchronicity> input_sync_;
|
||||
|
||||
@@ -234,10 +234,24 @@ void SessionCreator::loadInputCallbacks(tinyxml2::XMLElement *inputsNode)
|
||||
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) {
|
||||
Target target;
|
||||
uint64_t sid = 0;
|
||||
size_t bid = 0;
|
||||
if ( xmlCurrent_->QueryUnsigned64Attribute("id", &sid) != XML_NO_ATTRIBUTE ) {
|
||||
// find the source with the given id
|
||||
SourceList::iterator sit = session_->find(sid);
|
||||
if (sit != session_->end()) {
|
||||
// assign variant
|
||||
target = *sit;
|
||||
}
|
||||
}
|
||||
else if ( xmlCurrent_->QueryUnsigned64Attribute("batch", &bid) != XML_NO_ATTRIBUTE ) {
|
||||
// assign variant
|
||||
target = bid;
|
||||
}
|
||||
|
||||
// if could identify the target
|
||||
if (target.index() > 0) {
|
||||
// what type is the callback ?
|
||||
uint type = 0;
|
||||
xmlCurrent_->QueryUnsignedAttribute("type", &type);
|
||||
@@ -247,12 +261,8 @@ void SessionCreator::loadInputCallbacks(tinyxml2::XMLElement *inputsNode)
|
||||
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);
|
||||
}
|
||||
// assign to target
|
||||
session_->assignInputCallback(input, target, loadedcallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,7 +331,7 @@ void SessionCreator::loadPlayGroups(tinyxml2::XMLElement *playgroupNode)
|
||||
playgroup_sources.push_back( id__ );
|
||||
|
||||
}
|
||||
session_->addPlayGroup( playgroup_sources );
|
||||
session_->addBatch( playgroup_sources );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ void SessionVisitor::savePlayGroups(tinyxml2::XMLDocument *doc, Session *session
|
||||
if (doc != nullptr && session != nullptr)
|
||||
{
|
||||
XMLElement *playlistNode = doc->NewElement("PlayGroups");
|
||||
std::vector<SourceIdList> pl = session->getPlayGroups();
|
||||
std::vector<SourceIdList> pl = session->getAllBatch();
|
||||
for (auto plit = pl.begin(); plit != pl.end(); ++plit) {
|
||||
XMLElement *list = doc->NewElement("PlayGroup");
|
||||
playlistNode->InsertEndChild(list);
|
||||
@@ -231,7 +231,14 @@ void SessionVisitor::saveInputCallbacks(tinyxml2::XMLDocument *doc, Session *ses
|
||||
// create node for this callback
|
||||
XMLElement *cbNode = doc->NewElement("Callback");
|
||||
cbNode->SetAttribute("input", *i);
|
||||
cbNode->SetAttribute("id", kit->first->id());
|
||||
// 1. Case of variant as Source pointer
|
||||
if (Source * const* v = std::get_if<Source *>(&kit->first)) {
|
||||
cbNode->SetAttribute("id", (*v)->id());
|
||||
}
|
||||
// 2. Case of variant as index of batch
|
||||
else if ( const size_t* v = std::get_if<size_t>(&kit->first)) {
|
||||
cbNode->SetAttribute("batch", *v);
|
||||
}
|
||||
inputsNode->InsertEndChild(cbNode);
|
||||
// launch visitor to complete attributes
|
||||
SessionVisitor sv(doc, cbNode);
|
||||
|
||||
@@ -117,21 +117,26 @@ bool SourceCallback::overlap( SourceCallback *a, SourceCallback *b)
|
||||
{
|
||||
const Grab *_a = static_cast<Grab*>(a);
|
||||
const Grab *_b = static_cast<Grab*>(b);
|
||||
// there is no overlap if the X or Y of a vector is zero
|
||||
if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
|
||||
ret = false;
|
||||
else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
|
||||
if ( ABS_DIFF(_a->value().x, _b->value().x) > EPSILON || ABS_DIFF(_a->value().y, _b->value().y) > EPSILON )
|
||||
ret = false;
|
||||
|
||||
// // there is no overlap if the X or Y of a vector is zero
|
||||
// if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
|
||||
// ret = false;
|
||||
// else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
|
||||
// ret = false;
|
||||
}
|
||||
break;
|
||||
case SourceCallback::CALLBACK_RESIZE:
|
||||
{
|
||||
const Resize *_a = static_cast<Resize*>(a);
|
||||
const Resize *_b = static_cast<Resize*>(b);
|
||||
if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
|
||||
ret = false;
|
||||
else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
|
||||
if ( ABS_DIFF(_a->value().x, _b->value().x) > EPSILON || ABS_DIFF(_a->value().y, _b->value().y) > EPSILON )
|
||||
ret = false;
|
||||
// if ( ABS(_a->value().x) < EPSILON || ABS(_b->value().x) < EPSILON )
|
||||
// ret = false;
|
||||
// else if ( ABS(_a->value().y) < EPSILON || ABS(_b->value().y) < EPSILON )
|
||||
// ret = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Source;
|
||||
@@ -61,4 +62,7 @@ SourceLinkList getLinkList (const SourceList &list);
|
||||
void clearLinkList (SourceLinkList list);
|
||||
SourceList validateLinkList (const SourceLinkList &list);
|
||||
|
||||
// utility type to target either a source or a batch
|
||||
typedef std::variant<std::monostate, Source *, size_t> Target;
|
||||
|
||||
#endif // SOURCELIST_H
|
||||
|
||||
@@ -105,7 +105,7 @@ TextEditor _editor;
|
||||
#define PLOT_CIRCLE_SEGMENTS 64
|
||||
#define PLOT_ARRAY_SIZE 180
|
||||
#define LABEL_AUTO_MEDIA_PLAYER ICON_FA_USER_CIRCLE " User selection"
|
||||
#define LABEL_STORE_SELECTION " Store selection"
|
||||
#define LABEL_STORE_SELECTION " Create Batch"
|
||||
#define LABEL_EDIT_FADING ICON_FA_RANDOM " Fade in & out"
|
||||
#define LABEL_VIDEO_SEQUENCE " Encode an image sequence"
|
||||
|
||||
@@ -2386,7 +2386,7 @@ void SourceController::Render()
|
||||
if (ImGui::BeginMenu(active_label_.c_str()))
|
||||
{
|
||||
// info on selection status
|
||||
size_t N = Mixer::manager().session()->numPlayGroups();
|
||||
size_t N = Mixer::manager().session()->numBatch();
|
||||
bool enabled = !selection_.empty() && active_selection_ < 0;
|
||||
|
||||
// Menu : Dynamic selection
|
||||
@@ -2396,8 +2396,8 @@ void SourceController::Render()
|
||||
if (ImGui::MenuItem(ICON_FA_PLUS_CIRCLE LABEL_STORE_SELECTION, NULL, false, enabled))
|
||||
{
|
||||
active_selection_ = N;
|
||||
active_label_ = std::string(ICON_FA_CHECK_CIRCLE " Selection #") + std::to_string(active_selection_);
|
||||
Mixer::manager().session()->addPlayGroup( ids(selection_) );
|
||||
active_label_ = std::string(ICON_FA_CHECK_CIRCLE " Batch #") + std::to_string(active_selection_);
|
||||
Mixer::manager().session()->addBatch( ids(selection_) );
|
||||
info_.reset();
|
||||
}
|
||||
// Menu : list of selections
|
||||
@@ -2405,7 +2405,7 @@ void SourceController::Render()
|
||||
ImGui::Separator();
|
||||
for (size_t i = 0 ; i < N; ++i)
|
||||
{
|
||||
std::string label = std::string(ICON_FA_CHECK_CIRCLE " Selection #") + std::to_string(i);
|
||||
std::string label = std::string(ICON_FA_CHECK_CIRCLE " Batch #") + std::to_string(i);
|
||||
if (ImGui::MenuItem( label.c_str() ))
|
||||
{
|
||||
active_selection_ = i;
|
||||
@@ -2713,7 +2713,7 @@ void SourceController::RenderSelection(size_t i)
|
||||
ImVec2 rendersize = ImGui::GetContentRegionAvail() - ImVec2(0, buttons_height_ + scrollbar_ + v_space_);
|
||||
ImVec2 bottom = ImVec2(top.x, top.y + rendersize.y + v_space_);
|
||||
|
||||
selection_ = Mixer::manager().session()->playGroup(i);
|
||||
selection_ = Mixer::manager().session()->getBatch(i);
|
||||
int numsources = selection_.size();
|
||||
|
||||
// no source selected
|
||||
@@ -3009,11 +3009,11 @@ void SourceController::RenderSelection(size_t i)
|
||||
std::string label = std::string((*s)->initials()) + " - " + (*s)->name();
|
||||
if (std::find(selection_.begin(),selection_.end(),*s) == selection_.end()) {
|
||||
if (ImGui::MenuItem( label.c_str() , 0, false ))
|
||||
Mixer::manager().session()->addToPlayGroup(i, *s);
|
||||
Mixer::manager().session()->addSourceToBatch(i, *s);
|
||||
}
|
||||
else {
|
||||
if (ImGui::MenuItem( label.c_str(), 0, true ))
|
||||
Mixer::manager().session()->removeFromPlayGroup(i, *s);
|
||||
Mixer::manager().session()->removeSourceFromBatch(i, *s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3025,7 +3025,7 @@ void SourceController::RenderSelection(size_t i)
|
||||
ImGui::SetCursorPosX(rendersize.x - buttons_height_ / 1.3f);
|
||||
if (ImGui::Button(ICON_FA_MINUS_CIRCLE)) {
|
||||
resetActiveSelection();
|
||||
Mixer::manager().session()->deletePlayGroup(i);
|
||||
Mixer::manager().session()->deleteBatch(i);
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGuiToolkit::ToolTip("Delete");
|
||||
@@ -3390,9 +3390,9 @@ void SourceController::RenderSelectedSources()
|
||||
ImGui::SameLine(0, space -width);
|
||||
ImGui::SetNextItemWidth(width);
|
||||
if (ImGui::Button( label.c_str() )) {
|
||||
active_selection_ = Mixer::manager().session()->numPlayGroups();
|
||||
active_label_ = std::string("Selection #") + std::to_string(active_selection_);
|
||||
Mixer::manager().session()->addPlayGroup( ids(selection_) );
|
||||
active_selection_ = Mixer::manager().session()->numBatch();
|
||||
active_label_ = std::string("Batch #") + std::to_string(active_selection_);
|
||||
Mixer::manager().session()->addBatch( ids(selection_) );
|
||||
}
|
||||
if (space < buttons_width_ && ImGui::IsItemHovered())
|
||||
ImGuiToolkit::ToolTip(LABEL_STORE_SELECTION);
|
||||
@@ -4816,14 +4816,25 @@ bool InputMappingInterface::Visible() const
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Source *InputMappingInterface::ComboSelectSource(Source *current)
|
||||
///
|
||||
/// Draw a combo box listing all sources and all batch of the current session
|
||||
/// Returns a Target variant : non-assigned by default (std::monostate), or a Source, or a Batch
|
||||
/// If current element is indicated, it is displayed first
|
||||
///
|
||||
Target InputMappingInterface::ComboSelectTarget(const Target ¤t)
|
||||
{
|
||||
Source *selected = nullptr;
|
||||
Target selected;
|
||||
std::string label = "Select";
|
||||
|
||||
if (current)
|
||||
label = std::string(current->initials()) + " - " + current->name();
|
||||
// depending on variant, fill the label of combo
|
||||
if (current.index() > 0) {
|
||||
if (Source * const* v = std::get_if<Source *>(¤t)) {
|
||||
label = std::string((*v)->initials()) + " - " + (*v)->name();
|
||||
}
|
||||
else if ( const size_t* v = std::get_if<size_t>(¤t)) {
|
||||
label = std::string("Batch #") + std::to_string(*v);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("##ComboSelectSource", label.c_str()) )
|
||||
{
|
||||
@@ -4834,6 +4845,13 @@ Source *InputMappingInterface::ComboSelectSource(Source *current)
|
||||
selected = *sit;
|
||||
}
|
||||
}
|
||||
for (size_t b = 0; b < Mixer::manager().session()->numBatch(); ++b){
|
||||
label = std::string("Batch #") + std::to_string(b);
|
||||
if (ImGui::Selectable( label.c_str() )) {
|
||||
selected = b;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
@@ -4893,7 +4911,7 @@ struct ClosestIndex
|
||||
void operator()(float v) { if (v < val) ++index; }
|
||||
};
|
||||
|
||||
void InputMappingInterface::SliderParametersCallback(SourceCallback *callback, Source *source)
|
||||
void InputMappingInterface::SliderParametersCallback(SourceCallback *callback, const Target &target)
|
||||
{
|
||||
const float right_align = -1.05f * ImGui::GetTextLineHeightWithSpacing();
|
||||
static const char *press_tooltip[3] = {"Key Press\nApply value on key press",
|
||||
@@ -4956,22 +4974,52 @@ void InputMappingInterface::SliderParametersCallback(SourceCallback *callback, S
|
||||
if (ImGuiToolkit::IconMultistate(speed_icon, &speed_index, speed_tooltip ))
|
||||
edited->setDuration(speed_values[speed_index]);
|
||||
|
||||
if (target.index() > 0) {
|
||||
// 1. Case of variant as Source pointer
|
||||
if (Source * const* v = std::get_if<Source *>(&target)) {
|
||||
// Button to capture the source current geometry
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 2);
|
||||
if (ImGui::Button("Capture", ImVec2(right_align, 0))) {
|
||||
edited->setTarget( source->group(View::GEOMETRY) );
|
||||
edited->setTarget( (*v)->group(View::GEOMETRY) );
|
||||
}
|
||||
}
|
||||
// 2. Case of variant as index of batch
|
||||
else if ( const size_t* v = std::get_if<size_t>(&target)) {
|
||||
|
||||
std::vector<SourceIdList> _batch = Mixer::manager().session()->getAllBatch();
|
||||
|
||||
std::string label = "Capture";
|
||||
// Combo box to set which source to capture
|
||||
if (ImGui::BeginCombo("##ComboSelectGeometryCapture", label.c_str()) )
|
||||
{
|
||||
if ( *v < _batch.size() )
|
||||
{
|
||||
for (auto sid = _batch[*v].begin(); sid != _batch[*v].end(); ++sid){
|
||||
SourceList::iterator sit = Mixer::manager().session()->find(*sid);
|
||||
if ( sit != Mixer::manager().session()->end() ) {
|
||||
|
||||
label = std::string((*sit)->initials()) + " - " + (*sit)->name();
|
||||
// C to capture the source current geometry
|
||||
if (ImGui::Selectable( label.c_str() )) {
|
||||
edited->setTarget( (*sit)->group(View::GEOMETRY) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
// show current geometry on over
|
||||
if (ImGui::IsItemHovered()) {
|
||||
InfoVisitor info;
|
||||
Group tmp; edited->getTarget(&tmp);
|
||||
tmp.accept(info);
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 2);
|
||||
ImGuiToolkit::Indication("Target geometry places the source by setting its position, scale and rotation", 1, 16);
|
||||
}
|
||||
else {
|
||||
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE / 2);
|
||||
ImGui::TextDisabled("Invalid");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
@@ -5251,7 +5299,7 @@ void InputMappingInterface::Render()
|
||||
|
||||
// Clear all
|
||||
if ( ImGui::MenuItem( ICON_FA_BACKSPACE " Clear all" ) )
|
||||
S->clearSourceCallbacks();
|
||||
S->clearInputCallbacks();
|
||||
|
||||
// output manager menu
|
||||
ImGui::Separator();
|
||||
@@ -5289,7 +5337,7 @@ void InputMappingInterface::Render()
|
||||
{
|
||||
if ( ImGui::MenuItem( ICON_FA_WINDOW_CLOSE " Reset mapping", NULL, false, S->inputAssigned(current_input_) ) )
|
||||
// remove all source callback of this input
|
||||
S->deleteSourceCallbacks(current_input_);
|
||||
S->deleteInputCallbacks(current_input_);
|
||||
|
||||
if (ImGui::BeginMenu(ICON_FA_CLOCK " Metronome", S->inputAssigned(current_input_)))
|
||||
{
|
||||
@@ -5315,7 +5363,7 @@ void InputMappingInterface::Render()
|
||||
for (auto m = models.cbegin(); m != models.cend(); ++m) {
|
||||
if ( *m != current_input_ ) {
|
||||
if ( ImGui::MenuItem( Control::inputLabel( *m ).c_str() ) ){
|
||||
S->copySourceCallback( *m, current_input_);
|
||||
S->copyInputCallback( *m, current_input_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5378,7 +5426,7 @@ void InputMappingInterface::Render()
|
||||
// drop means change key of input callbacks
|
||||
uint previous_input_key = *(const int*)payload->Data;
|
||||
// swap
|
||||
S->swapSourceCallback(previous_input_key, ik);
|
||||
S->swapInputCallback(previous_input_key, ik);
|
||||
// switch to this key
|
||||
current_input_ = ik;
|
||||
}
|
||||
@@ -5455,7 +5503,7 @@ void InputMappingInterface::Render()
|
||||
// drop means change key of input callbacks
|
||||
uint previous_input_key = *(const int*)payload->Data;
|
||||
// swap
|
||||
S->swapSourceCallback(previous_input_key, ik);
|
||||
S->swapInputCallback(previous_input_key, ik);
|
||||
// switch to this key
|
||||
current_input_ = ik;
|
||||
}
|
||||
@@ -5529,7 +5577,7 @@ void InputMappingInterface::Render()
|
||||
// drop means change key of input callbacks
|
||||
uint previous_input_key = *(const int*)payload->Data;
|
||||
// swap
|
||||
S->swapSourceCallback(previous_input_key, it);
|
||||
S->swapInputCallback(previous_input_key, it);
|
||||
// switch to this key
|
||||
current_input_ = it;
|
||||
}
|
||||
@@ -5623,7 +5671,7 @@ void InputMappingInterface::Render()
|
||||
// drop means change key of input callbacks
|
||||
uint previous_input_key = *(const int*)payload->Data;
|
||||
// swap
|
||||
S->swapSourceCallback(previous_input_key, ig);
|
||||
S->swapInputCallback(previous_input_key, ig);
|
||||
// switch to this key
|
||||
current_input_ = ig;
|
||||
}
|
||||
@@ -5741,7 +5789,7 @@ void InputMappingInterface::Render()
|
||||
auto result = S->getSourceCallbacks(current_input_);
|
||||
for (auto kit = result.cbegin(); kit != result.cend(); ++kit) {
|
||||
|
||||
Source *source = kit->first;
|
||||
Target target = kit->first;
|
||||
SourceCallback *callback = kit->second;
|
||||
|
||||
// push ID because we repeat the same UI
|
||||
@@ -5749,33 +5797,42 @@ void InputMappingInterface::Render()
|
||||
|
||||
// Delete interface
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_MINUS, "Remove") ){
|
||||
S->deleteSourceCallback(callback);
|
||||
S->deleteInputCallback(callback);
|
||||
// reload
|
||||
ImGui::PopID();
|
||||
break;
|
||||
}
|
||||
|
||||
// Select Source
|
||||
// Select Target
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
ImGui::SetNextItemWidth(w);
|
||||
Source *selected_source = ComboSelectSource(source);
|
||||
if (selected_source != nullptr) {
|
||||
Target selected_target = ComboSelectTarget(target);
|
||||
// if the selected target variant was filled with a value
|
||||
if (selected_target.index() > 0) {
|
||||
// reassign the callback to newly selected source
|
||||
S->assignSourceCallback(current_input_, selected_source, callback);
|
||||
S->assignInputCallback(current_input_, selected_target, callback);
|
||||
// reload
|
||||
ImGui::PopID();
|
||||
break;
|
||||
}
|
||||
|
||||
// check if target is a Source with image processing enabled
|
||||
bool withimageprocessing = false;
|
||||
if ( selected_target.index() == 1 ) {
|
||||
if (Source * const* v = std::get_if<Source *>(&selected_target)) {
|
||||
withimageprocessing = (*v)->imageProcessingEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
// Select Reaction
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
ImGui::SetNextItemWidth(w);
|
||||
uint type = ComboSelectCallback( callback->type(), source->imageProcessingEnabled() );
|
||||
uint type = ComboSelectCallback( callback->type(), withimageprocessing );
|
||||
if (type > 0) {
|
||||
// remove previous callback
|
||||
S->deleteSourceCallback(callback);
|
||||
S->deleteInputCallback(callback);
|
||||
// assign callback
|
||||
S->assignSourceCallback(current_input_, source, SourceCallback::create((SourceCallback::CallbackType)type) );
|
||||
S->assignInputCallback(current_input_, target, SourceCallback::create((SourceCallback::CallbackType)type) );
|
||||
// reload
|
||||
ImGui::PopID();
|
||||
break;
|
||||
@@ -5784,7 +5841,7 @@ void InputMappingInterface::Render()
|
||||
// Adjust parameters
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
if (callback)
|
||||
SliderParametersCallback( callback, source );
|
||||
SliderParametersCallback( callback, target );
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
@@ -5799,13 +5856,13 @@ void InputMappingInterface::Render()
|
||||
/// Add a new interface
|
||||
///
|
||||
static bool temp_new_input = false;
|
||||
static Source *temp_new_source = nullptr;
|
||||
static Target temp_new_target;
|
||||
static uint temp_new_callback = 0;
|
||||
|
||||
// step 1 : press '+'
|
||||
if (temp_new_input) {
|
||||
if (ImGuiToolkit::IconButton(ICON_FA_TIMES, "Cancel") ){
|
||||
temp_new_source = nullptr;
|
||||
temp_new_target = std::monostate();
|
||||
temp_new_callback = 0;
|
||||
temp_new_input = false;
|
||||
}
|
||||
@@ -5817,24 +5874,32 @@ void InputMappingInterface::Render()
|
||||
// step 2 : Get input for source
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
ImGui::SetNextItemWidth(w);
|
||||
Source *s = ComboSelectSource(temp_new_source);
|
||||
// user selected a source (or changed selection)
|
||||
if (s != nullptr) {
|
||||
temp_new_source = s;
|
||||
|
||||
Target selected_target = ComboSelectTarget(temp_new_target);
|
||||
// if the selected target variant was filled with a value
|
||||
if (selected_target.index() > 0) {
|
||||
temp_new_target = selected_target;
|
||||
temp_new_callback = 0;
|
||||
}
|
||||
// possible new source
|
||||
if (temp_new_source != nullptr) {
|
||||
// possible new target
|
||||
if (temp_new_target.index() > 0) {
|
||||
// check if target is a Source with image processing enabled
|
||||
bool withimageprocessing = false;
|
||||
if ( temp_new_target.index() == 1 ) {
|
||||
if (Source * const* v = std::get_if<Source *>(&temp_new_target)) {
|
||||
withimageprocessing = (*v)->imageProcessingEnabled();
|
||||
}
|
||||
}
|
||||
// step 3: Get input for callback type
|
||||
ImGui::SameLine(0, IMGUI_SAME_LINE);
|
||||
ImGui::SetNextItemWidth(w);
|
||||
temp_new_callback = ComboSelectCallback( temp_new_callback, temp_new_source->imageProcessingEnabled() );
|
||||
temp_new_callback = ComboSelectCallback( temp_new_callback, withimageprocessing );
|
||||
// user selected a callback type
|
||||
if (temp_new_callback > 0) {
|
||||
// step 4 : create new callback and add it to source
|
||||
S->assignSourceCallback(current_input_, temp_new_source, SourceCallback::create((SourceCallback::CallbackType)temp_new_callback) );
|
||||
S->assignInputCallback(current_input_, temp_new_target, SourceCallback::create((SourceCallback::CallbackType)temp_new_callback) );
|
||||
// done
|
||||
temp_new_source = nullptr;
|
||||
temp_new_target = std::monostate();
|
||||
temp_new_callback = 0;
|
||||
temp_new_input = false;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#ifndef __UI_MANAGER_H_
|
||||
#define __UI_MANAGER_H_
|
||||
|
||||
#include "Session.h"
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <future>
|
||||
#include <variant>
|
||||
|
||||
#include <gst/gstutils.h>
|
||||
|
||||
@@ -392,9 +394,9 @@ class InputMappingInterface : public WorkspaceWindow
|
||||
std::array< uint, 4 > current_input_for_mode;
|
||||
uint current_input_;
|
||||
|
||||
Source *ComboSelectSource(Source *current = nullptr);
|
||||
Target ComboSelectTarget(const Target ¤t);
|
||||
uint ComboSelectCallback(uint current, bool imageprocessing);
|
||||
void SliderParametersCallback(SourceCallback *callback, Source *source);
|
||||
void SliderParametersCallback(SourceCallback *callback, const Target &target);
|
||||
|
||||
public:
|
||||
InputMappingInterface();
|
||||
|
||||
Reference in New Issue
Block a user