/* * Copyright (C) 2018 Robin Gareus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "gtkmm2ext/utils.h" #include "ardour/session.h" #include "ardour/audioengine.h" #include "ardour/audio_backend.h" #include "widgets/tooltips.h" #include "ardour_ui.h" #include "dsp_stats_ui.h" #include "timers.h" #include "utils.h" #include "pbd/i18n.h" using namespace ARDOUR; using namespace Gtkmm2ext; using namespace Gtk; DspStatisticsGUI::DspStatisticsGUI () : buffer_size_label ("", ALIGN_END, ALIGN_CENTER) , reset_button (_("Reset")) { const size_t nlabels = Session::NTT + AudioEngine::NTT + AudioBackend::NTT; char buf[64]; labels = new Label*[nlabels]; snprintf (buf, sizeof (buf), "%7.2f msec %6.2f%%", 10000.0, 100.0); for (size_t n = 0; n < nlabels; ++n) { labels[n] = new Label ("", ALIGN_END, ALIGN_CENTER); set_size_request_to_display_given_text (*labels[n], buf, 0, 0); } int row = 0; table.attach (*manage (new Gtk::Label (_("Buffer size: "), ALIGN_END, ALIGN_CENTER)), 0, 1, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (buffer_size_label, 2, 3, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); row++; table.attach (*manage (new Gtk::Label (_("Idle: "), ALIGN_END, ALIGN_CENTER)), 0, 1, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (*labels[AudioEngine::NTT + Session::NTT + AudioBackend::DeviceWait], 2, 3, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); row++; table.attach (*manage (new Gtk::Label (_("DSP: "), ALIGN_END, ALIGN_CENTER)), 0, 1, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (*labels[AudioEngine::NTT + Session::NTT + AudioBackend::RunLoop], 2, 3, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); row++; Label* right_angle_text1 = manage (new Label ("\xe2\x94\x94", ALIGN_END, ALIGN_CENTER)); table.attach (*manage (new Gtk::Label (_("Engine: "), ALIGN_END, ALIGN_CENTER)), 1, 2, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (*right_angle_text1, 0, 1, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (*labels[AudioEngine::ProcessCallback], 2, 3, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); row++; Label* right_angle_text2 = manage (new Label ("\xe2\x94\x94", ALIGN_END, ALIGN_CENTER)); table.attach (*manage (new Gtk::Label (_("Session: "), ALIGN_END, ALIGN_CENTER)), 1, 2, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (*right_angle_text2, 0, 1, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); table.attach (*labels[AudioEngine::NTT + Session::OverallProcess], 2, 3, row, row+1, Gtk::FILL, Gtk::SHRINK, 2, 0); row++; HBox* hbox2 = manage (new HBox); hbox2->pack_start (reset_button, true, true); set_border_width (12); set_spacing (6); info_text.set_markup (_("The measurements shown below are worst case.\n" "\n" "This is more important in determining system load\n" "than an average. To see average values, mouse-over\n" "any line:")); Gtk::Alignment *i_align = Gtk::manage (new Gtk::Alignment()); i_align->set_padding (6, 6, 6, 6); i_align->add (info_text); Gtk::Frame* frame = manage (new Gtk::Frame); frame->set_shadow_type (Gtk::SHADOW_IN); frame->add (*i_align); pack_start (*frame, false, false); pack_start (table, true, true, 20); pack_start (*hbox2, false, false); reset_button.signal_clicked().connect (sigc::mem_fun (*this, &DspStatisticsGUI::reset_button_clicked)); show_all (); } void DspStatisticsGUI::reset_button_clicked () { ARDOUR::reset_performance_meters (_session); } void DspStatisticsGUI::start_updating () { update (); update_connection = Timers::second_connect (sigc::mem_fun(*this, &DspStatisticsGUI::update)); } void DspStatisticsGUI::stop_updating () { update_connection.disconnect (); } void DspStatisticsGUI::update () { PBD::microseconds_t min = 0; PBD::microseconds_t max = 0; double avg = 0.; double dev = 0.; char buf[64]; char const * const not_measured_string = X_("--"); double devf; double avgf; /* translatable math and unit terms */ const char * const str_msec = _("msec"); const char * const str_usec = _("usec"); const char * const str_average = _("average"); const char * const str_std_dev = _("std dev"); int bufsize = AudioEngine::instance()->samples_per_cycle (); double bufsize_usecs = (bufsize * 1000000.0) / AudioEngine::instance()->sample_rate(); double bufsize_msecs = (bufsize * 1000.0) / AudioEngine::instance()->sample_rate(); snprintf (buf, sizeof (buf), "%d samples / %5.2f msecs", bufsize, bufsize_msecs); buffer_size_label.set_text (buf); if (AudioEngine::instance()->current_backend()->dsp_stats[AudioBackend::DeviceWait].get_stats (min, max, avg, dev)) { /* We show the min time here, since that's the worst case * (other timers are max == worst case) */ if (min > 1000) { double minf = min / 1000.0; devf = dev / 1000.0; avgf = avg / 1000.0; snprintf (buf, sizeof (buf), "%7.2f %s %5.2f%%", minf, str_msec, (100.0 * minf) / bufsize_msecs); } else { snprintf (buf, sizeof (buf), "%" PRId64 " %s %5.2f%%", min, str_usec, (100.0 * min) / bufsize_usecs); } labels[AudioEngine::NTT + Session::NTT + AudioBackend::DeviceWait]->set_text (buf); if (min > 1000) { snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avgf, str_msec, (100.0 * avgf) / bufsize_msecs, str_std_dev, devf); } else { snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avg, str_usec, (100.0 * avg) / bufsize_usecs, str_std_dev, dev); } ArdourWidgets::set_tooltip (labels[AudioEngine::NTT + Session::NTT + AudioBackend::DeviceWait], buf); } else { labels[AudioEngine::NTT + Session::NTT + AudioBackend::DeviceWait]->set_text (not_measured_string); ArdourWidgets::set_tooltip (labels[AudioEngine::NTT + Session::NTT + AudioBackend::DeviceWait], ""); } if (AudioEngine::instance()->current_backend()->dsp_stats[AudioBackend::RunLoop].get_stats (min, max, avg, dev)) { if (max > 1000) { double maxf = max / 1000.0; snprintf (buf, sizeof (buf), "%7.2f %s %5.2f%%", maxf, str_msec, (100.0 * maxf) / bufsize_msecs); } else { snprintf (buf, sizeof (buf), "%" PRId64 " %s %5.2f%%", max, str_usec, (100.0 * max) / bufsize_usecs); } labels[AudioEngine::NTT + Session::NTT + AudioBackend::RunLoop]->set_text (buf); if (min > 1000) { devf = dev / 1000.0; avgf = avg / 1000.0; snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avgf, str_msec, (100.0 * avgf) / bufsize_msecs, str_std_dev, devf); } else { snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avg, str_usec, (100.0 * avg) / bufsize_usecs, str_std_dev, dev); } ArdourWidgets::set_tooltip (labels[AudioEngine::NTT + Session::NTT + AudioBackend::RunLoop], buf); } else { labels[AudioEngine::NTT + Session::NTT + AudioBackend::RunLoop]->set_text (not_measured_string); ArdourWidgets::set_tooltip (labels[AudioEngine::NTT + Session::NTT + AudioBackend::RunLoop], ""); } AudioEngine::instance()->dsp_stats[AudioEngine::ProcessCallback].get_stats (min, max, avg, dev); if (_session) { PBD::microseconds_t smin = 0; PBD::microseconds_t smax = 0; double savg = 0.; double sdev = 0.; _session->dsp_stats[AudioEngine::ProcessCallback].get_stats (smin, smax, savg, sdev); if (smax > 1000) { double maxf = smax / 1000.0; snprintf (buf, sizeof (buf), "%7.2f %s %5.2f%%", maxf, str_msec, (100.0 * maxf) / bufsize_msecs); } else { snprintf (buf, sizeof (buf), "%" PRId64 " %s %5.2f%%", max, str_usec, (100.0 * max) / bufsize_usecs); } labels[AudioEngine::NTT + Session::OverallProcess]->set_text (buf); if (max > 1000) { devf = dev / 1000.0; avgf = avg / 1000.0; snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avgf, str_msec, (100.0 * avgf) / bufsize_msecs, str_std_dev, devf); } else { snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avg, str_usec, (100.0 * avg) / bufsize_usecs, str_std_dev, dev); } ArdourWidgets::set_tooltip (labels[AudioEngine::NTT + Session::OverallProcess], buf); /* Subtract session time from engine process time to show * engine overhead */ min -= smin; max -= smax; avg -= savg; dev -= sdev; if (max > 1000) { double maxf = max / 1000.0; snprintf (buf, sizeof (buf), "%7.2f %s %5.2f%%", maxf, str_msec, (100.0 * maxf) / bufsize_msecs); } else { snprintf (buf, sizeof (buf), "%" PRId64 " %s %5.2f%%", max, str_usec, (100.0 * max) / bufsize_usecs); } labels[AudioEngine::ProcessCallback]->set_text (buf); if (max > 1000) { devf = dev / 1000.0; avgf = avg / 1000.0; snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avgf, str_msec, (100.0 * avgf) / bufsize_msecs, str_std_dev, devf); } else { snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avg, str_usec, (100.0 * avg) / bufsize_usecs, str_std_dev, dev); } ArdourWidgets::set_tooltip (labels[AudioEngine::ProcessCallback], buf); } else { if (max > 1000) { double maxf = max / 1000.0; snprintf (buf, sizeof (buf), "%7.2f %s %5.2f%%", maxf, str_msec, (100.0 * maxf) / bufsize_msecs); } else { snprintf (buf, sizeof (buf), "%" PRId64 " %s %5.2f%%", max, str_usec, (100.0 * max) / bufsize_usecs); } labels[AudioEngine::ProcessCallback]->set_text (buf); if (max > 1000) { devf = dev / 1000.0; avgf = avg / 1000.0; snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avgf, str_msec, (100.0 * avgf) / bufsize_msecs, str_std_dev, devf); } else { snprintf (buf, sizeof (buf), "%s: %7.2f %s %5.2f%% (%s. %5.2f)", str_average, avg, str_usec, (100.0 * avg) / bufsize_usecs, str_std_dev, dev); } ArdourWidgets::set_tooltip (labels[AudioEngine::ProcessCallback], buf); labels[AudioEngine::NTT + Session::OverallProcess]->set_text (_("No session loaded")); ArdourWidgets::set_tooltip (labels[AudioEngine::NTT + Session::OverallProcess], ""); } } bool DspStatisticsGUI::on_key_press_event (GdkEventKey* ev) { Gtk::Window& main_window (ARDOUR_UI::instance()->main_window()); return ARDOUR_UI_UTILS::relay_key_press (ev, &main_window); }