diff --git a/ControlManager.cpp b/ControlManager.cpp index ca9d4db..91c1674 100644 --- a/ControlManager.cpp +++ b/ControlManager.cpp @@ -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, diff --git a/ControlManager.h b/ControlManager.h index d95f5da..72454b3 100644 --- a/ControlManager.h +++ b/ControlManager.h @@ -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, diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index a83108e..c9cfd0a 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -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; diff --git a/Mixer.cpp b/Mixer.cpp index 00bf7ad..ffc3ca3 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -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(); diff --git a/MixingView.cpp b/MixingView.cpp index b15f5ec..a3f42aa 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -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 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_) { diff --git a/RenderView.h b/RenderView.h index 7a2a7f1..f7e5bf6 100644 --- a/RenderView.h +++ b/RenderView.h @@ -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 (); diff --git a/Session.cpp b/Session.cpp index 4bbd30d..6591cb0 100644 --- a/Session.cpp +++ b/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() diff --git a/Session.h b/Session.h index 93c18fc..91dd49a 100644 --- a/Session.h +++ b/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 config_; SessionSnapshots snapshots_; std::vector 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_; }; diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 7f62f14..9896b9d 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -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) { diff --git a/TransitionView.cpp b/TransitionView.cpp index 1f6721d..9b39a1d 100644 --- a/TransitionView.cpp +++ b/TransitionView.cpp @@ -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 diff --git a/rsc/osc/vimix.mk1.touchosc b/rsc/osc/vimix.mk1.touchosc index 33cd296..ec0801a 100644 Binary files a/rsc/osc/vimix.mk1.touchosc and b/rsc/osc/vimix.mk1.touchosc differ