Timeline display in beat unit for synched to metronome

This commit is contained in:
Bruno Herbelin
2021-11-27 19:26:33 +01:00
parent ef9e41f20d
commit 809e30d906
3 changed files with 208 additions and 55 deletions

View File

@@ -584,7 +584,8 @@ bool ImGuiToolkit::SliderTiming (const char* label, uint* ms, uint v_min, uint v
#define LARGE_TICK_INCREMENT 1 #define LARGE_TICK_INCREMENT 1
#define LABEL_TICK_INCREMENT 3 #define LABEL_TICK_INCREMENT 3
void ImGuiToolkit::RenderTimeline (ImGuiWindow* window, ImRect timeline_bbox, guint64 begin, guint64 end, guint64 step, bool verticalflip) void ImGuiToolkit::RenderTimeline (ImGuiWindow* window, ImRect timeline_bbox,
guint64 begin, guint64 end, guint64 step, bool verticalflip)
{ {
static guint64 optimal_tick_marks[NUM_MARKS + LABEL_TICK_INCREMENT] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE, 60 * MINUTE }; static guint64 optimal_tick_marks[NUM_MARKS + LABEL_TICK_INCREMENT] = { 100 * MILISECOND, 500 * MILISECOND, 1 * SECOND, 2 * SECOND, 5 * SECOND, 10 * SECOND, 20 * SECOND, 1 * MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 60 * MINUTE, 60 * MINUTE };
@@ -719,7 +720,151 @@ void ImGuiToolkit::RenderTimeline (ImGuiWindow* window, ImRect timeline_bbox, gu
ImGui::PopStyleColor(1); ImGui::PopStyleColor(1);
} }
bool ImGuiToolkit::TimelineSlider (const char* label, guint64 *time, guint64 begin, guint64 first, guint64 end, guint64 step, const float width)
void ImGuiToolkit::RenderTimelineBPM (ImGuiWindow* window, ImRect timeline_bbox, double tempo, double quantum,
guint64 begin, guint64 end, guint64 step, bool verticalflip)
{
if (tempo<1.)
return;
const ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style;
const float fontsize = g.FontSize;
const ImU32 text_color = ImGui::GetColorU32(ImGuiCol_Text);
// keep duration
const guint64 duration = end - begin;
// by default, put a tick mark at every frame step
guint64 tick_step = step;
// how many pixels to represent one frame step?
const float step_ = static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
// spacing between small tick marks for frames
for (float tick_step_pixels = timeline_bbox.GetWidth() * step_; tick_step_pixels < 5.f; tick_step *= 2)
{
tick_step_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(tick_step) / static_cast<double>(duration) );
}
// by default, put large mark every beat and label every phase
guint64 time_beat = GST_SECOND * (60.0 / tempo) ;
guint64 large_tick_step = time_beat;
// how many pixels to represent one beat?
const float beat_ = static_cast<float> ( static_cast<double>(large_tick_step) / static_cast<double>(duration) );
// spacing between small tick marks for frames
for (float tick_beat_pixels = timeline_bbox.GetWidth() * beat_; tick_beat_pixels < 10.f; large_tick_step *= 2)
{
tick_beat_pixels = timeline_bbox.GetWidth() * static_cast<float> ( static_cast<double>(large_tick_step) / static_cast<double>(duration) );
}
guint64 label_tick_step = large_tick_step * (guint64) ceil(quantum);
// render tics and text
char text_buf[24];
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD);
// render tick and text END
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s",
GstToolkit::time_to_string(end, GstToolkit::TIME_STRING_MINIMAL).c_str());
ImVec2 duration_label_size = ImGui::CalcTextSize(text_buf, NULL);
ImVec2 duration_label_pos = timeline_bbox.GetTR() + ImVec2( -2.f -duration_label_size.x, fontsize);
if (verticalflip)
duration_label_pos.y -= fontsize;
ImGui::RenderTextClipped( duration_label_pos, duration_label_pos + duration_label_size,
text_buf, NULL, &duration_label_size);
window->DrawList->AddLine( timeline_bbox.GetTR(), timeline_bbox.GetBR(), text_color, 1.5f);
// render tick and text START
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s",
GstToolkit::time_to_string(begin, GstToolkit::TIME_STRING_MINIMAL).c_str());
ImVec2 beginning_label_size = ImGui::CalcTextSize(text_buf, NULL);
ImVec2 beginning_label_pos = timeline_bbox.GetTL() + ImVec2(3.f, fontsize);
if (verticalflip)
beginning_label_pos.y -= fontsize;
if ( beginning_label_pos.x + beginning_label_size.x < duration_label_pos . x) {
ImGui::RenderTextClipped( beginning_label_pos, beginning_label_pos + beginning_label_size,
text_buf, NULL, &beginning_label_size);
}
window->DrawList->AddLine( timeline_bbox.GetTL(), timeline_bbox.GetBL(), text_color, 1.5f);
ImGui::PopFont();
// render the tick marks along TIMELINE
ImGui::PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] -ImVec4(0.f,0.f,0.f,0.4f));
ImVec2 pos = verticalflip ? timeline_bbox.GetBL() : timeline_bbox.GetTL();
// SMALL ticks every step
float tick_length = style.FramePadding.y;
guint64 tick = 0;
while ( tick < end )
{
// draw the tick mark each step
window->DrawList->AddLine( pos, pos + ImVec2(0.f, verticalflip ? -tick_length : tick_length), text_color);
// next tick
tick += tick_step;
float tick_percent = static_cast<float> ( static_cast<double>(tick-begin) / static_cast<double>(duration) );
if (verticalflip)
pos = ImLerp(timeline_bbox.GetBL(), timeline_bbox.GetBR(), tick_percent);
else
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
// LARGE tick marks every tempo
pos = verticalflip ? timeline_bbox.GetBL() : timeline_bbox.GetTL();
tick_length = fontsize - style.FramePadding.y;
tick = 0;
while ( tick < end )
{
// label tick mark
if ( (tick%label_tick_step) < 1 )
{
// beat count label
guint64 ticklabel = tick / time_beat;
ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%ld", ticklabel);
ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL);
ImVec2 mini = ImVec2( pos.x - label_size.x / 2.f, pos.y);
ImVec2 maxi = ImVec2( pos.x + label_size.x / 2.f, pos.y);
if (verticalflip) {
mini.y -= tick_length + label_size.y;
maxi.y -= tick_length;
}
else {
mini.y += tick_length;
maxi.y += tick_length + label_size.y;
}
// do not overlap with labels for beginning and duration
if (mini.x - style.ItemSpacing.x > (beginning_label_pos.x + beginning_label_size.x) && maxi.x + style.ItemSpacing.x < duration_label_pos.x)
ImGui::RenderTextClipped(mini, maxi, text_buf, NULL, &label_size);
}
// draw the tick mark each step
window->DrawList->AddLine( pos, pos + ImVec2(0.f, verticalflip ? -tick_length : tick_length), text_color);
// next tick
tick += large_tick_step;
float tick_percent = static_cast<float> ( static_cast<double>(tick-begin) / static_cast<double>(duration) );
if (verticalflip)
pos = ImLerp(timeline_bbox.GetBL(), timeline_bbox.GetBR(), tick_percent);
else
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), tick_percent);
}
ImGui::PopStyleColor(1);
}
bool ImGuiToolkit::TimelineSlider (const char* label, guint64 *time, guint64 begin, guint64 first,
guint64 end, guint64 step, const float width, double tempo, double quantum)
{ {
// get window // get window
ImGuiWindow* window = ImGui::GetCurrentWindow(); ImGuiWindow* window = ImGui::GetCurrentWindow();
@@ -788,7 +933,7 @@ bool ImGuiToolkit::TimelineSlider (const char* label, guint64 *time, guint64 beg
&time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb); &time_end, "%.2f", 1.f, ImGuiSliderFlags_None, &grab_slider_bb);
if (value_changed){ if (value_changed){
*time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(end - begin) ); *time = static_cast<guint64> ( 0.1 * static_cast<double>(time_slider) * static_cast<double>(end - begin) );
if (first != -1) if (first != GST_CLOCK_TIME_NONE)
*time -= first; *time -= first;
grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive); grab_slider_color = ImGui::GetColorU32(ImGuiCol_SliderGrabActive);
} }
@@ -802,7 +947,10 @@ bool ImGuiToolkit::TimelineSlider (const char* label, guint64 *time, guint64 beg
ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding); ImGui::RenderFrame(bbox.Min, bbox.Max, frame_col, true, style.FrameRounding);
// render the timeline // render the timeline
RenderTimeline(window, timeline_bbox, begin, end, step); if (tempo > 0 && quantum > 0)
RenderTimelineBPM(window, timeline_bbox, tempo, quantum, begin, end, step);
else
RenderTimeline(window, timeline_bbox, begin, end, step);
// draw slider grab handle // draw slider grab handle
if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) { if (grab_slider_bb.Max.x > grab_slider_bb.Min.x) {
@@ -817,59 +965,59 @@ bool ImGuiToolkit::TimelineSlider (const char* label, guint64 *time, guint64 beg
} }
void ImGuiToolkit::Timeline (const char* label, guint64 time, guint64 begin, guint64 end, guint64 step, const float width) //void ImGuiToolkit::Timeline (const char* label, guint64 time, guint64 begin, guint64 end, guint64 step, const float width)
{ //{
// get window // // get window
ImGuiWindow* window = ImGui::GetCurrentWindow(); // ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems) // if (window->SkipItems)
return; // return;
// get style & id // // get style & id
const ImGuiContext& g = *GImGui; // const ImGuiContext& g = *GImGui;
const ImGuiStyle& style = g.Style; // const ImGuiStyle& style = g.Style;
const float fontsize = g.FontSize; // const float fontsize = g.FontSize;
const ImGuiID id = window->GetID(label); // const ImGuiID id = window->GetID(label);
// // //
// FIRST PREPARE ALL data structures // // FIRST PREPARE ALL data structures
// // //
// widget bounding box // // widget bounding box
const float height = 2.f * (fontsize + style.FramePadding.y); // const float height = 2.f * (fontsize + style.FramePadding.y);
ImVec2 pos = window->DC.CursorPos; // ImVec2 pos = window->DC.CursorPos;
ImVec2 size = ImVec2(width, height); // ImVec2 size = ImVec2(width, height);
ImRect bbox(pos, pos + size); // ImRect bbox(pos, pos + size);
ImGui::ItemSize(size, style.FramePadding.y); // ImGui::ItemSize(size, style.FramePadding.y);
if (!ImGui::ItemAdd(bbox, id)) // if (!ImGui::ItemAdd(bbox, id))
return; // return;
// cursor size // // cursor size
const float cursor_width = 0.5f * fontsize; // const float cursor_width = 0.5f * fontsize;
// TIMELINE is inside the bbox, in a slightly smaller bounding box // // TIMELINE is inside the bbox, in a slightly smaller bounding box
ImRect timeline_bbox(bbox); // ImRect timeline_bbox(bbox);
timeline_bbox.Expand( ImVec2() - style.FramePadding ); // timeline_bbox.Expand( ImVec2() - style.FramePadding );
// units conversion: from time to float (calculation made with higher precision first) // // units conversion: from time to float (calculation made with higher precision first)
guint64 duration = end - begin; // guint64 duration = end - begin;
float time_ = static_cast<float> ( static_cast<double>(time - begin) / static_cast<double>(duration) ); // float time_ = static_cast<float> ( static_cast<double>(time - begin) / static_cast<double>(duration) );
// // //
// THIRD RENDER // // THIRD RENDER
// // //
// Render the bounding box // // Render the bounding box
ImGui::RenderFrame(bbox.Min, bbox.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); // ImGui::RenderFrame(bbox.Min, bbox.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
// render the timeline // // render the timeline
RenderTimeline(window, timeline_bbox, begin, end, step); // RenderTimeline(window, timeline_bbox, begin, end, step);
// draw the cursor // // draw the cursor
if ( time_ > -FLT_EPSILON && time_ < 1.f ) { // if ( time_ > -FLT_EPSILON && time_ < 1.f ) {
pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f); // pos = ImLerp(timeline_bbox.GetTL(), timeline_bbox.GetTR(), time_) - ImVec2(cursor_width, 2.f);
ImGui::RenderArrow(window->DrawList, pos, ImGui::GetColorU32(ImGuiCol_SliderGrab), ImGuiDir_Up); // ImGui::RenderArrow(window->DrawList, pos, ImGui::GetColorU32(ImGuiCol_SliderGrab), ImGuiDir_Up);
} // }
} //}
bool ImGuiToolkit::InvisibleSliderInt (const char* label, uint *index, uint min, uint max, ImVec2 size) bool ImGuiToolkit::InvisibleSliderInt (const char* label, uint *index, uint min, uint max, ImVec2 size)
{ {

View File

@@ -3,10 +3,8 @@
#include <glib.h> #include <glib.h>
#include <string> #include <string>
#include <list>
#include <vector> #include <vector>
#include "imgui.h" #include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS #ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
@@ -45,9 +43,10 @@ namespace ImGuiToolkit
// sliders // sliders
bool SliderTiming (const char* label, uint *ms, uint v_min, uint v_max, uint v_step, const char* text_max = nullptr); bool SliderTiming (const char* label, uint *ms, uint v_min, uint v_max, uint v_step, const char* text_max = nullptr);
bool TimelineSlider (const char* label, guint64 *time, guint64 begin, guint64 first, guint64 end, guint64 step, const float width); bool TimelineSlider (const char* label, guint64 *time, guint64 begin, guint64 first, guint64 end, guint64 step, const float width, double tempo = 0, double quantum = 0);
void RenderTimeline (struct ImGuiWindow* window, struct ImRect timeline_bbox, guint64 begin, guint64 end, guint64 step, bool verticalflip = false); void RenderTimeline (struct ImGuiWindow* window, struct ImRect timeline_bbox, guint64 begin, guint64 end, guint64 step, bool verticalflip = false);
void Timeline (const char* label, guint64 time, guint64 begin, guint64 end, guint64 step, const float width); void RenderTimelineBPM (struct ImGuiWindow* window, struct ImRect timeline_bbox, double tempo, double quantum, guint64 begin, guint64 end, guint64 step, bool verticalflip = false);
// void Timeline (const char* label, guint64 time, guint64 begin, guint64 end, guint64 step, const float width);
bool InvisibleSliderInt(const char* label, uint *index, uint min, uint max, const ImVec2 size); bool InvisibleSliderInt(const char* label, uint *index, uint min, uint max, const ImVec2 size);
bool EditPlotLines(const char* label, float *array, int values_count, float values_min, float values_max, const ImVec2 size); bool EditPlotLines(const char* label, float *array, int values_count, float values_min, float values_max, const ImVec2 size);
bool EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, guint64 begin, guint64 end, bool cut, bool *released, const ImVec2 size); bool EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, guint64 begin, guint64 end, bool cut, bool *released, const ImVec2 size);

