diff --git a/MediaPlayer.cpp b/MediaPlayer.cpp index 512abeb..a2be6d6 100644 --- a/MediaPlayer.cpp +++ b/MediaPlayer.cpp @@ -28,6 +28,7 @@ #include "BaseToolkit.h" #include "GstToolkit.h" #include "RenderingManager.h" +#include "Metronome.h" #include "MediaPlayer.h" @@ -49,6 +50,9 @@ MediaPlayer::MediaPlayer() desired_state_ = GST_STATE_PAUSED; failed_ = false; + pending_ = false; + metro_linked_ = false; + force_update_ = false; seeking_ = false; rewind_on_disable_ = false; force_software_decoding_ = false; @@ -597,13 +601,9 @@ void MediaPlayer::setSoftwareDecodingForced(bool on) reopen(); } -void MediaPlayer::play(bool on) +void MediaPlayer::execute_play_command(bool on) { - // ignore if disabled, and cannot play an image - if (!enabled_ || media_.isimage) - return; - - // request state + // request state GstState requested_state = on ? GST_STATE_PLAYING : GST_STATE_PAUSED; // ignore if requesting twice same state @@ -619,9 +619,10 @@ void MediaPlayer::play(bool on) // requesting to play, but stopped at end of stream : rewind first ! if ( desired_state_ == GST_STATE_PLAYING) { - if ( ( rate_ < 0.0 && position_ <= timeline_.next(0) ) - || ( rate_ > 0.0 && position_ >= timeline_.previous(timeline_.last()) ) ) - rewind(); + if (rate_ > 0.0 && position_ >= timeline_.previous(timeline_.last())) + execute_seek_command(timeline_.next(0)); + else if ( rate_ < 0.0 && position_ <= timeline_.next(0) ) + execute_seek_command(timeline_.previous(timeline_.last())); } // all ready, apply state change immediately @@ -629,14 +630,32 @@ void MediaPlayer::play(bool on) if (ret == GST_STATE_CHANGE_FAILURE) { Log::Warning("MediaPlayer %s Failed to play", std::to_string(id_).c_str()); failed_ = true; - } + } #ifdef MEDIA_PLAYER_DEBUG else if (on) Log::Info("MediaPlayer %s Start", std::to_string(id_).c_str()); else Log::Info("MediaPlayer %s Stop [%ld]", std::to_string(id_).c_str(), position()); #endif +} +void MediaPlayer::play(bool on) +{ + // ignore if disabled, and cannot play an image + if (!enabled_ || media_.isimage || pending_) + return; + + // Metronome + if (metro_linked_) { + // busy with this play() + pending_ = true; + // Execute: sync to Metronome if active + Metronome::manager().executeAtBeat( std::bind([](MediaPlayer *p, bool o) { + p->execute_play_command(o); p->pending_=false; }, this, on) ); + } + else + // execute immediately + execute_play_command( on ); } bool MediaPlayer::isPlaying(bool testpipeline) const @@ -666,44 +685,60 @@ void MediaPlayer::setLoop(MediaPlayer::LoopMode mode) loop_ = mode; } +//void + void MediaPlayer::rewind(bool force) { - if (!enabled_ || !media_.seekable) + if (!enabled_ || !media_.seekable || pending_) return; - // playing forward, loop to begin - if (rate_ > 0.0) { - // begin is the end of a gab which includes the first PTS (if exists) - // normal case, begin is zero - execute_seek_command( timeline_.next(0) ); - } + // playing forward, loop to begin; + // begin is the end of a gab which includes the first PTS (if exists) + // normal case, begin is zero // playing backward, loop to endTimeInterval gap; - else { - // end is the start of a gab which includes the last PTS (if exists) - // normal case, end is last frame - execute_seek_command( timeline_.previous(timeline_.last()) ); - } + // end is the start of a gab which includes the last PTS (if exists) + // normal case, end is last frame + GstClockTime target = (rate_ > 0.0) ? timeline_.next(0) : timeline_.previous(timeline_.last()); - if (force) { - GstState state; - gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE); - update(); + // Metronome + if (metro_linked_) { + // busy with this play() + pending_ = true; + // Execute: sync to Metronome if active + Metronome::manager().executeAtBeat( std::bind([](MediaPlayer *p, GstClockTime t, bool f) { + p->execute_seek_command( t, f ); p->pending_=false; }, this, target, force) ); } + else + // execute immediately + execute_seek_command( target, force ); } void MediaPlayer::step() { // useful only when Paused - if (!enabled_ || isPlaying()) + if (!enabled_ || isPlaying() || pending_) return; if ( ( rate_ < 0.0 && position_ <= timeline_.next(0) ) || ( rate_ > 0.0 && position_ >= timeline_.previous(timeline_.last()) ) ) rewind(); + else { + // step event + GstEvent *stepevent = gst_event_new_step (GST_FORMAT_BUFFERS, 1, ABS(rate_), TRUE, FALSE); - // step - gst_element_send_event (pipeline_, gst_event_new_step (GST_FORMAT_BUFFERS, 1, ABS(rate_), TRUE, FALSE)); + // Metronome + if (metro_linked_) { + // busy with this play() + pending_ = true; + // Execute: sync to Metronome if active + Metronome::manager().executeAtBeat( std::bind([](MediaPlayer *p, GstEvent *e) { + gst_element_send_event(p->pipeline_, e); p->pending_=false; }, this, stepevent) ); + } + else + // execute immediately + gst_element_send_event (pipeline_, stepevent); + } } bool MediaPlayer::go_to(GstClockTime pos) @@ -893,7 +928,7 @@ void MediaPlayer::update() } // prevent unnecessary updates: disabled or already filled image - if (!enabled_ || (media_.isimage && textureindex_>0 ) ) + if ( (!enabled_ && !force_update_) || (media_.isimage && textureindex_>0 ) ) return; // local variables before trying to update @@ -904,13 +939,6 @@ void MediaPlayer::update() index_lock_.lock(); // get the last frame filled from fill_frame() read_index = last_index_; -// // Do NOT miss and jump directly (after seek) to a pre-roll -// for (guint i = 0; i < N_VFRAME; ++i) { -// if (frame_[i].status == PREROLL) { -// read_index = i; -// break; -// } -// } // unlock access to index change index_lock_.unlock(); @@ -988,6 +1016,7 @@ void MediaPlayer::update() execute_loop_command(); } + force_update_ = false; } void MediaPlayer::execute_loop_command() @@ -1004,7 +1033,7 @@ void MediaPlayer::execute_loop_command() } } -void MediaPlayer::execute_seek_command(GstClockTime target) +void MediaPlayer::execute_seek_command(GstClockTime target, bool force) { if ( pipeline_ == nullptr || !media_.seekable ) return; @@ -1052,6 +1081,13 @@ void MediaPlayer::execute_seek_command(GstClockTime target) #endif } + // Force update + if (force) { + GstState state; + gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE); + force_update_ = true; + } + } void MediaPlayer::setPlaySpeed(double s) diff --git a/MediaPlayer.h b/MediaPlayer.h index fe0bba8..dfc6d69 100644 --- a/MediaPlayer.h +++ b/MediaPlayer.h @@ -188,6 +188,10 @@ public: * Seek to zero * */ void rewind(bool force = false); + /** + * pending + * */ + bool pending() const { return pending_; } /** * Get position time * */ @@ -296,11 +300,14 @@ private: GstVideoInfo v_frame_video_info_; std::atomic opened_; std::atomic failed_; + bool force_update_; + bool pending_; bool seeking_; bool enabled_; bool rewind_on_disable_; bool force_software_decoding_; std::string decoder_name_; + bool metro_linked_; // fps counter struct TimeCounter { @@ -348,8 +355,9 @@ private: // gst pipeline control void execute_open(); + void execute_play_command(bool on); void execute_loop_command(); - void execute_seek_command(GstClockTime target = GST_CLOCK_TIME_NONE); + void execute_seek_command(GstClockTime target = GST_CLOCK_TIME_NONE, bool force = false); // gst frame filling void init_texture(guint index); diff --git a/MediaSource.cpp b/MediaSource.cpp index 31f9c7e..edf2c76 100644 --- a/MediaSource.cpp +++ b/MediaSource.cpp @@ -17,6 +17,7 @@ * along with this program. If not, see . **/ + #include #include "defines.h" diff --git a/Metronome.cpp b/Metronome.cpp index 641bf26..3526402 100644 --- a/Metronome.cpp +++ b/Metronome.cpp @@ -17,6 +17,7 @@ #include "Settings.h" #include "Metronome.h" +#include "Log.h" namespace ableton @@ -129,15 +130,11 @@ Metronome::Metronome() bool Metronome::init() { - // connect - link_.enable(true); - - // enable sync - link_.enableStartStopSync(true); - // set parameters - setTempo(Settings::application.metronome.tempo); - setQuantum(Settings::application.metronome.quantum); + setEnabled(Settings::application.timer.link_enabled); + setTempo(Settings::application.timer.link_tempo); + setQuantum(Settings::application.timer.link_quantum); + setStartStopSync(Settings::application.timer.link_start_stop_sync); // no reason for failure? return true; @@ -146,12 +143,24 @@ bool Metronome::init() void Metronome::terminate() { // save current tempo - Settings::application.metronome.tempo = tempo(); + Settings::application.timer.link_tempo = tempo(); // disconnect link_.enable(false); } +void Metronome::setEnabled (bool on) +{ + link_.enable(on); + Settings::application.timer.link_enabled = link_.isEnabled(); + Log::Info("Metronome Ableton Link %s", Settings::application.timer.link_enabled ? "Enabled" : "Disabled"); +} + +bool Metronome::enabled () const +{ + return link_.isEnabled(); +} + double Metronome::beats() const { return engine_.beatTime(); @@ -165,7 +174,7 @@ double Metronome::phase() const void Metronome::setQuantum(double q) { engine_.setQuantum(q); - Settings::application.metronome.quantum = engine_.quantum(); + Settings::application.timer.link_quantum = engine_.quantum(); } double Metronome::quantum() const @@ -178,7 +187,7 @@ void Metronome::setTempo(double t) // set the tempo to t // OR // adopt the last tempo value that have been proposed on the network - Settings::application.metronome.tempo = engine_.setTempo(t); + Settings::application.timer.link_tempo = engine_.setTempo(t); } double Metronome::tempo() const @@ -186,11 +195,40 @@ double Metronome::tempo() const return engine_.tempo(); } + +void Metronome::setStartStopSync (bool on) +{ + engine_.setStartStopSyncEnabled(on); + Settings::application.timer.link_start_stop_sync = engine_.isStartStopSyncEnabled(); + Log::Info("Metronome Ableton Link start & stop sync %s", Settings::application.timer.link_start_stop_sync ? "Enabled" : "Disabled"); +} + +bool Metronome::startStopSync () const +{ + return engine_.isStartStopSyncEnabled(); +} + +void Metronome::restart() +{ + engine_.startPlaying(); +} + std::chrono::microseconds Metronome::timeToBeat() { return engine_.timeNextBeat() - engine_.now(); } +void delay(std::function f, std::chrono::microseconds us) +{ + std::this_thread::sleep_for(us); + f(); +} + +void Metronome::executeAtBeat( std::function f ) +{ + std::thread( delay, f, timeToBeat() ).detach(); +} + size_t Metronome::peers() const { return link_.numPeers(); diff --git a/Metronome.h b/Metronome.h index 862a214..71dff6c 100644 --- a/Metronome.h +++ b/Metronome.h @@ -2,6 +2,8 @@ #define METRONOME_H #include +#include +#include class Metronome { @@ -22,8 +24,8 @@ public: bool init (); void terminate (); - double beats () const; - double phase () const; + void setEnabled (bool on); + bool enabled () const; void setTempo (double t); double tempo () const; @@ -31,9 +33,31 @@ public: void setQuantum (double q); double quantum () const; + void setStartStopSync (bool on); + bool startStopSync () const; + void restart(); + + // get beat and phase + double beats () const; + double phase () const; + + // mechanisms to delay execution to next beat of phase std::chrono::microseconds timeToBeat(); + void executeAtBeat( std::function f ); size_t peers () const; + }; +/// Example calls to executeAtBeat +/// +/// With a Lamda function calling a member function of an object +/// - without parameter +/// Metronome::manager().executeAtBeat( std::bind([](MediaPlayer *p) { p->rewind(); }, mediaplayer_) ); +/// +/// - with parameter +/// Metronome::manager().executeAtBeat( std::bind([](MediaPlayer *p, bool o) { p->play(o); }, mediaplayer_, on) ); +/// + + #endif // METRONOME_H diff --git a/Settings.cpp b/Settings.cpp index 0002ff5..868a19b 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -34,7 +34,7 @@ using namespace tinyxml2; Settings::Application Settings::application; string settingsFilename = ""; -void Settings::Save() +void Settings::Save(uint64_t runtime) { // impose C locale for all app setlocale(LC_ALL, "C"); @@ -49,6 +49,9 @@ void Settings::Save() pRoot->SetAttribute("minor", VIMIX_VERSION_MINOR); xmlDoc.InsertEndChild(pRoot); #endif + // runtime + if (runtime>0) + pRoot->SetAttribute("runtime", runtime + application.total_runtime); string comment = "Settings for " + application.name; XMLComment *pComment = xmlDoc.NewComment(comment.c_str()); @@ -93,7 +96,8 @@ void Settings::Save() XMLElement *widgetsNode = xmlDoc.NewElement( "Widgets" ); widgetsNode->SetAttribute("preview", application.widget.preview); widgetsNode->SetAttribute("preview_view", application.widget.preview_view); - widgetsNode->SetAttribute("history", application.widget.history); + widgetsNode->SetAttribute("timer", application.widget.timer); + widgetsNode->SetAttribute("timer_view", application.widget.timer_view); widgetsNode->SetAttribute("media_player", application.widget.media_player); widgetsNode->SetAttribute("media_player_view", application.widget.media_player_view); widgetsNode->SetAttribute("timeline_editmode", application.widget.timeline_editmode); @@ -250,10 +254,14 @@ void Settings::Save() } // Metronome - XMLElement *metroConfNode = xmlDoc.NewElement( "Metronome" ); - metroConfNode->SetAttribute("tempo", application.metronome.tempo); - metroConfNode->SetAttribute("quantum", application.metronome.quantum); - pRoot->InsertEndChild(metroConfNode); + XMLElement *timerConfNode = xmlDoc.NewElement( "Timer" ); + timerConfNode->SetAttribute("mode", application.timer.mode); + timerConfNode->SetAttribute("link_enabled", application.timer.link_enabled); + timerConfNode->SetAttribute("link_tempo", application.timer.link_tempo); + timerConfNode->SetAttribute("link_quantum", application.timer.link_quantum); + timerConfNode->SetAttribute("link_start_stop_sync", application.timer.link_start_stop_sync); + timerConfNode->SetAttribute("stopwatch_duration", application.timer.stopwatch_duration); + pRoot->InsertEndChild(timerConfNode); // First save : create filename if (settingsFilename.empty()) @@ -296,6 +304,8 @@ void Settings::Load() if (version_major != VIMIX_VERSION_MAJOR || version_minor != VIMIX_VERSION_MINOR) return; #endif + // runtime + pRoot->QueryUnsigned64Attribute("runtime", &application.total_runtime); XMLElement * applicationNode = pRoot->FirstChildElement("Application"); if (applicationNode != nullptr) { @@ -314,7 +324,8 @@ void Settings::Load() if (widgetsNode != nullptr) { widgetsNode->QueryBoolAttribute("preview", &application.widget.preview); widgetsNode->QueryIntAttribute("preview_view", &application.widget.preview_view); - widgetsNode->QueryBoolAttribute("history", &application.widget.history); + widgetsNode->QueryBoolAttribute("timer", &application.widget.timer); + widgetsNode->QueryIntAttribute("timer_view", &application.widget.timer_view); widgetsNode->QueryBoolAttribute("media_player", &application.widget.media_player); widgetsNode->QueryIntAttribute("media_player_view", &application.widget.media_player_view); widgetsNode->QueryBoolAttribute("timeline_editmode", &application.widget.timeline_editmode); @@ -525,10 +536,14 @@ void Settings::Load() } // bloc metronome - XMLElement * metroconfnode = pRoot->FirstChildElement("Metronome"); - if (metroconfnode != nullptr) { - metroconfnode->QueryDoubleAttribute("tempo", &application.metronome.tempo); - metroconfnode->QueryDoubleAttribute("quantum", &application.metronome.quantum); + XMLElement * timerconfnode = pRoot->FirstChildElement("Timer"); + if (timerconfnode != nullptr) { + timerconfnode->QueryUnsigned64Attribute("mode", &application.timer.mode); + timerconfnode->QueryBoolAttribute("link_enabled", &application.timer.link_enabled); + timerconfnode->QueryDoubleAttribute("link_tempo", &application.timer.link_tempo); + timerconfnode->QueryDoubleAttribute("link_quantum", &application.timer.link_quantum); + timerconfnode->QueryBoolAttribute("link_start_stop_sync", &application.timer.link_start_stop_sync); + timerconfnode->QueryUnsigned64Attribute("stopwatch_duration", &application.timer.stopwatch_duration); } } @@ -606,8 +621,6 @@ void Settings::Unlock() void Settings::Check() { - Settings::Save(); - XMLDocument xmlDoc; XMLError eResult = xmlDoc.LoadFile(settingsFilename.c_str()); if (XMLResultError(eResult)) { diff --git a/Settings.h b/Settings.h index 279a008..daa86ca 100644 --- a/Settings.h +++ b/Settings.h @@ -25,10 +25,11 @@ struct WidgetsConfig int preview_view; bool media_player; int media_player_view; + bool timer; + int timer_view; bool timeline_editmode; bool shader_editor; bool toolbox; - bool history; bool help; WidgetsConfig() { @@ -38,13 +39,14 @@ struct WidgetsConfig logs = false; preview = false; preview_view = -1; - history = false; media_player = false; media_player_view = -1; timeline_editmode = false; shader_editor = false; toolbox = false; help = false; + timer = false; + timer_view = -1; } }; @@ -168,18 +170,22 @@ struct SourceConfig } }; -struct MetronomeConfig +struct TimerConfig { - bool start_stop_sync; - double tempo; - double quantum; - bool sync_tempo; + uint64_t mode; + bool link_enabled; + double link_tempo; + double link_quantum; + bool link_start_stop_sync; + uint64_t stopwatch_duration; - MetronomeConfig() { - start_stop_sync = true; - tempo = 120.; - quantum = 4.; - sync_tempo = true; + TimerConfig() { + mode = 0; + link_enabled = true; + link_tempo = 120.; + link_quantum = 4.; + link_start_stop_sync = true; + stopwatch_duration = 60; } }; @@ -192,6 +198,7 @@ struct Application // Verification std::string name; std::string executable; + uint64_t total_runtime; // Global settings Application interface float scale; @@ -238,8 +245,8 @@ struct Application History recentImport; std::map< std::string, std::string > dialogRecentFolder; - // Metronome - MetronomeConfig metronome; + // Metronome & stopwatch + TimerConfig timer; Application() : fresh_start(false), instance_id(0), name(APP_NAME), executable(APP_NAME) { scale = 1.f; @@ -268,7 +275,7 @@ struct Application extern Application application; // Save and Load store settings in XML file -void Save(); +void Save(uint64_t runtime = 0); void Load(); void Lock(); void Unlock(); diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index a1a80a8..7f234d9 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -109,9 +109,22 @@ FrameGrabber *delayTrigger(FrameGrabber *g, std::chrono::milliseconds delay) { return g; } +// Helper functions for imgui window aspect-ratio constraints +struct CustomConstraints +{ + static void AspectRatio(ImGuiSizeCallbackData* data) { + float *ar = (float*) data->UserData; + data->DesiredSize.y = (data->CurrentSize.x / (*ar)) + 35.f; + } + static void Square(ImGuiSizeCallbackData* data) { + data->DesiredSize.x = data->DesiredSize.y = (data->DesiredSize.x > data->DesiredSize.y ? data->DesiredSize.x : data->DesiredSize.y); + } + +}; UserInterface::UserInterface() { + start_time = gst_util_get_timestamp (); ctrl_modifier_active = false; alt_modifier_active = false; shift_modifier_active = false; @@ -204,6 +217,11 @@ bool UserInterface::Init() return true; } +uint64_t UserInterface::Runtime() const +{ + return gst_util_get_timestamp () - start_time; +} + void UserInterface::handleKeyboard() { const ImGuiIO& io = ImGui::GetIO(); @@ -250,11 +268,15 @@ void UserInterface::handleKeyboard() Settings::application.widget.logs = !Settings::application.widget.logs; } else if (ImGui::IsKeyPressed( GLFW_KEY_T )) { + // Timers + Settings::application.widget.timer = !Settings::application.widget.timer; + } + else if (ImGui::IsKeyPressed( GLFW_KEY_G )) { // Developer toolbox Settings::application.widget.toolbox = !Settings::application.widget.toolbox; } else if (ImGui::IsKeyPressed( GLFW_KEY_H )) { - // Session toolbox + // Helper Settings::application.widget.help = !Settings::application.widget.help; } else if (ImGui::IsKeyPressed( GLFW_KEY_E )) { @@ -374,11 +396,11 @@ void UserInterface::handleKeyboard() // 3. hide windows else if (Settings::application.widget.preview || Settings::application.widget.media_player || - Settings::application.widget.history || + Settings::application.widget.timer || Settings::application.widget.logs) { Settings::application.widget.preview = false; Settings::application.widget.media_player = false; - Settings::application.widget.history = false; + Settings::application.widget.timer = false; Settings::application.widget.logs = false; } // 4. cancel selection @@ -771,8 +793,9 @@ void UserInterface::Render() if (Settings::application.widget.preview && ( Settings::application.widget.preview_view < 0 || Settings::application.widget.preview_view == Settings::application.current_view )) RenderPreview(); - if (Settings::application.widget.history) - RenderHistory(); + if (Settings::application.widget.timer && ( Settings::application.widget.timer_view < 0 || + Settings::application.widget.timer_view == Settings::application.current_view )) + RenderTimer(); if (Settings::application.widget.shader_editor) RenderShaderEditor(); if (Settings::application.widget.logs) @@ -944,62 +967,263 @@ void UserInterface::handleScreenshot() } } -void UserInterface::RenderHistory() -{ - float history_height = 5.f * ImGui::GetFrameHeightWithSpacing(); - ImVec2 MinWindowSize = ImVec2(250.f, history_height); - ImGui::SetNextWindowPos(ImVec2(1180, 400), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSizeConstraints(MinWindowSize, ImVec2(FLT_MAX, FLT_MAX)); - if ( !ImGui::Begin(IMGUI_TITLE_HISTORY, &Settings::application.widget.history, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse )) +#define MAX_SEGMENTS 64 + +void UserInterface::RenderTimer() +{ + // timer modes : 0 Metronome, 1 Stopwatch + static std::array< std::string, 2 > timer_menu = { " Metronome ", " Stopwatch " }; + + // constraint position + static ImVec2 timer_window_pos = ImVec2(1180, 20); + static ImVec2 timer_window_size = ImVec2(400, 400); + SetNextWindowVisible(timer_window_pos, timer_window_size); + + // constraint square resizing + ImGui::SetNextWindowSizeConstraints(ImVec2(300, 300), ImVec2(600, 600), CustomConstraints::Square); + + if ( !ImGui::Begin(IMGUI_TITLE_TIMER, &Settings::application.widget.timer, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse )) { ImGui::End(); return; } + // current window + ImGuiWindow* window = ImGui::GetCurrentWindow(); + timer_window_pos = window->Pos; + timer_window_size = window->Size; + // menu (no title bar) - bool tmp = false; if (ImGui::BeginMenuBar()) { + // Close and widget menu if (ImGuiToolkit::IconButton(4,16)) - Settings::application.widget.history = false; - if (ImGui::BeginMenu(IMGUI_TITLE_HISTORY)) + Settings::application.widget.timer = false; + if (ImGui::BeginMenu(IMGUI_TITLE_TIMER)) { - if ( ImGui::MenuItem( ICON_FA_UNDO " Undo", CTRL_MOD "Z") ) - Action::manager().undo(); - if ( ImGui::MenuItem( ICON_FA_REDO " Redo", CTRL_MOD "Shift+Z") ) - Action::manager().redo(); - - ImGui::MenuItem( ICON_FA_DIRECTIONS " Follow view", nullptr, &Settings::application.action_history_follow_view); + // Enable/Disable Ableton Link + if ( ImGui::MenuItem( ICON_FA_USER_CLOCK " Ableton Link", nullptr, &Settings::application.timer.link_enabled) ) { + Metronome::manager().setEnabled(Settings::application.timer.link_enabled); + } + // output manager menu + ImGui::Separator(); + bool pinned = Settings::application.widget.timer_view == Settings::application.current_view; + if ( ImGui::MenuItem( ICON_FA_MAP_PIN " Pin window to view", nullptr, &pinned) ){ + if (pinned) + Settings::application.widget.timer_view = Settings::application.current_view; + else + Settings::application.widget.timer_view = -1; + } if ( ImGui::MenuItem( ICON_FA_TIMES " Close") ) - Settings::application.widget.history = false; + Settings::application.widget.timer = false; ImGui::EndMenu(); } - if ( ImGui::Selectable(ICON_FA_UNDO, &tmp, ImGuiSelectableFlags_None, ImVec2(20,0))) - Action::manager().undo(); - if ( ImGui::Selectable(ICON_FA_REDO, &tmp, ImGuiSelectableFlags_None, ImVec2(20,0))) - Action::manager().redo(); + // Selection of the timer mode + if (ImGui::BeginMenu( timer_menu[Settings::application.timer.mode].c_str() )) + { + for (size_t i = 0; i < timer_menu.size(); ++i) { + if (ImGui::MenuItem(timer_menu[i].c_str(), NULL, Settings::application.timer.mode==i)) + Settings::application.timer.mode = i; + } + ImGui::EndMenu(); + } ImGui::EndMenuBar(); } - if (ImGui::ListBoxHeader("##History", ImGui::GetContentRegionAvail() ) ) - { - for (uint i = Action::manager().max(); i > 0; i--) { + // Window draw parameters + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + // positions and size of GUI elements + const float margin = window->MenuBarHeight(); + const float h = 0.4f * ImGui::GetFrameHeight(); + const ImVec2 circle_top_left = window->Pos + ImVec2(margin + h, margin + h); + const ImVec2 circle_top_right = window->Pos + ImVec2(window->Size.y - margin - h, margin + h); +// const ImVec2 circle_botom_left = window->Pos + ImVec2(margin + h, window->Size.x - margin - h); + const ImVec2 circle_botom_right = window->Pos + ImVec2(window->Size.y - margin - h, window->Size.x - margin - h); + const ImVec2 circle_center = window->Pos + (window->Size + ImVec2(margin, margin) )/ 2.f; + const float circle_radius = (window->Size.y - 2.f * margin) / 2.f; - std::string step_label_ = Action::manager().label(i); + // color palette + const ImU32 colorbg = ImGui::GetColorU32(ImGuiCol_FrameBgActive, 0.6f); + const ImU32 colorfg = ImGui::GetColorU32(ImGuiCol_FrameBg, 2.5f); + const ImU32 colorline = ImGui::GetColorU32(ImGuiCol_PlotHistogram); - bool enable = i == Action::manager().current(); - if (ImGui::Selectable( step_label_.c_str(), &enable, ImGuiSelectableFlags_AllowDoubleClick )) { + // + // METRONOME + // + if (Settings::application.timer.mode < 1) { - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - Action::manager().stepTo(i); + // Metronome info + double t = Metronome::manager().tempo(); + double p = Metronome::manager().phase(); + double q = Metronome::manager().quantum(); + uint np = (int) Metronome::manager().peers(); + + // draw background ring + draw_list->AddCircleFilled(circle_center, circle_radius, colorbg, MAX_SEGMENTS); + + // draw quarter + static const float resolution = MAX_SEGMENTS / (2.f * M_PI); + static ImVec2 buffer[MAX_SEGMENTS]; + float a0 = -M_PI_2 + (floor(p)/floor(q)) * (2.f * M_PI); + float a1 = a0 + (1.f / floor(q)) * (2.f * M_PI); + int n = ImMax(3, (int)((a1 - a0) * resolution)); + double da = (a1 - a0) / (n - 1); + int index = 0; + buffer[index++] = circle_center; + for (int i = 0; i < n; ++i) { + double a = a0 + i * da; + buffer[index++] = ImVec2(circle_center.x + circle_radius * cos(a), circle_center.y + circle_radius * sin(a)); + } + draw_list->AddConvexPolyFilled(buffer, index, colorfg); + + // draw clock hand + a0 = -M_PI_2 + (p/q) * (2.f * M_PI); + draw_list->AddLine(ImVec2(circle_center.x + margin * cos(a0), circle_center.y + margin * sin(a0)), + ImVec2(circle_center.x + circle_radius * cos(a0), circle_center.y + circle_radius * sin(a0)), colorline, 2.f); + + // centered indicator 'x / N' + draw_list->AddCircleFilled(circle_center, margin, colorfg, MAX_SEGMENTS); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); + char text_buf[24]; + sprintf(text_buf, "%d/%d", (int)(p)+1, (int)(q) ); + ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL); + ImGui::SetCursorScreenPos(circle_center - label_size/2); + ImGui::Text("%s", text_buf); + ImGui::PopFont(); + + // left slider : quantum + float float_value = ceil(Metronome::manager().quantum()); + ImGui::SetCursorScreenPos(timer_window_pos + ImVec2(0.5f * margin, 1.5f * margin)); + if ( ImGui::VSliderFloat("##quantum", ImVec2(0.5f * margin, 2.f * circle_radius ), &float_value, 2, 200, "", 2.f) ){ + Metronome::manager().setQuantum( ceil(float_value) ); + } + if (ImGui::IsItemHovered() || ImGui::IsItemActive() ) { + ImGui::BeginTooltip(); + 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::EndTooltip(); + } + + // Controls NOT operational if peer connected + if (np >0 ) { + // Tempo + ImGui::SetCursorScreenPos(circle_top_right); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD); + ImGui::PushStyleColor(ImGuiCol_Text, colorfg); + sprintf(text_buf, "%d", (int) ceil(t) ); + ImGui::Text("%s", text_buf); + ImGui::PopStyleColor(); + ImGui::PopFont(); + if (ImGui::IsItemHovered()){ + sprintf(text_buf, "%d BPM\n(set by peer)", (int) ceil(t)); + ImGuiToolkit::ToolTip(text_buf); } } - ImGui::ListBoxFooter(); + // Controls operational only if no peer + else { + // Tempo + ImGui::SetCursorScreenPos(circle_top_right); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD); + sprintf(text_buf, "%d", (int) ceil(t) ); + ImGui::Text("%s", text_buf); + ImGui::PopFont(); + if (ImGui::IsItemClicked()) + ImGui::OpenPopup("bpm_popup"); + else if (ImGui::IsItemHovered()){ + sprintf(text_buf, "%d BPM\n(clic to edit)", (int) ceil(t)); + ImGuiToolkit::ToolTip(text_buf); + } + if (ImGui::BeginPopup("bpm_popup", ImGuiWindowFlags_NoMove)) + { + ImGui::SetNextItemWidth(80); + ImGui::InputText("BPM", text_buf, 8, ImGuiInputTextFlags_CharsDecimal); + if (ImGui::IsItemDeactivatedAfterEdit()) { + int t = 0; + sscanf(text_buf, "%d", &t); + t = CLAMP(t, 20, 2000); + Metronome::manager().setTempo((double) t); + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + // restart icon + ImGui::SetCursorScreenPos(circle_top_left); + if (ImGuiToolkit::IconButton(9, 13)) { + Metronome::manager().restart(); + } + } + + // Network indicator, if link enabled + if (Settings::application.timer.link_enabled) { + ImGui::SetCursorScreenPos(circle_botom_right); + ImGuiToolkit::Icon(16, 5, np > 0); + if (ImGui::IsItemHovered()){ + sprintf(text_buf, np < 1 ? "Ableton Link\nNo peer" : "Ableton Link\n%d peer%c", np, np < 2 ? ' ' : 's' ); + ImGuiToolkit::ToolTip(text_buf); + } + } + + } + // + // STOPWATCH + // + else { + // clock times + static guint64 start_time_ = gst_util_get_timestamp (); + static guint64 start_time_hand_ = gst_util_get_timestamp (); + static guint64 duration_hand_ = Settings::application.timer.stopwatch_duration * GST_SECOND; + guint64 time_ = gst_util_get_timestamp (); + + // draw ring + draw_list->AddCircle(circle_center, circle_radius, colorbg, MAX_SEGMENTS, 12 ); + draw_list->AddCircleFilled(ImVec2(circle_center.x, circle_center.y - circle_radius), 7, colorfg, MAX_SEGMENTS); + // draw indicator time hand + double da = -M_PI_2 + ( (double) (time_-start_time_hand_) / (double) duration_hand_) * (2.f * M_PI); + draw_list->AddCircleFilled(ImVec2(circle_center.x + circle_radius * cos(da), circle_center.y + circle_radius * sin(da)), 7, colorline, MAX_SEGMENTS); + + // left slider : countdown + float float_value = (float) Settings::application.timer.stopwatch_duration; + ImGui::SetCursorScreenPos(timer_window_pos + ImVec2(0.5f * margin, 1.5f * margin)); + if ( ImGui::VSliderFloat("##duration", ImVec2(0.5f * margin, 2.f * circle_radius ), &float_value, 1, 3600, "", 3.f) ){ + Settings::application.timer.stopwatch_duration = (uint64_t) float_value; + duration_hand_ = Settings::application.timer.stopwatch_duration * GST_SECOND; + } + if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { + ImGui::BeginTooltip(); + ImGui::Text("Countdown\n%s", GstToolkit::time_to_string(duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() ); + ImGui::EndTooltip(); + } + + // main text: elapsed time + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); + char text_buf[24]; + sprintf(text_buf, "%s", GstToolkit::time_to_string(time_-start_time_, GstToolkit::TIME_STRING_FIXED).c_str() ); + ImVec2 label_size = ImGui::CalcTextSize(text_buf, NULL); + ImGui::SetCursorScreenPos(circle_center - label_size/2); + ImGui::Text("%s", text_buf); + ImGui::PopFont(); + + // small text: remaining time + ImGui::PushStyleColor(ImGuiCol_Text, colorfg); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD); + sprintf(text_buf, "%s", GstToolkit::time_to_string(duration_hand_-(time_-start_time_hand_)%duration_hand_, GstToolkit::TIME_STRING_READABLE).c_str() ); + label_size = ImGui::CalcTextSize(text_buf, NULL); + ImGui::SetCursorScreenPos(circle_center + ImVec2(0.f, circle_radius * -0.7f) - label_size/2); + ImGui::Text("%s", text_buf); + ImGui::PopFont(); + ImGui::PopStyleColor(); + + // reset icon + ImGui::SetCursorScreenPos(circle_top_left); + if (ImGuiToolkit::IconButton(8, 13)) + start_time_ = start_time_hand_ = time_; // reset timers + + // TODO : pause ? } ImGui::End(); @@ -1014,15 +1238,6 @@ void UserInterface::RenderPreview() // recording location static DialogToolkit::OpenFolderDialog recordFolderDialog("Recording Location"); - // Helper functions for aspect-ratio constraints - struct CustomConstraints - { - static void AspectRatio(ImGuiSizeCallbackData* data) { - float *ar = (float*) data->UserData; - data->DesiredSize.y = (data->CurrentSize.x / (*ar)) + 35.f; - } - }; - FrameBuffer *output = Mixer::manager().session()->frame(); if (output) { @@ -1562,9 +1777,6 @@ void UserInterface::RenderShaderEditor() void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode) { - static guint64 start_time_1_ = gst_util_get_timestamp (); - static guint64 start_time_2_ = gst_util_get_timestamp (); - if (!p_corner || !p_open) return; @@ -1589,53 +1801,15 @@ void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode) ImGui::SetNextItemWidth(200); ImGui::Combo("##mode", p_mode, ICON_FA_TACHOMETER_ALT " Performance\0" - ICON_FA_HOURGLASS_HALF " Timers\0" - ICON_FA_VECTOR_SQUARE " Source\0" - ICON_FA_USER_CLOCK " Metronome\0"); + ICON_FA_HOURGLASS_HALF " Runtime\0" + ICON_FA_VECTOR_SQUARE " Source\0"); ImGui::SameLine(); if (ImGuiToolkit::IconButton(5,8)) ImGui::OpenPopup("metrics_menu"); ImGui::Spacing(); - if (*p_mode > 2) { - // get values - double t = Metronome::manager().tempo(); - double p = Metronome::manager().phase(); - double q = Metronome::manager().quantum(); - uint n = (int) Metronome::manager().peers(); - - ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); - - // tempo - char buf[32]; - ImGui::Text("Tempo %.1f BPM ", t); - // network peers indicator - ImGui::SameLine(); - if ( n < 1) { - ImGui::TextDisabled( ICON_FA_NETWORK_WIRED ); - if (ImGui::IsItemHovered()){ - sprintf(buf, "No peer linked"); - ImGuiToolkit::ToolTip(buf); - } - } - else { - ImGui::Text( ICON_FA_NETWORK_WIRED ); - if (ImGui::IsItemHovered()){ - sprintf(buf, "%d peer%c linked", n, n < 2 ? ' ' : 's'); - ImGuiToolkit::ToolTip(buf); - } - } - // compute and display duration of a phase - guint64 time_phase = GST_SECOND * (60.0 * q / t) ; - ImGui::Text("Phase %s", GstToolkit::time_to_string(time_phase, GstToolkit::TIME_STRING_READABLE).c_str()); - // metronome - sprintf(buf, "%d/%d", (int)(p)+1, (int)(q) ); - ImGui::ProgressBar(ceil(p)/ceil(q), ImVec2(250.f,0.f), buf); - - ImGui::PopFont(); - } - else if (*p_mode > 1) { + if (*p_mode > 1) { ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); Source *s = Mixer::manager().currentSource(); if (s) { @@ -1690,24 +1864,13 @@ void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode) ImGui::PopFont(); } else if (*p_mode > 0) { - guint64 time_ = gst_util_get_timestamp (); - - ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); - ImGui::Text("%s", GstToolkit::time_to_string(time_-start_time_1_, GstToolkit::TIME_STRING_FIXED).c_str()); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); + ImGui::Text("Session %s", GstToolkit::time_to_string(Mixer::manager().session()->runtime(), GstToolkit::TIME_STRING_READABLE).c_str()); + uint64_t time = Runtime(); + ImGui::Text("Program %s", GstToolkit::time_to_string(time, GstToolkit::TIME_STRING_READABLE).c_str()); + time += Settings::application.total_runtime; + ImGui::Text("Total %s", GstToolkit::time_to_string(time, GstToolkit::TIME_STRING_READABLE).c_str()); ImGui::PopFont(); - ImGui::SameLine(0, 10); - ImGui::PushID( "timermetric1" ); - if (ImGuiToolkit::IconButton(12, 14)) - start_time_1_ = time_; // reset timer 1 - ImGui::PopID(); - ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); - ImGui::Text("%s", GstToolkit::time_to_string(time_-start_time_2_, GstToolkit::TIME_STRING_FIXED).c_str()); - ImGui::PopFont(); - ImGui::SameLine(0, 10); - ImGui::PushID( "timermetric2" ); - if (ImGuiToolkit::IconButton(12, 14)) - start_time_2_ = time_; // reset timer 2 - ImGui::PopID(); } else { ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO); @@ -2089,7 +2252,7 @@ void HelperToolbox::Render() /// SourceController::SourceController() : focused_(false), min_width_(0.f), h_space_(0.f), v_space_(0.f), scrollbar_(0.f), timeline_height_(0.f), mediaplayer_height_(0.f), buttons_width_(0.f), buttons_height_(0.f), - play_toggle_request_(false), replay_request_(false), + play_toggle_request_(false), replay_request_(false), pending_(false), active_label_(LABEL_AUTO_MEDIA_PLAYER), active_selection_(-1), selection_context_menu_(false), selection_mediaplayer_(nullptr), selection_target_slower_(0), selection_target_faster_(0), mediaplayer_active_(nullptr), mediaplayer_edit_fading_(false), mediaplayer_mode_(false), mediaplayer_slider_pressed_(false), mediaplayer_timeline_zoom_(1.f) @@ -3081,7 +3244,7 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp) ImGui::PopFont(); - ImVec2 scrollwindow = ImVec2(ImGui::GetContentRegionAvail().x - slider_zoom_width - 3.0, + const ImVec2 scrollwindow = ImVec2(ImGui::GetContentRegionAvail().x - slider_zoom_width - 3.0, 2.f * timeline_height_ + scrollbar_ ); /// @@ -3267,6 +3430,11 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp) // restore buttons style ImGui::PopStyleColor(5); + if (mediaplayer_active_->pending()) + { + draw_list->AddRectFilled(bottom, bottom + ImVec2(rendersize.x, buttons_height_), ImGui::GetColorU32(ImGuiCol_ScrollbarBg), h_space_); + } + /// /// media player timeline actions /// @@ -3283,11 +3451,12 @@ void SourceController::RenderMediaPlayer(MediaPlayer *mp) if ( mediaplayer_active_->isPlaying() != media_play ) { mediaplayer_active_->play( media_play ); } + } else { const ImGuiContext& g = *GImGui; - const double width_ratio = static_cast(scrollwindow.x + g.Style.FramePadding.x ) / static_cast(mediaplayer_active_->timeline()->sectionsDuration()); + const double width_ratio = static_cast(scrollwindow.x - slider_zoom_width + g.Style.FramePadding.x ) / static_cast(mediaplayer_active_->timeline()->sectionsDuration()); DrawTimeline("##timeline_mediaplayers", mediaplayer_active_->timeline(), mediaplayer_active_->position(), width_ratio, 2.f * timeline_height_); /// @@ -4716,6 +4885,15 @@ void Navigator::RenderMainPannelVimix() if (ImGui::IsItemHovered()) tooltip_ = "Output " CTRL_MOD "D"; + ImGui::SameLine(0, 40); + if ( ImGuiToolkit::IconButton( ICON_FA_CLOCK ) ) { + Settings::application.widget.timer = true; + if (Settings::application.widget.timer_view != Settings::application.current_view) + Settings::application.widget.timer_view = -1; + } + if (ImGui::IsItemHovered()) + tooltip_ = "Timer " CTRL_MOD "T"; + ImGui::PopFont(); if (!tooltip_.empty()) { ImGuiToolkit::ToolTip(tooltip_.substr(0, tooltip_.size()-12).c_str(), tooltip_.substr(tooltip_.size()-12, 12).c_str()); @@ -4753,47 +4931,11 @@ void Navigator::RenderMainPannelSettings() ImGuiToolkit::ButtonSwitch( ICON_FA_MOUSE_POINTER " Smooth cursor", &Settings::application.smooth_cursor); ImGuiToolkit::ButtonSwitch( ICON_FA_TACHOMETER_ALT " Metrics", &Settings::application.widget.stats); - // - // Metronome - // - ImGuiToolkit::Spacing(); - ImGui::Text("Ableton link"); - - ImGuiToolkit::HelpMarker("Time synchronization between computers with Ableton link\n " - ICON_FA_ANGLE_RIGHT " Tempo is the number of beats per minute (or set by peers).\n " - ICON_FA_ANGLE_RIGHT " Quantum is the number of beats in a phase."); - ImGui::SameLine(0); - ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN); - ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - int t = (int) ceil(Metronome::manager().tempo()); - // if no other peers, user can set a tempo - if (Metronome::manager().peers() < 1) { - if ( ImGui::SliderInt("Tempo", &t, 20, 240, "%d BPM") ) - Metronome::manager().setTempo((double) t); - } - // if there are other peers, tempo cannot be changed - else { - // show static info (same size than slider) - static char dummy_str[64]; - sprintf(dummy_str, "%d BPM", t); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f)); - ImGui::InputText("Tempo", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly); - ImGui::PopStyleColor(1); - } - - ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN); - ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - int q = (int) ceil(Metronome::manager().quantum()); - if ( ImGui::SliderInt("Quantum", &q, 2, 100) ) - Metronome::manager().setQuantum((double) q); - -// ImGuiToolkit::ButtonSwitch( ICON_FA_USER_CLOCK " Start/stop sync", &Settings::application.metronome.start_stop_sync); - #ifndef NDEBUG ImGui::Text("Expert"); // ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_HISTORY, &Settings::application.widget.history); ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_SHADEREDITOR, &Settings::application.widget.shader_editor, CTRL_MOD "E"); - ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_TOOLBOX, &Settings::application.widget.toolbox, CTRL_MOD "T"); + ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_TOOLBOX, &Settings::application.widget.toolbox, CTRL_MOD "G"); ImGuiToolkit::ButtonSwitch( IMGUI_TITLE_LOGS, &Settings::application.widget.logs, CTRL_MOD "L"); #endif // @@ -5118,6 +5260,7 @@ void Thumbnail::Render(float width) #define SEGMENT_ARRAY_MAX 1000 #define MAXSIZE 65535 + void ShowSandbox(bool* p_open) { ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver); diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index 94486c2..df1051c 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -131,6 +131,7 @@ class SourceController float buttons_height_; bool play_toggle_request_, replay_request_; + bool pending_; std::string active_label_; int active_selection_; InfoVisitor info_; @@ -185,6 +186,7 @@ class UserInterface SourceController sourcecontrol; HelperToolbox sessiontoolbox; + uint64_t start_time; bool ctrl_modifier_active; bool alt_modifier_active; bool shift_modifier_active; @@ -230,6 +232,8 @@ public: void Render(); // Post-loop termination void Terminate(); + // Runtime + uint64_t Runtime() const; // status querries inline bool ctrlModifier() const { return ctrl_modifier_active; } @@ -255,7 +259,7 @@ protected: void RenderMetrics (bool* p_open, int* p_corner, int *p_mode); void RenderPreview(); - void RenderHistory(); + void RenderTimer(); void RenderShaderEditor(); int RenderViewNavigator(int* shift); void RenderAbout(bool* p_open); diff --git a/defines.h b/defines.h index 0530fa9..1fe2450 100644 --- a/defines.h +++ b/defines.h @@ -64,10 +64,10 @@ #define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix" #define IMGUI_TITLE_MEDIAPLAYER ICON_FA_PLAY_CIRCLE " Player" -#define IMGUI_TITLE_HISTORY ICON_FA_HISTORY " History" +#define IMGUI_TITLE_TIMER ICON_FA_CLOCK " Timer" #define IMGUI_TITLE_LOGS ICON_FA_LIST " Logs" #define IMGUI_TITLE_HELP ICON_FA_LIFE_RING " Help" -#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Toolbox" +#define IMGUI_TITLE_TOOLBOX ICON_FA_HAMSA " Guru Toolbox" #define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code Editor" #define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput" #define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?" diff --git a/main.cpp b/main.cpp index ce5d835..730e550 100644 --- a/main.cpp +++ b/main.cpp @@ -85,13 +85,13 @@ int main(int argc, char *argv[]) /// lock to inform an instance is running Settings::Lock(); - /// /// /// CONNECTION INIT /// if ( !Connection::manager().init() ) return 1; + /// /// METRONOME INIT /// if ( !Metronome::manager().init() ) @@ -162,7 +162,7 @@ int main(int argc, char *argv[]) /// /// Settings /// - Settings::Save(); + Settings::Save(UserInterface::manager().Runtime()); /// ok return 0;