mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
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.
272 lines
6.7 KiB
C++
272 lines
6.7 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
#include <algorithm>
|
|
|
|
#include <glm/gtx/vector_angle.hpp>
|
|
#include <glm/gtx/rotate_vector.hpp>
|
|
|
|
#include "Source.h"
|
|
#include "Session.h"
|
|
|
|
#include "SourceList.h"
|
|
|
|
// utility to sort Sources by depth
|
|
bool compare_depth (Source * first, Source * second)
|
|
{
|
|
return ( first->depth() < second->depth() );
|
|
}
|
|
|
|
|
|
bool notplayable (const Source *s) { return !s->playable(); }
|
|
|
|
SourceList playable_only (const SourceList &list)
|
|
{
|
|
SourceList pl = list;
|
|
|
|
pl.remove_if(notplayable);
|
|
|
|
return pl;
|
|
}
|
|
|
|
SourceList depth_sorted(const SourceList &list)
|
|
{
|
|
SourceList sl = list;
|
|
sl.sort(compare_depth);
|
|
|
|
return sl;
|
|
}
|
|
|
|
// utility to sort Sources in MixingView in a clockwise order
|
|
// in reference to a center point
|
|
struct clockwise_centered {
|
|
explicit clockwise_centered(glm::vec2 c) : center(c) { }
|
|
bool operator() (Source * first, Source * second) {
|
|
glm::vec2 pos_first = glm::vec2(first->group(View::MIXING)->translation_)-center;
|
|
float angle_first = glm::orientedAngle( glm::normalize(pos_first), glm::vec2(1.f, 0.f) );
|
|
glm::vec2 pos_second = glm::vec2(second->group(View::MIXING)->translation_)-center;
|
|
float angle_second = glm::orientedAngle( glm::normalize(pos_second), glm::vec2(1.f, 0.f) );
|
|
return (angle_first < angle_second);
|
|
}
|
|
glm::vec2 center;
|
|
};
|
|
|
|
SourceList mixing_sorted(const SourceList &list, glm::vec2 center)
|
|
{
|
|
SourceList sl = list;
|
|
sl.sort(clockwise_centered(center));
|
|
|
|
return sl;
|
|
}
|
|
|
|
|
|
SourceIdList ids (const SourceList &list)
|
|
{
|
|
SourceIdList idlist;
|
|
|
|
for( auto sit = list.begin(); sit != list.end(); ++sit)
|
|
idlist.push_back( (*sit)->id() );
|
|
|
|
// make sure no duplicate
|
|
idlist.unique();
|
|
|
|
return idlist;
|
|
}
|
|
|
|
|
|
SourceListCompare compare (const SourceList &first, const SourceList &second)
|
|
{
|
|
SourceListCompare ret = SOURCELIST_DISTINCT;
|
|
if (first.empty() || second.empty())
|
|
return ret;
|
|
|
|
// a new test list: start with the second list and remove all commons with first list
|
|
SourceList test = second;
|
|
for (auto it = first.begin(); it != first.end(); ++it){
|
|
test.remove(*it);
|
|
}
|
|
|
|
// all sources of the second list were in the first list
|
|
if (test.empty()) {
|
|
// same size, therefore they are the same!
|
|
if (first.size() == second.size())
|
|
ret = SOURCELIST_EQUAL;
|
|
// otherwise, first list contains all sources of the second list.
|
|
else
|
|
ret = SOURCELIST_SECOND_IN_FIRST;
|
|
}
|
|
// some sources of the second list were in the first
|
|
else if ( second.size() != test.size() ){
|
|
// if the number of sources removed from second is the number of sources in the first
|
|
if (second.size() - test.size() == first.size())
|
|
ret = SOURCELIST_FIRST_IN_SECOND;
|
|
// else, there is a patrial intersection
|
|
else
|
|
ret = SOURCELIST_INTERSECT;
|
|
}
|
|
// else no intersection, lists are distinct (return detault)
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
SourceList intersect (const SourceList &first, const SourceList &second)
|
|
{
|
|
// take second list and remove all elements also in first list
|
|
// -> builds the list of what remains in second list
|
|
SourceList l1 = second;
|
|
for (auto it = first.begin(); it != first.end(); ++it)
|
|
l1.remove(*it);
|
|
// take second list and remove all elements in the remainer list
|
|
// -> builds the list of what is in second list and was part of the first list
|
|
SourceList l2 = second;
|
|
for (auto it = l1.begin(); it != l1.end(); ++it)
|
|
l2.remove(*it);
|
|
return l2;
|
|
}
|
|
|
|
|
|
SourceList join (const SourceList &first, const SourceList &second)
|
|
{
|
|
SourceList l = second;
|
|
for (auto it = first.begin(); it != first.end(); ++it)
|
|
l.push_back(*it);
|
|
l.unique();
|
|
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)
|
|
{
|
|
connect(s);
|
|
}
|
|
|
|
SourceLink::SourceLink(const SourceLink &l)
|
|
{
|
|
if (l.target_ != nullptr)
|
|
connect(l.target_);
|
|
else
|
|
host_ = l.host_;
|
|
id_ = l.id_;
|
|
}
|
|
|
|
void SourceLink::connect(uint64_t id, Session *se)
|
|
{
|
|
if (connected())
|
|
disconnect();
|
|
|
|
id_ = id;
|
|
host_ = se;
|
|
}
|
|
|
|
void SourceLink::connect(Source *s)
|
|
{
|
|
if (connected())
|
|
disconnect();
|
|
|
|
target_ = s;
|
|
target_->links_.push_back(this);
|
|
|
|
id_ = s->id();
|
|
// TODO veryfy circular dependency recursively ?
|
|
}
|
|
|
|
void SourceLink::disconnect()
|
|
{
|
|
if (target_)
|
|
target_->links_.remove(this);
|
|
target_ = nullptr;
|
|
|
|
id_ = 0;
|
|
host_ = nullptr;
|
|
}
|
|
|
|
SourceLink::~SourceLink()
|
|
{
|
|
disconnect();
|
|
}
|
|
|
|
Source *SourceLink::source()
|
|
{
|
|
// no link to pointer yet?
|
|
if ( target_ == nullptr ) {
|
|
// to find a source, we need a host and an id
|
|
if ( id_ > 0 && host_ != nullptr) {
|
|
// find target in session
|
|
SourceList::iterator it = host_->find(id_);
|
|
// found: keep pointer
|
|
if (it != host_->end()) {
|
|
target_ = *it;
|
|
target_->links_.push_back(this);
|
|
}
|
|
// // not found: invalidate link
|
|
// else
|
|
// disconnect();
|
|
}
|
|
// no host: invalidate link
|
|
else
|
|
disconnect();
|
|
}
|
|
|
|
return target_;
|
|
}
|
|
|
|
|
|
SourceList validateLinkList (const SourceLinkList &list)
|
|
{
|
|
SourceList sourcelist;
|
|
|
|
for( auto sit = list.begin(); sit != list.end(); ++sit) {
|
|
|
|
Source *s = (*sit)->source();
|
|
if (s)
|
|
sourcelist.push_back( s );
|
|
|
|
}
|
|
|
|
// make sure no duplicate
|
|
sourcelist.unique();
|
|
|
|
return sourcelist;
|
|
}
|
|
|
|
|
|
SourceLinkList getLinkList (const SourceList &list)
|
|
{
|
|
SourceLinkList linklist;
|
|
|
|
for( auto sit = list.begin(); sit != list.end(); ++sit)
|
|
linklist.push_back( new SourceLink(*sit) );
|
|
|
|
return linklist;
|
|
}
|
|
|
|
|
|
void clearLinkList (SourceLinkList list)
|
|
{
|
|
for( auto sit = list.begin(); sit != list.end(); ) {
|
|
delete (*sit);
|
|
sit = list.erase(sit);
|
|
}
|
|
}
|