View File

@@ -1119,7 +1119,8 @@ void UserInterface::RenderTimer()
if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) { if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
guint64 time_phase = GST_SECOND * (60.0 * q / t) ; guint64 time_phase = GST_SECOND * (60.0 * q / t) ;
ImGui::Text("Quantum: %d\nPhase duration: %s", (int) ceil(float_value), GstToolkit::time_to_string(time_phase, GstToolkit::TIME_STRING_READABLE).c_str() ); ImGui::Text("%d beats per phase\n= %s at %d BPM", (int) ceil(float_value),
GstToolkit::time_to_string(time_phase, GstToolkit::TIME_STRING_READABLE).c_str(), (int) t);
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
@@ -1210,7 +1211,7 @@ void UserInterface::RenderTimer()
} }
if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { if (ImGui::IsItemHovered() || ImGui::IsItemActive()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::Text("Countdown\n%s", GstToolkit::time_to_string(duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() ); ImGui::Text("%s\ncountdown", GstToolkit::time_to_string(duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() );
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
@@ -3320,8 +3321,13 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp)
} }
// custom timeline slider // custom timeline slider
mediaplayer_slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, tl->begin(), if (mediaplayer_active_->syncToMetronome() > Metronome::SYNC_NONE)
tl->first(), tl->end(), tl->step(), size.x); mediaplayer_slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, tl->begin(),
tl->first(), tl->end(), tl->step(), size.x,
Metronome::manager().tempo(), Metronome::manager().quantum());
else
mediaplayer_slider_pressed_ = ImGuiToolkit::TimelineSlider("##timeline", &seek_t, tl->begin(),
tl->first(), tl->end(), tl->step(), size.x);
} }
} }