mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Output session fading fixed for OSC and animation.
Linear interpolation (instead of dichotomy converge) for fading at Session update. Mixing View update reads value of session fading to animate the cursor (which was preventing other manipulation of fading). Cleanup fading in OSC controller, with animation options and fade-in and fade-out controls.
This commit is contained in:
@@ -86,7 +86,8 @@ void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
|
||||
Control::manager().sendOutputStatus(remoteEndpoint);
|
||||
}
|
||||
else
|
||||
Control::manager().receiveOutputAttribute(attribute, m.ArgumentStream());
|
||||
if ( Control::manager().receiveOutputAttribute(attribute, m.ArgumentStream()) )
|
||||
Control::manager().sendOutputStatus(remoteEndpoint);
|
||||
}
|
||||
// ALL sources target: apply attribute to all sources of the session
|
||||
else if ( target.compare(OSC_ALL) == 0 )
|
||||
@@ -268,11 +269,13 @@ void Control::terminate()
|
||||
}
|
||||
|
||||
|
||||
void Control::receiveOutputAttribute(const std::string &attribute,
|
||||
bool Control::receiveOutputAttribute(const std::string &attribute,
|
||||
osc::ReceivedMessageArgumentStream arguments)
|
||||
{
|
||||
bool send_feedback = false;
|
||||
|
||||
try {
|
||||
/// e.g. '/vimix/output/enable' or '/vimix/output/enable T' or '/vimix/output/enable F'
|
||||
/// e.g. '/vimix/output/enable' or '/vimix/output/enable 1.0' or '/vimix/output/enable 0.0'
|
||||
if ( attribute.compare(OSC_OUTPUT_ENABLE) == 0) {
|
||||
float on = 1.f;
|
||||
if ( !arguments.Eos()) {
|
||||
@@ -280,7 +283,7 @@ void Control::receiveOutputAttribute(const std::string &attribute,
|
||||
}
|
||||
Settings::application.render.disabled = on < 0.5f;
|
||||
}
|
||||
/// e.g. '/vimix/output/disable' or '/vimix/output/disable T' or '/vimix/output/disable F'
|
||||
/// e.g. '/vimix/output/disable' or '/vimix/output/disable 1.0' or '/vimix/output/disable 0.0'
|
||||
else if ( attribute.compare(OSC_OUTPUT_DISABLE) == 0) {
|
||||
float on = 1.f;
|
||||
if ( !arguments.Eos()) {
|
||||
@@ -288,11 +291,34 @@ void Control::receiveOutputAttribute(const std::string &attribute,
|
||||
}
|
||||
Settings::application.render.disabled = on > 0.5f;
|
||||
}
|
||||
/// e.g. '/vimix/output/fading f 0.2'
|
||||
/// e.g. '/vimix/output/fading f 0.2' or '/vimix/output/fading ff 1.0 300.f'
|
||||
else if ( attribute.compare(OSC_OUTPUT_FADING) == 0) {
|
||||
float fading = 0.f;
|
||||
arguments >> fading >> osc::EndMessage;
|
||||
Mixer::manager().session()->setFading(fading); // TODO move cursor when in Mixing view
|
||||
float f = 0.f, d = 0.f;
|
||||
// first argument is fading value
|
||||
arguments >> f;
|
||||
if (arguments.Eos())
|
||||
arguments >> osc::EndMessage;
|
||||
// if a second argument is given, it is a duration
|
||||
else
|
||||
arguments >> d >> osc::EndMessage;
|
||||
Mixer::manager().session()->setFadingTarget(f, d);
|
||||
}
|
||||
/// e.g. '/vimix/output/fadein' or '/vimix/output/fadein f 300.f'
|
||||
else if ( attribute.compare(OSC_OUTPUT_FADE_IN) == 0) {
|
||||
float f = 0.f;
|
||||
// if argument is given, it is a duration
|
||||
if (!arguments.Eos())
|
||||
arguments >> f >> osc::EndMessage;
|
||||
Mixer::manager().session()->setFadingTarget( Mixer::manager().session()->fading() + f * 0.001);
|
||||
send_feedback = true;
|
||||
}
|
||||
else if ( attribute.compare(OSC_OUTPUT_FADE_OUT) == 0) {
|
||||
float f = 0.f;
|
||||
// if argument is given, it is a duration
|
||||
if (!arguments.Eos())
|
||||
arguments >> f >> osc::EndMessage;
|
||||
Mixer::manager().session()->setFadingTarget( Mixer::manager().session()->fading() - f * 0.001);
|
||||
send_feedback = true;
|
||||
}
|
||||
#ifdef CONTROL_DEBUG
|
||||
else {
|
||||
@@ -310,6 +336,8 @@ void Control::receiveOutputAttribute(const std::string &attribute,
|
||||
catch (osc::WrongArgumentTypeException &e) {
|
||||
Log::Info(CONTROL_OSC_MSG "Invalid argument for attribute '%s' for target 'output'", attribute.c_str());
|
||||
}
|
||||
|
||||
return send_feedback;
|
||||
}
|
||||
|
||||
bool Control::receiveSourceAttribute(Source *target, const std::string &attribute,
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#define OSC_OUTPUT_ENABLE "/enable"
|
||||
#define OSC_OUTPUT_DISABLE "/disable"
|
||||
#define OSC_OUTPUT_FADING "/fading"
|
||||
#define OSC_OUTPUT_FADE_IN "/fadein"
|
||||
#define OSC_OUTPUT_FADE_OUT "/fadeout"
|
||||
|
||||
#define OSC_ALL "/all"
|
||||
#define OSC_SELECTED "/selected"
|
||||
@@ -68,7 +70,7 @@ protected:
|
||||
std::string FullMessage( const osc::ReceivedMessage& m );
|
||||
};
|
||||
|
||||
void receiveOutputAttribute(const std::string &attribute,
|
||||
bool receiveOutputAttribute(const std::string &attribute,
|
||||
osc::ReceivedMessageArgumentStream arguments);
|
||||
|
||||
bool receiveSourceAttribute(Source *target, const std::string &attribute,
|
||||
|
||||
@@ -620,12 +620,12 @@ void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Sources");
|
||||
|
||||
if (ImGuiToolkit::ButtonIcon(3, 2)) s.session()->setFading(0.f);
|
||||
if (ImGuiToolkit::ButtonIcon(3, 2)) s.session()->setFadingTarget(0.f);
|
||||
float f = s.session()->fading();
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::SliderFloat("Fading", &f, 0.0, 1.0, f < 0.001 ? "None" : "%.2f") )
|
||||
s.session()->setFading(f);
|
||||
s.session()->setFadingTarget(f);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Fading " << std::setprecision(2) << f;
|
||||
|
||||
@@ -1240,7 +1240,7 @@ void Mixer::swap()
|
||||
session_->setResolution( session_->config(View::RENDERING)->scale_ );
|
||||
|
||||
// transfer fading
|
||||
session_->setFading( MAX(back_session_->fading(), session_->fading()), true );
|
||||
session_->setFadingTarget( MAX(back_session_->fadingTarget(), session_->fadingTarget()));
|
||||
|
||||
// no current source
|
||||
current_source_ = session_->end();
|
||||
|
||||
@@ -321,18 +321,8 @@ void MixingView::update(float dt)
|
||||
limbo_->scale_ = glm::vec3(p, p, 1.f);
|
||||
|
||||
//
|
||||
// Set slider to match the actual fading of the session
|
||||
//
|
||||
float f = Mixer::manager().session()->empty() ? 0.f : Mixer::manager().session()->fading();
|
||||
|
||||
// reverse calculate angle from fading & move slider
|
||||
slider_root_->rotation_.z = SIGN(slider_root_->rotation_.z) * asin(f) * 2.f;
|
||||
|
||||
// visual feedback on mixing circle
|
||||
f = 1.f - f;
|
||||
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
|
||||
|
||||
// prevent invalid scaling
|
||||
//
|
||||
float s = CLAMP(scene.root()->scale_.x, MIXING_MIN_SCALE, MIXING_MAX_SCALE);
|
||||
scene.root()->scale_.x = s;
|
||||
scene.root()->scale_.y = s;
|
||||
@@ -342,22 +332,16 @@ void MixingView::update(float dt)
|
||||
if (Mixer::manager().view() == this ){
|
||||
|
||||
//
|
||||
// Set session fading to match the slider angle (during animation)
|
||||
// Set slider to match the actual fading of the session
|
||||
//
|
||||
float f = Mixer::manager().session()->fading();
|
||||
|
||||
// calculate fading from angle
|
||||
float f = sin( ABS(slider_root_->rotation_.z) * 0.5f);
|
||||
|
||||
// apply fading
|
||||
if ( ABS_DIFF( f, Mixer::manager().session()->fading()) > EPSILON )
|
||||
{
|
||||
// apply fading to session
|
||||
Mixer::manager().session()->setFading(f);
|
||||
// reverse calculate angle from fading & move slider
|
||||
slider_root_->rotation_.z = SIGN(-slider_root_->rotation_.z) * asin(f) * -2.f;
|
||||
|
||||
// visual feedback on mixing circle
|
||||
f = 1.f - f;
|
||||
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
|
||||
}
|
||||
|
||||
// update the selection overlay
|
||||
updateSelectionOverlay();
|
||||
@@ -374,18 +358,14 @@ std::pair<Node *, glm::vec2> MixingView::pick(glm::vec2 P)
|
||||
// deal with internal interactive objects
|
||||
if ( pick.first == button_white_ || pick.first == button_black_ ) {
|
||||
|
||||
RotateToCallback *anim = nullptr;
|
||||
if (pick.first == button_white_)
|
||||
anim = new RotateToCallback(0.f, 500.f);
|
||||
else
|
||||
anim = new RotateToCallback(SIGN(slider_root_->rotation_.z) * M_PI, 500.f);
|
||||
|
||||
// animate clic
|
||||
pick.first->update_callbacks_.push_back(new BounceScaleCallback(0.3f));
|
||||
|
||||
// reset & start animation
|
||||
slider_root_->update_callbacks_.clear();
|
||||
slider_root_->update_callbacks_.push_back(anim);
|
||||
// animated fading in session
|
||||
if (pick.first == button_white_)
|
||||
Mixer::manager().session()->setFadingTarget(0.f, 500.f);
|
||||
else
|
||||
Mixer::manager().session()->setFadingTarget(1.f, 500.f);
|
||||
|
||||
}
|
||||
else if ( overlay_selection_icon_ != nullptr && pick.first == overlay_selection_icon_ ) {
|
||||
@@ -470,10 +450,14 @@ View::Cursor MixingView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pai
|
||||
// animate slider (rotation angle on its parent)
|
||||
slider_root_->rotation_.z = angle;
|
||||
|
||||
// calculate fading from angle
|
||||
float f = sin( ABS(angle) * 0.5f);
|
||||
Mixer::manager().session()->setFadingTarget(f);
|
||||
|
||||
// cursor feedback
|
||||
slider_->color = glm::vec4( COLOR_CIRCLE_OVER, 0.9f );
|
||||
std::ostringstream info;
|
||||
info << "Output " << 100 - int(Mixer::manager().session()->fading() * 100.0) << " %";
|
||||
info << "Output " << 100 - int(f * 100.0) << " %";
|
||||
return Cursor(Cursor_Hand, info.str() );
|
||||
}
|
||||
else if (pick.first == limbo_slider_) {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
class RenderView : public View
|
||||
{
|
||||
friend class Session;
|
||||
|
||||
// rendering FBO
|
||||
FrameBuffer *frame_buffer_;
|
||||
Surface *fading_overlay_;
|
||||
@@ -26,12 +28,15 @@ public:
|
||||
void setResolution (glm::vec3 resolution = glm::vec3(0.f), bool useAlpha = false);
|
||||
glm::vec3 resolution() const { return frame_buffer_->resolution(); }
|
||||
|
||||
void setFading(float f = 0.f);
|
||||
float fading() const;
|
||||
|
||||
// current frame
|
||||
inline FrameBuffer *frame () const { return frame_buffer_; }
|
||||
|
||||
protected:
|
||||
|
||||
void setFading(float f = 0.f);
|
||||
float fading() const;
|
||||
|
||||
// get a thumbnail outside of opengl context; wait for a promise to be fullfiled after draw
|
||||
void drawThumbnail();
|
||||
FrameBufferImage *thumbnail ();
|
||||
|
||||
43
Session.cpp
43
Session.cpp
@@ -38,7 +38,7 @@ SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_str
|
||||
}
|
||||
|
||||
Session::Session() : active_(true), activation_threshold_(MIXING_MIN_THRESHOLD),
|
||||
filename_(""), failedSource_(nullptr), fading_target_(0.f), thumbnail_(nullptr)
|
||||
filename_(""), failedSource_(nullptr), thumbnail_(nullptr)
|
||||
{
|
||||
config_[View::RENDERING] = new Group;
|
||||
config_[View::RENDERING]->scale_ = glm::vec3(0.f);
|
||||
@@ -150,10 +150,28 @@ void Session::update(float dt)
|
||||
group_iter = deleteMixingGroup(group_iter);
|
||||
}
|
||||
|
||||
// apply fading (smooth dicotomic reaching)
|
||||
float f = render_.fading();
|
||||
if ( ABS_DIFF(f, fading_target_) > EPSILON) {
|
||||
render_.setFading( f + ( fading_target_ - f ) / 2.f);
|
||||
// update fading requested
|
||||
if (fading_.active) {
|
||||
|
||||
// animate
|
||||
fading_.progress += dt;
|
||||
|
||||
// update animation
|
||||
if ( fading_.duration > 0.f && fading_.progress < fading_.duration ) {
|
||||
// interpolation
|
||||
float f = fading_.progress / fading_.duration;
|
||||
f = ( 1.f - f ) * fading_.start + f * fading_.target;
|
||||
render_.setFading( f );
|
||||
}
|
||||
// arrived at target
|
||||
else {
|
||||
// set precise value
|
||||
render_.setFading( fading_.target );
|
||||
// fading finished
|
||||
fading_.active = false;
|
||||
fading_.start = fading_.target;
|
||||
fading_.duration = fading_.progress = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
// update the scene tree
|
||||
@@ -292,12 +310,17 @@ void Session::setResolution(glm::vec3 resolution, bool useAlpha)
|
||||
config_[View::RENDERING]->scale_ = render_.resolution();
|
||||
}
|
||||
|
||||
void Session::setFading(float f, bool forcenow)
|
||||
void Session::setFadingTarget(float f, float duration)
|
||||
{
|
||||
if (forcenow)
|
||||
render_.setFading( f );
|
||||
|
||||
fading_target_ = CLAMP(f, 0.f, 1.f);
|
||||
// targetted fading value
|
||||
fading_.target = CLAMP(f, 0.f, 1.f);
|
||||
// starting point for interpolation
|
||||
fading_.start = fading();
|
||||
// initiate animation
|
||||
fading_.progress = 0.f;
|
||||
fading_.duration = duration;
|
||||
// activate update
|
||||
fading_.active = true;
|
||||
}
|
||||
|
||||
SourceList::iterator Session::begin()
|
||||
|
||||
24
Session.h
24
Session.h
@@ -98,8 +98,9 @@ public:
|
||||
void setResolution (glm::vec3 resolution, bool useAlpha = false);
|
||||
|
||||
// manipulate fading of output
|
||||
void setFading (float f, bool forcenow = false);
|
||||
inline float fading () const { return fading_target_; }
|
||||
void setFadingTarget (float f, float duration = 0.f);
|
||||
inline float fadingTarget () const { return fading_.target; }
|
||||
inline float fading () const { return render_.fading(); }
|
||||
|
||||
// activation threshold for source (mixing distance)
|
||||
inline void setActivationThreshold(float t) { activation_threshold_ = t; }
|
||||
@@ -162,10 +163,27 @@ protected:
|
||||
std::map<View::Mode, Group*> config_;
|
||||
SessionSnapshots snapshots_;
|
||||
std::vector<SourceIdList> play_groups_;
|
||||
float fading_target_;
|
||||
std::mutex access_;
|
||||
FrameBufferImage *thumbnail_;
|
||||
uint64_t start_time_;
|
||||
|
||||
struct Fading
|
||||
{
|
||||
bool active;
|
||||
float start;
|
||||
float target;
|
||||
float duration;
|
||||
float progress;
|
||||
|
||||
Fading() {
|
||||
active = false;
|
||||
start = 0.f;
|
||||
target = 0.f;
|
||||
duration = 0.f;
|
||||
progress = 0.f;
|
||||
}
|
||||
};
|
||||
Fading fading_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -922,7 +922,7 @@ void SessionLoader::visit (SessionFileSource& s)
|
||||
// set fading
|
||||
float f = 0.f;
|
||||
xmlCurrent_->QueryFloatAttribute("fading", &f);
|
||||
s.session()->setFading(f);
|
||||
s.session()->setFadingTarget(f);
|
||||
// set uri
|
||||
XMLElement* pathNode = xmlCurrent_->FirstChildElement("path");
|
||||
if (pathNode) {
|
||||
|
||||
@@ -134,7 +134,7 @@ void TransitionView::update(float dt)
|
||||
transition_source_->group(View::MIXING)->translation_.y = 0.f;
|
||||
|
||||
// no fading when cross fading
|
||||
Mixer::manager().session()->setFading( 0.f );
|
||||
Mixer::manager().session()->setFadingTarget( 0.f );
|
||||
}
|
||||
// fade to black
|
||||
else
|
||||
@@ -151,7 +151,7 @@ void TransitionView::update(float dt)
|
||||
f = ( 2.f * d + 1.f); // quadratic
|
||||
f *= f;
|
||||
}
|
||||
Mixer::manager().session()->setFading( 1.f - f );
|
||||
Mixer::manager().session()->setFadingTarget( 1.f - f );
|
||||
}
|
||||
|
||||
// request update
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user