mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Metronome and Stopwatch User Interface
New Timer window in UI for Metronome (Ableton Link management) and replaces Timers. Former Timers in Metrics are replaced with Runtime (of session, of program and of total vimix runtime in settings). Temporarily disconnected Metronome from MediaPlayer actions.
This commit is contained in:
112
MediaPlayer.cpp
112
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)
|
||||
|
||||
@@ -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<bool> opened_;
|
||||
std::atomic<bool> 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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
@@ -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<void()> f, std::chrono::microseconds us)
|
||||
{
|
||||
std::this_thread::sleep_for(us);
|
||||
f();
|
||||
}
|
||||
|
||||
void Metronome::executeAtBeat( std::function<void()> f )
|
||||
{
|
||||
std::thread( delay, f, timeToBeat() ).detach();
|
||||
}
|
||||
|
||||
size_t Metronome::peers() const
|
||||
{
|
||||
return link_.numPeers();
|
||||
|
||||
28
Metronome.h
28
Metronome.h
@@ -2,6 +2,8 @@
|
||||
#define METRONOME_H
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
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<void()> 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
|
||||
|
||||
39
Settings.cpp
39
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)) {
|
||||
|
||||
37
Settings.h
37
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();
|
||||
|
||||
@@ -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<double>(scrollwindow.x + g.Style.FramePadding.x ) / static_cast<double>(mediaplayer_active_->timeline()->sectionsDuration());
|
||||
const double width_ratio = static_cast<double>(scrollwindow.x - slider_zoom_width + g.Style.FramePadding.x ) / static_cast<double>(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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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?"
|
||||
|
||||
4
main.cpp
4
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;
|
||||
|
||||
Reference in New Issue
Block a user