From 18e1785e579387dee3f8fdd5cbb2449bf5f64477 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sat, 9 Nov 2024 14:52:24 +0100 Subject: [PATCH] BugFix Acceptable behavior for media Player timeline panel First acceptable implementation of timeline editing panel, with cut and fading modes and UI. --- rsc/images/icons.dds | Bin 1638528 -> 1638528 bytes src/SourceControlWindow.cpp | 184 +++++++++++------------------------- src/Timeline.cpp | 79 ++++++++++------ src/Timeline.h | 5 +- 4 files changed, 107 insertions(+), 161 deletions(-) diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index 0f5b4832f2ca22f7fc05f618e5748c3af128c8cb..4791cb3fa3c2581993a7573039c58bcdc799b98e 100644 GIT binary patch delta 899 zcmZo@Olbh37RDB)7UmX~7S(6%b5p5EC>shVdA{zqD7x)K(Fh0bZ$rC~aB)AzM5GKX20RkbGwE`_; zhgvqhLy$?6Jd-@OPngMXNDY%bw@+NluPQ9WiwIfDofo-%;X!_8VdY2vKY@Y)9HenB zU?!aJ(gJ2qZx>|JGC&W`P*k;{uBiO)(*yNc6zriP3z7pzT0N?&5HwSm8MqiYff%NK zVuQi<4OjU;2r+J)zObInK)nVvk|8_>xO#?N5RvI83VAgHpd6TC98lFTK0^aU0LEXv t3YEVKO+Wj@Mu+x|FZqF30Eh*FSO|!Pfmj5HMS)lhh{d;Wd?|5W3;?D`&}aYv delta 803 zcmZo@Olbh37RDB)7UmX~7SU8qx|$`F&v7VzA&REHmGiIjpf%C zW|W(Jutt8m04K9B%m4dI_osh<%OorE`~NSnF(6xkCV=7eA2)!;d>3QkXL8;=y<;Vt z<@Uxxeh1;{j9;1L8ShUIT*D|oIU!8I0;U0G7lQ)?!ubF9|344J`(b>B1rP`|vlWY( z5k!~?G7-Z}m>vJ)!LT33X8?nckjWe#8rvt#timing != GST_CLOCK_TIME_NONE) { - // replace drop time by payload timing - // time = pl->timing; - // // replace mouse coordinate by position from time - // size_t index = ((pl->timing - begin) * static_cast(MAX_TIMELINE_ARRAY)) / end ; - // double x = static_cast(index) / static_cast(MAX_TIMELINE_ARRAY); - // mouse_pos_in_canvas.x = ( x * (size.x - 2.f * _h_space)) + _h_space; - - } - // dragged onto the timeline : apply changes on local copy switch (pl->action) { case TimelinePayload::CUT: @@ -751,7 +740,7 @@ bool EditTimeline(const char *label, } // dropped into the timeline : confirm change to timeline - if (ImGui::AcceptDragDropPayload("DND_TIMELINE")) { + if (ImGui::AcceptDragDropPayload("DND_TIMELINE", ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { // copy temporary timeline into given timeline *tl = _tl; // like a mouse release @@ -2271,75 +2260,45 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) } /// - /// SEPARATOR + /// SECTION WITH BUTTONS /// ImGui::SameLine(0, 0); ImGui::Text("|"); - /// - /// Enter time or percentage of time - /// + /// Enter time value for CUT target_time = MIN(target_time, tl->duration()); - ImGui::SameLine(0, 0); ImVec2 draw_pos = ImGui::GetCursorPos(); - ImGui::SetCursorPosY(ImGui::GetTextLineHeightWithSpacing() * 0.333 ); - float w = gap_dialog_size.x - 4.f * ImGui::GetTextLineHeightWithSpacing() ; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 11)); ImGui::SetNextItemWidth(w - draw_pos.x - IMGUI_SAME_LINE); // VARIABLE WIDTH ImGuiToolkit::InputTime("##Time", &target_time); + ImGui::PopStyleVar(); - // ImGui::SetCursorPos(ImVec2(draw_pos.x, draw_pos.y + 35)); - // ImGuiToolkit::HSliderUInt64("#SliderTime", ImVec2(180, 14), &target_time, 0, tl->duration()); - - // static int p = 5; - // ImGuiToolkit::HSliderInt("#toto", ImVec2(140, 14), &p, 1, 9); - // if (ImGui::IsItemActive()) { - // ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT); - // ImGui::SetTooltip("%d%%", p * 10); - // ImGui::PopFont(); - // } - // if (ImGui::IsItemDeactivatedAfterEdit()){ - - // } - - // ImGui::SameLine(0, IMGUI_SAME_LINE); - // ImVec2 draw_pos = ImGui::GetCursorPos() + ImVec2(0, v_space_); - // ImGui::SetCursorPos(ImVec2(gap_dialog_size.x - ImGui::GetTextLineHeightWithSpacing()- IMGUI_SAME_LINE, - // draw_pos.y)); - // static bool percentage = false; - // ImGuiToolkit::IconToggle(19, 10, 3, 7, &percentage); - // ImGui::SetCursorPos(draw_pos); - // if (percentage) { - // int target_percent = (int) ( (100 * target_time) / tl->duration() ); - // ImGui::SetNextItemWidth(-ImGui::GetTextLineHeightWithSpacing()); - // ImGui::DragInt("##percent", &target_percent, 1, 0, 100); - // target_time = (tl->duration() * (guint64) target_percent) / 100; - // } - // else { - // ImGui::SetNextItemWidth(-ImGui::GetTextLineHeightWithSpacing()); - // ImGuiToolkit::InputTime("##Time", &target_time); - // } - - // Action buttons + /// CUT LEFT TIME ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); ImGui::SetCursorPos( ImVec2(w, draw_pos.y)); - - // ImGui::SameLine(0, IMGUI_SAME_LINE); if (ImGuiToolkit::ButtonIcon(17, 3, "Cut left at given time")) { tl->cut(target_time, true); tl->refresh(); + oss << ": Timeline cut"; + Action::manager().store(oss.str()); } - + /// CUT RIGHT TIME ImGui::SameLine(0, IMGUI_SAME_LINE); if (ImGuiToolkit::ButtonIcon(18, 3, "Cut right at given time")){ tl->cut(target_time, false); tl->refresh(); + oss << ": Timeline cut"; + Action::manager().store(oss.str()); } - + /// CLEAR ImGui::SameLine(0, IMGUI_SAME_LINE); - if (ImGuiToolkit::ButtonIcon(11, 14, "Clear all gaps")) + if (ImGuiToolkit::ButtonIcon(11, 14, "Clear all gaps")) { tl->clearGaps(); + oss << ": Timeline clear gaps"; + Action::manager().store(oss.str()); + } ImGui::PopStyleColor(); @@ -2365,9 +2324,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) {14, 12}}; ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - // ImGuiToolkit::ButtonIconMultistate(options_curve, ¤t_curve, tooltips_curve); ImGuiToolkit::IconMultistate(options_curve, ¤t_curve, tooltips_curve); - ImGui::PopStyleColor(); /// @@ -2392,12 +2349,6 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) ImGui::SameLine(0, IMGUI_SAME_LINE); DragButtonIcon(9, 10, "Drop in timeline to insert\nSharp fade in & out", TimelinePayload(TimelinePayload::FADE_IN_OUT, d*GST_MSECOND, Timeline::FADING_SHARP)); - /// - /// FADE OUT & IN - /// - ImGui::SameLine(0, IMGUI_SAME_LINE); - DragButtonIcon(10, 10, "Drop in timeline to insert\nSharp fade out & in", - TimelinePayload(TimelinePayload::FADE_OUT_IN, d*GST_MSECOND, Timeline::FADING_SHARP)); } /// /// FADE LINEAR @@ -2421,12 +2372,6 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) ImGui::SameLine(0, IMGUI_SAME_LINE); DragButtonIcon(7, 10, "Drop in timeline to insert\nLinear fade in & out", TimelinePayload(TimelinePayload::FADE_IN_OUT, d*GST_MSECOND, Timeline::FADING_LINEAR)); - /// - /// FADE OUT & IN - /// - ImGui::SameLine(0, IMGUI_SAME_LINE); - DragButtonIcon(8, 10, "Drop in timeline to insert\nLinear fade out & in", - TimelinePayload(TimelinePayload::FADE_OUT_IN, d*GST_MSECOND, Timeline::FADING_LINEAR)); } /// /// FADE SMOOTH @@ -2450,89 +2395,70 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) ImGui::SameLine(0, IMGUI_SAME_LINE); DragButtonIcon(17, 10, "Drop in timeline to insert\nSmooth fade in & out", TimelinePayload(TimelinePayload::FADE_IN_OUT, d*GST_MSECOND, Timeline::FADING_SMOOTH)); - /// - /// FADE OUT & IN - /// - ImGui::SameLine(0, IMGUI_SAME_LINE); - DragButtonIcon(18, 10, "Drop in timeline to insert\nSmooth fade out & in", - TimelinePayload(TimelinePayload::FADE_OUT_IN, d*GST_MSECOND, Timeline::FADING_SMOOTH)); - } - // float md = 10000; - ImGui::SameLine(0, IMGUI_SAME_LINE); - - ImVec2 draw_pos = ImGui::GetCursorPos(); - float w = gap_dialog_size.x - 3.f * ImGui::GetTextLineHeightWithSpacing() - IMGUI_SAME_LINE; - ImGui::SetNextItemWidth(w - draw_pos.x); + /// + /// DURATION SLIDER + /// float seconds = (float) d / 1000.f; + float maxi = (float) GST_TIME_AS_MSECONDS(tl->duration()) / 1000.f; + float w = gap_dialog_size.x - 4.f * ImGui::GetTextLineHeightWithSpacing(); - if (current_curve > 0) { - // ImGuiToolkit::SliderTiming("##Duration", &d, 60, md+50, 50, "Auto"); - // if (d > md) d = UINT_MAX; - if (ImGui::SliderFloat("##DurationFading", - &seconds, - 0.05f, - 60.f, - seconds < 59.5f ? "%.2f s" : "Auto")) { - if (seconds > 59.5f) - d = UINT_MAX; - else - d = (uint) floor(seconds * 1000); - } - } else { - // ImGui::TextDisabled("%.2f s", seconds); - static char dummy_str[512]; - if (seconds < 59.5f ) - snprintf(dummy_str, 512, "%.2f s", seconds); + ImGui::SameLine(0, IMGUI_SAME_LINE); + ImVec2 draw_pos = ImGui::GetCursorPos(); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 11)); + ImGui::SetNextItemWidth(w - draw_pos.x); + if (ImGui::SliderFloat("##DurationFading", + &seconds, + 0.05f, + maxi, + d < UINT_MAX ? "%.2f s" : "Auto")) { + if (seconds > maxi - 0.05f) + d = UINT_MAX; else - snprintf(dummy_str, 512, "Auto"); - - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f)); - ImGui::InputText("##disabledduration", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly); - ImGui::PopStyleColor(1); - + d = (uint) floor(seconds * 1000); } + ImGui::PopStyleVar(); + ImGui::PopFont(); - - // Action buttons + /// + /// SECTION WITH BUTTONS + /// ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - - /// - /// SEPARATOR - /// - ImGui::SameLine(0, 0); + ImGui::SetCursorPos( ImVec2(w, draw_pos.y)); ImGui::Text("|"); - // action smooth + /// SMOOTH Curve static int _actionsmooth = 0; ImGui::SameLine(0, 0); ImGui::PushButtonRepeat(true); if (ImGuiToolkit::ButtonIcon(2, 7, "Apply smoothing filter")){ - tl->smoothFading( 5 ); + tl->smoothFading( 15 ); ++_actionsmooth; } ImGui::PopButtonRepeat(); if (_actionsmooth > 0 && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { - oss << ": Timeline opacity smooth"; + oss << ": Timeline smooth curve"; Action::manager().store(oss.str()); _actionsmooth = 0; } + /// CLEANUP + ImGui::SameLine(0, 0); + if (ImGuiToolkit::ButtonIcon(3, 7, "Clean curve in gaps")) { + tl->autoFadeInGaps(); + oss << ": Timeline cleanup curve"; + Action::manager().store(oss.str()); + } - // ImGui::SameLine(0, IMGUI_SAME_LINE); - // ImGui::SetNextItemWidth(140); // TODO VARIABLE WIDTH - // ImVec2 draw_pos = ImGui::GetCursorPos(); - // ImGui::SetCursorPosY(draw_pos.y + 8); // TODO VARIABLE H - // ImGuiToolkit::InputTime("##Time", &target_time); - - - /// /// CLEAR - /// - ImGui::SameLine(0, IMGUI_SAME_LINE); - if (ImGuiToolkit::ButtonIcon(11, 14, "Clear Fading curve")) + ImGui::SameLine(0, 0); + if (ImGuiToolkit::ButtonIcon(11, 14, "Clear fading curve")){ tl->clearFading(); + oss << ": Timeline clear fading"; + Action::manager().store(oss.str()); + } ImGui::PopStyleColor(); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 7d5b7a2..f2c91d6 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -40,6 +40,10 @@ float linestep(size_t edge0, size_t edge1, size_t i) { return x; } +float sharpstep(size_t edge0, size_t edge1, size_t i) { + float x = clamp( static_cast(i - edge0) / static_cast(edge1 - edge0)); + return fabs(x)<1.f ? 0.f : 1.f; +} @@ -545,7 +549,6 @@ void Timeline::clearFading() fading_array_allones_ = true; } - bool Timeline::fadingIsClear() { if (fading_array_changed_) { @@ -696,18 +699,16 @@ void Timeline::fadeOut(const GstClockTime from, const GstClockTime duration, Fad N = 2; } - // linear fade out starts at s + // fade out starts at s size_t i = s; // float val = fadingArray_[s]; for (; i <= s+n; ++i) { - const float x = static_cast(e-i) / static_cast(n); - // const float x = 1.f - static_cast(i-s) / static_cast(n); if (curve==FADING_LINEAR) - fadingArray_[i] = x; + fadingArray_[i] = 1.f - linestep(s, s+n, i); else if (curve==FADING_SMOOTH) fadingArray_[i] = 1.f - smootherstep(s, s+n, i); else - fadingArray_[i] = 0.f; + fadingArray_[i] = sharpstep(s, s+n, i); // fadingArray_[i] *= val; } fading_array_changed_ = true; @@ -746,18 +747,16 @@ void Timeline::fadeIn(const GstClockTime to, const GstClockTime duration, Fading // calculate size of the smooth transition in [s e] interval const size_t n = MIN( e-s, N ); - // linear fade in ends at e + // fade in ends at e size_t i = e-n; // float val = fadingArray_[e]; for (; i < e; ++i) { - // const float x = static_cast(i-s) / static_cast(n); - const float x = 1.f - static_cast(e - i) / static_cast(n); if (curve==FADING_LINEAR) - fadingArray_[i] = x; + fadingArray_[i] = linestep(e-n, e, i); else if (curve==FADING_SMOOTH) fadingArray_[i] = smootherstep(e-n, e, i); else - fadingArray_[i] = 0.f; + fadingArray_[i] = sharpstep(e-n, e, i); // fadingArray_[i] *= val; } fading_array_changed_ = true; @@ -771,12 +770,7 @@ void Timeline::fadeInOutRange(const GstClockTime t, const GstClockTime duration, // find cuts at left and right of given time for (auto g = gaps_.begin(); g != gaps_.end(); g++) { if (g->begin < t) { - if (g->end > t) { - // inside a gap - range.begin = g->begin; - range.end = g->end; - break; - } else { + if (g->end < t) { // after a gap range.begin = g->end; } @@ -794,48 +788,56 @@ void Timeline::fadeInOutRange(const GstClockTime t, const GstClockTime duration, const size_t e = (range.end * MAX_TIMELINE_ARRAY) / timing_.end; // get index of time in section - const size_t m = (t * MAX_TIMELINE_ARRAY) / timing_.end; - - size_t l = m; - size_t r = m; + size_t l = ( MIN(t, range.begin + duration) * MAX_TIMELINE_ARRAY) / timing_.end; + size_t r = ( MAX(t, range.end - duration) * MAX_TIMELINE_ARRAY) / timing_.end; // if duration too short for a linear or smooth if (duration < 2 * step_) { curve = FADING_SHARP; } - // if duration allows to fade in and out - else if (2 * duration < range.duration()) { - l = ( (range.begin + duration) * MAX_TIMELINE_ARRAY) / timing_.end; - r = ( (range.end - duration) * MAX_TIMELINE_ARRAY) / timing_.end; + else if (duration > range.duration()) { + if (curve == FADING_SHARP) { + l = s+1; + r = e-1; + } + else + l = r = (t * MAX_TIMELINE_ARRAY) / timing_.end; } // fill values inside range - for (size_t k = s; k < e; ++k) { + for (size_t k = s; k <= e; ++k) { if (curve == FADING_LINEAR){ if (k 0 ? fadingArray_[i-1] : 0; + } + } +} + void Timeline::updateGapsFromArray(float *array, size_t array_size) { // reset gaps diff --git a/src/Timeline.h b/src/Timeline.h index 9383f4f..39cf96e 100644 --- a/src/Timeline.h +++ b/src/Timeline.h @@ -165,7 +165,10 @@ public: void fadeIn(const GstClockTime from, const GstClockTime duration, FadingCurve curve = FADING_SHARP); void fadeOut(const GstClockTime to, const GstClockTime duration, FadingCurve curve = FADING_SHARP); void fadeInOutRange(const GstClockTime t, const GstClockTime duration, bool in_and_out, FadingCurve curve = FADING_SHARP); - bool autoCut(); + + // link Fading and Gaps + bool autoGapInFade(); + void autoFadeInGaps(); private: