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:
Bruno Herbelin
2021-12-26 00:41:02 +01:00
parent 3d05444f30
commit 1cb448c42e
11 changed files with 124 additions and 64 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
// reverse calculate angle from fading & move slider
slider_root_->rotation_.z = SIGN(-slider_root_->rotation_.z) * asin(f) * -2.f;
// apply fading
if ( ABS_DIFF( f, Mixer::manager().session()->fading()) > EPSILON )
{
// apply fading to session
Mixer::manager().session()->setFading(f);
// visual feedback on mixing circle
f = 1.f - f;
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.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_) {

View File

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

View File

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

View File

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

View File

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

View File

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