From 7228144d6e01ae58a31bbbf7f4c3be48bd07a027 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 2 Jan 2024 14:26:56 +0100 Subject: [PATCH] Vapor: GUI support to add Surround Bus --- gtk2_ardour/ardour.menus.in | 3 + gtk2_ardour/editor.cc | 2 +- gtk2_ardour/mixer_strip.cc | 16 +- gtk2_ardour/mixer_ui.cc | 91 ++++++- gtk2_ardour/mixer_ui.h | 9 +- gtk2_ardour/processor_box.cc | 27 +- gtk2_ardour/processor_box.h | 1 + gtk2_ardour/route_list_base.cc | 3 + gtk2_ardour/route_time_axis.cc | 18 +- gtk2_ardour/route_ui.cc | 6 + gtk2_ardour/route_ui.h | 1 + gtk2_ardour/surround_strip.cc | 466 +++++++++++++++++++++++++++++++++ gtk2_ardour/surround_strip.h | 101 +++++++ gtk2_ardour/wscript | 6 +- 14 files changed, 726 insertions(+), 24 deletions(-) create mode 100644 gtk2_ardour/surround_strip.cc create mode 100644 gtk2_ardour/surround_strip.h diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index aa4d789fd8..eca9813183 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -37,6 +37,9 @@ +#ifdef VAPOR + +#endif diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index abc42610b1..21ff1bc191 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -5962,7 +5962,7 @@ Editor::add_stripables (StripableList& sl) } else if ((r = std::dynamic_pointer_cast (*s)) != 0) { - if (r->is_auditioner() || r->is_monitor()) { + if (r->is_auditioner() || r->is_monitor() || r->is_surround_master ()) { continue; } diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 15bf275658..819529ff9d 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -617,6 +617,10 @@ MixerStrip::set_route (std::shared_ptr rt) monitor_section_button->set_can_focus (false); monitor_section_added_or_removed (); } + } else if (route()->is_surround_master()) { + mute_solo_table.attach (*mute_button, 0, 2, 0, 1); + mute_button->show (); + master_volume_table.hide (); } else { bottom_button_table.attach (group_button, 1, 2, 0, 1); mute_solo_table.attach (*mute_button, 0, 1, 0, 1); @@ -761,7 +765,7 @@ MixerStrip::set_route (std::shared_ptr rt) connect_to_pan (); panners.setup_pan (); - if (has_audio_outputs ()) { + if (has_audio_outputs () && !_route->is_surround_master ()) { panners.show_all (); } else { panners.hide_all (); @@ -945,7 +949,7 @@ MixerStrip::update_input_display () { panners.setup_pan (); - if (has_audio_outputs ()) { + if (has_audio_outputs () && !_route->is_surround_master ()) { panners.show_all (); } else { panners.hide_all (); @@ -959,7 +963,7 @@ MixerStrip::update_output_display () gpm.setup_meters (); panners.setup_pan (); - if (has_audio_outputs ()) { + if (has_audio_outputs () && !_route->is_surround_master ()) { panners.show_all (); } else { panners.hide_all (); @@ -1677,7 +1681,7 @@ MixerStrip::revert_to_default_display () panner_ui().setup_pan (); panner_ui().set_send_drawing_mode (false); - if (has_audio_outputs ()) { + if (has_audio_outputs () && !_route->is_surround_master ()) { panners.show_all (); } else { panners.hide_all (); @@ -1895,7 +1899,7 @@ MixerStrip::update_sensitivity () bool send = _current_delivery && std::dynamic_pointer_cast(_current_delivery) != 0; bool aux = _current_delivery && std::dynamic_pointer_cast(_current_delivery) != 0; - if (route()->is_master()) { + if (route()->is_main_bus ()) { solo_iso_table.set_sensitive (false); control_slave_ui.set_sensitive (false); } else { @@ -2134,7 +2138,7 @@ MixerStrip::set_marked_for_display (bool yn) void MixerStrip::hide_master_spacer (bool yn) { - if (_mixer_owned && route()->is_master() && !yn) { + if (_mixer_owned && (route()->is_master() || route()->is_surround_master ()) && !yn) { spacer.show(); } else { spacer.hide(); diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 3f7f1069be..7ddad90d69 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -90,6 +90,7 @@ #include "mixer_group_tabs.h" #include "plugin_utils.h" #include "route_sorter.h" +#include "surround_strip.h" #include "timers.h" #include "ui_config.h" #include "vca_master_strip.h" @@ -131,6 +132,7 @@ Mixer_UI::Mixer_UI () , in_group_row_change (false) , track_menu (0) , _plugin_selector (0) + , _surround_strip (0) , foldback_strip (0) , _show_foldback_strip (true) , _strip_width (UIConfiguration::instance().get_default_narrow_ms() ? Narrow : Wide) @@ -421,6 +423,7 @@ Mixer_UI::Mixer_UI () MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context()); VCAMasterStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_master, this, _1), gui_context()); FoldbackStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_foldback, this, _1), gui_context()); + SurroundStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_surround_master, this, _1), gui_context()); /* handle escape */ @@ -443,6 +446,7 @@ Mixer_UI::~Mixer_UI () { monitor_section_detached (); + delete _surround_strip; delete foldback_strip; foldback_strip = 0; delete _plugin_selector; @@ -677,6 +681,14 @@ Mixer_UI::add_stripables (StripableList& slist) continue; } + if (route->is_surround_master ()) { + if (!_surround_strip) { + _surround_strip = new SurroundStrip (*this, _session, route); + } + out_packer.pack_start (*_surround_strip, false, false); + continue; + } + strip = new MixerStrip (*this, _session, route); strip->set_selected (route->is_selected ()); strips.push_back (strip); @@ -690,8 +702,7 @@ Mixer_UI::add_stripables (StripableList& slist) show_strip (strip); if (route->is_master()) { - - out_packer.pack_start (*strip, false, false); + out_packer.pack_end (*strip, false, false); strip->set_packed (true); } else { @@ -802,6 +813,21 @@ Mixer_UI::remove_strip (MixerStrip* strip) } } +void +Mixer_UI::remove_surround_master (SurroundStrip* strip) +{ + if (_session && _session->deletion_in_progress()) { + /* its all being taken care of */ + return; + } + assert (strip == _surround_strip); + out_packer.remove (*_surround_strip); + _surround_strip = 0; + + RefPtr surround_action = ActionManager::get_toggle_action (X_("Mixer"), "ToggleSurroundMaster"); + surround_action->set_active (false); +} + void Mixer_UI::remove_foldback (FoldbackStrip* strip) { @@ -867,6 +893,10 @@ Mixer_UI::sync_presentation_info_from_treeview () assert (0); continue; } + if (stripable->is_surround_master()) { + assert (0); + continue; + } if (stripable->is_master()) { assert (0); continue; @@ -1251,7 +1281,10 @@ Mixer_UI::set_session (Session* sess) update_scene_buttons(); + RefPtr surround_action = ActionManager::get_toggle_action (X_("Mixer"), "ToggleSurroundMaster"); + if (!_session) { + surround_action->set_sensitive (false); PBD::Unwinder uw (ignore_plugin_reorder, true); favorite_plugins_model->clear (); _selection.clear (); @@ -1265,6 +1298,9 @@ Mixer_UI::set_session (Session* sess) update_title (); + surround_action->set_sensitive (_session->vapor_barrier ()); + surround_action->set_active (nullptr != _session->surround_master()); + #if 0 /* skip mapping all session-config vars, we only need one */ boost::function pc (boost::bind (&Mixer_UI::parameter_changed, this, _1)); @@ -1446,6 +1482,9 @@ Mixer_UI::fast_update_strips () if (foldback_strip) { foldback_strip->fast_update (); } + if (_surround_strip) { + _surround_strip->fast_update (); + } } } @@ -3679,6 +3718,12 @@ Mixer_UI::register_actions () ActionManager::register_toggle_action (group, X_("ToggleMonitorSection"), _("Mixer: Show Monitor Section"), sigc::mem_fun (*this, &Mixer_UI::toggle_monitor_section)); +#ifdef MIXBUS + ActionManager::register_toggle_action (group, X_("ToggleSurroundMaster"), _("Atmos Surround Master"), sigc::mem_fun (*this, &Mixer_UI::toggle_surround_master)); +#else + ActionManager::register_toggle_action (group, X_("ToggleSurroundMaster"), _("Surround Master"), sigc::mem_fun (*this, &Mixer_UI::toggle_surround_master)); +#endif + ActionManager::register_toggle_action (group, X_("ToggleFoldbackStrip"), _("Mixer: Show Foldback Strip"), sigc::mem_fun (*this, &Mixer_UI::toggle_foldback_strip)); ActionManager::register_toggle_action (group, X_("toggle-disk-monitor"), _("Toggle Disk Monitoring"), sigc::bind (sigc::mem_fun (*this, &Mixer_UI::toggle_monitor_action), MonitorDisk, false, false)); @@ -4250,9 +4295,14 @@ Mixer_UI::screenshot (std::string const& filename) vca_scroller_base.hide(); } + if (_surround_strip) { + out_packer.remove (*_surround_strip); + b.pack_start (*_surround_strip, false, false); + _surround_strip->hide_spacer (true); + } if (master) { out_packer.remove (*master); - b.pack_start (*master, false, false); + b.pack_end (*master, false, false); master->hide_master_spacer (true); } @@ -4284,9 +4334,13 @@ Mixer_UI::screenshot (std::string const& filename) vca_scroller_base.show(); vca_scroller.add (vca_hpacker); } + if (_surround_strip) { + _surround_strip->hide_spacer (false); + out_packer.pack_start (*_surround_strip, false, false); + } if (master) { master->hide_master_spacer (false); - out_packer.pack_start (*master, false, false); + out_packer.pack_end (*master, false, false); } return true; } @@ -4326,3 +4380,32 @@ Mixer_UI::toggle_monitor_action (MonitorChoice monitor_choice, bool group_overri } } + +void +Mixer_UI::toggle_surround_master () +{ + if (!_session || !_session->vapor_barrier ()) { + return; + } + + RefPtr act = ActionManager::get_toggle_action (X_("Mixer"), "ToggleSurroundMaster"); + bool want_sm = act->get_active(); + bool have_sm = _session->surround_master () != nullptr; + + if (want_sm == have_sm) { + return; + } + + if (want_sm) { + _session->config.set_use_surround_master (true); + } else { + ArdourMessageDialog md (_("Disabling surround master will delete all existing surround panner state.\nThis cannot be undonoe. Proceed anyway?"), false, MESSAGE_QUESTION, BUTTONS_YES_NO); + if (md.run () == RESPONSE_YES) { + _session->config.set_use_surround_master (false); + } + } + + have_sm = _session->surround_master () != nullptr; + + act->set_active (have_sm); +} diff --git a/gtk2_ardour/mixer_ui.h b/gtk2_ardour/mixer_ui.h index d9967a8ba5..f8c97bb3d3 100644 --- a/gtk2_ardour/mixer_ui.h +++ b/gtk2_ardour/mixer_ui.h @@ -72,6 +72,7 @@ class MixerStrip; class PluginSelector; class MixerGroupTabs; class MonitorSection; +class SurroundStrip; class VCAMasterStrip; class PluginTreeStore : public Gtk::TreeStore @@ -148,6 +149,8 @@ public: void toggle_mixer_list (); void showhide_mixer_list (bool yn); + void toggle_surround_master (); + void toggle_monitor_section (); void showhide_monitor_section (bool); @@ -248,6 +251,7 @@ private: void add_routes (ARDOUR::RouteList&); void remove_strip (MixerStrip *); void remove_foldback (FoldbackStrip *); + void remove_surround_master (SurroundStrip*); void add_masters (ARDOUR::VCAList&); void remove_master (VCAMasterStrip*); void new_masters_created (); @@ -332,8 +336,9 @@ private: void build_track_menu (); MonitorSection _monitor_section; - PluginSelector *_plugin_selector; - FoldbackStrip * foldback_strip; + PluginSelector* _plugin_selector; + SurroundStrip* _surround_strip; + FoldbackStrip* foldback_strip; bool _show_foldback_strip; void stripable_property_changed (const PBD::PropertyChange& what_changed, std::weak_ptr ws); diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 32f0c3c8fe..b69657f4cc 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -73,6 +73,7 @@ #include "ardour/route.h" #include "ardour/send.h" #include "ardour/session.h" +#include "ardour/surround_send.h" #include "ardour/types.h" #include "ardour/value_as_string.h" @@ -2412,6 +2413,7 @@ ProcessorBox::show_processor_menu (int arg) } } + ActionManager::get_action (X_("ProcessorMenu"), "newplugin")->set_sensitive (!_route->is_surround_master ()); ActionManager::get_action (X_("ProcessorMenu"), "newinsert")->set_sensitive (!_route->is_monitor () && !_route->is_foldbackbus () && !_route->is_surround_master ()); ActionManager::get_action (X_("ProcessorMenu"), "newsend")->set_sensitive (!_route->is_monitor () && !_route->is_foldbackbus () && !_route->is_surround_master ()); @@ -2476,7 +2478,7 @@ ProcessorBox::show_processor_menu (int arg) /* Sensitise actions as approprioate */ - const bool sensitive = !processor_display.selection().empty() && ! stub_processor_selected () && !channelstrip_selected(); + const bool sensitive = !processor_display.selection().empty() && ! stub_processor_selected () && !channelstrip_selected() && !surrsend_selected (); paste_action->set_sensitive (!_p_selection.processors.empty()); cut_action->set_sensitive (sensitive && can_cut ()); @@ -2522,6 +2524,7 @@ ProcessorBox::show_processor_menu (int arg) /* aux-send names are kept in sync with the target bus name */ && !std::dynamic_pointer_cast (single_selection->processor ()) && !std::dynamic_pointer_cast (single_selection->processor ()) + && !std::dynamic_pointer_cast (single_selection->processor ()) && !std::dynamic_pointer_cast (single_selection->processor ())); processor_menu->popup (3, arg); @@ -3152,13 +3155,14 @@ ProcessorBox::add_processor_to_display (std::weak_ptr p) std::shared_ptr bb = std::dynamic_pointer_cast (processor); #endif std::shared_ptr stub = std::dynamic_pointer_cast (processor); + std::shared_ptr sursend = std::dynamic_pointer_cast (processor); //faders and meters are not deletable, copy/paste-able, so they shouldn't be selectable #ifdef HAVE_BEATBOX - if (!send && !plugin_insert && !ext && !stub && !bb && !tb) { + if (!send && !plugin_insert && !ext && !stub && !bb && !tb && !sursend) { #else - if (!send && !plugin_insert && !ext && !stub && !tb) { + if (!send && !plugin_insert && !ext && !stub && !tb && !sursend) { #endif e->set_selectable(false); } @@ -3445,6 +3449,22 @@ ProcessorBox::stub_processor_selected () const return false; } +bool +ProcessorBox::surrsend_selected () const +{ + vector > sel; + + get_selected_processors (sel); + + for (vector >::const_iterator i = sel.begin (); i != sel.end (); ++i) { + if (std::dynamic_pointer_cast((*i)) != 0) { + return true; + } + } + + return false; +} + bool ProcessorBox::channelstrip_selected () const { @@ -3679,6 +3699,7 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, std::shared_ptr

value() == "meter" || type->value() == "main-outs" || type->value() == "amp" || + type->value() == "sursend" || type->value() == "intreturn") { /* do not paste meter, main outs, amp or internal returns */ continue; diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 876a9ccc2f..e5d0020bb3 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -584,6 +584,7 @@ private: bool can_cut() const; bool stub_processor_selected() const; bool channelstrip_selected() const; + bool surrsend_selected() const; static Glib::RefPtr cut_action; static Glib::RefPtr copy_action; diff --git a/gtk2_ardour/route_list_base.cc b/gtk2_ardour/route_list_base.cc index da879fabae..4b8186f82c 100644 --- a/gtk2_ardour/route_list_base.cc +++ b/gtk2_ardour/route_list_base.cc @@ -579,6 +579,9 @@ RouteListBase::add_stripables (StripableList& slist) if (route->is_monitor ()) { continue; } + if (route->is_surround_master ()) { + continue; + } row = *(_model->insert (insert_iter)); diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 9f5c0407d5..3ed50083a1 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -57,6 +57,7 @@ #include "ardour/profile.h" #include "ardour/route_group.h" #include "ardour/session.h" +#include "ardour/surround_send.h" #include "ardour/track.h" #include "canvas/debug.h" @@ -1933,16 +1934,19 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (std::weak_ptr p) { std::shared_ptr processor (p.lock ()); - if (!processor || !processor->display_to_user ()) { + if (!processor) { return; } - /* we use this override to veto the Amp processor from the plugin menu, - as its automation lane can be accessed using the special "Fader" menu - option - */ - - if (std::dynamic_pointer_cast (processor) != 0) { + if (std::dynamic_pointer_cast (processor) != 0) { + /* OK, show surround send controls */ + } else if (std::dynamic_pointer_cast (processor) != 0) { + /* we use this override to veto the Amp processor from the plugin menu, + * as its automation lane can be accessed using the special "Fader" menu + * option + */ + return; + } else if (!processor->display_to_user ()) { return; } diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 08782b74dc..340f945be3 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -1476,6 +1476,12 @@ RouteUI::build_mute_menu(void) items.push_back (CheckMenuElem(*main_mute_check)); main_mute_check->show_all(); + surround_mute_check = manage (new Gtk::CheckMenuItem(_("Surround Send"))); + init_mute_menu(MuteMaster::SurroundSend, surround_mute_check); + surround_mute_check->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_mute_menu), MuteMaster::SurroundSend, surround_mute_check)); + items.push_back (CheckMenuElem(*surround_mute_check)); + surround_mute_check->show_all(); + _route->mute_points_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::muting_change, this), gui_context()); } diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index 03b3323372..6c738117aa 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -210,6 +210,7 @@ protected: Gtk::CheckMenuItem* post_fader_mute_check; Gtk::CheckMenuItem* listen_mute_check; Gtk::CheckMenuItem* main_mute_check; + Gtk::CheckMenuItem* surround_mute_check; Gtk::CheckMenuItem* solo_safe_check; Gtk::CheckMenuItem* solo_isolated_check; int set_color_from_route (); diff --git a/gtk2_ardour/surround_strip.cc b/gtk2_ardour/surround_strip.cc new file mode 100644 index 0000000000..47b8d9281f --- /dev/null +++ b/gtk2_ardour/surround_strip.cc @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2023 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 "pbd/fastlog.h" + +#include "ardour/logmeter.h" +#include "ardour/meter.h" +#include "ardour/profile.h" +#include "ardour/route.h" +#include "ardour/session.h" +#include "ardour/surround_return.h" +#include "ardour/value_as_string.h" +#include "ardour/vca_manager.h" + +#include "gtkmm2ext/utils.h" + +#include "widgets/tooltips.h" + +#include "ardour_window.h" +#include "surround_strip.h" + +#include "gui_thread.h" +#include "io_selector.h" +#include "keyboard.h" +#include "meter_patterns.h" +#include "mixer_ui.h" +#include "ui_config.h" +#include "utils.h" + +#include "pbd/i18n.h" + +using namespace ARDOUR; +using namespace ArdourWidgets; +using namespace PBD; +using namespace Gtk; + +#define PX_SCALE(px) std::max ((float)px, rintf ((float)px* UIConfiguration::instance ().get_ui_scale ())) + +PBD::Signal1 SurroundStrip::CatchDeletion; + +SurroundStrip::SurroundStrip (Mixer_UI& mx, Session* s, std::shared_ptr r) + : SessionHandlePtr (s) + , RouteUI (s) + , _width (80) + , _output_button (false) + , _comment_button (_("Comments")) + , _level_control (ArdourKnob::default_elements, ArdourKnob::Detent) +{ + init (); + set_route (r); +} + +SurroundStrip::~SurroundStrip () +{ + CatchDeletion (this); + for (int i = 0; i < 14; ++i) { + delete _meter[i]; + } +} + +void +SurroundStrip::init () +{ + _name_button.set_name ("mixer strip button"); + _name_button.set_text_ellipsize (Pango::ELLIPSIZE_END); + _name_button.set_layout_ellipsize_width (PX_SCALE (_width) * PANGO_SCALE); + + _lufs_cap.set_name("OptionsLabel"); + _lufs_cap.set_alignment(1.0, 0.5); + _lufs_cap.set_use_markup(); + _lufs_cap.set_markup ("LUFS:"); + + _lufs_label.set_name("OptionsLabel"); + _lufs_label.set_alignment(0.0, 0.5); + _lufs_label.set_use_markup(); + _lufs_label.set_markup (" --- "); + + _dbtp_cap.set_name("OptionsLabel"); + _dbtp_cap.set_alignment(1.0, 0.5); + _dbtp_cap.set_use_markup(); + _dbtp_cap.set_markup ("dBTP:"); + + _dbtp_label.set_name("OptionsLabel"); + _dbtp_label.set_alignment(0.0, 0.5); + _dbtp_label.set_use_markup(); + _dbtp_label.set_markup (" --- "); + + Gtk::Table *lufs_table = manage(new Gtk::Table()); + lufs_table->set_homogeneous(true); + lufs_table->set_border_width(2); + lufs_table->set_spacings(4); + lufs_table->attach(_lufs_cap, 0, 1, 0, 1, FILL|EXPAND, SHRINK); + lufs_table->attach(_lufs_label, 1, 2, 0, 1, FILL|EXPAND, SHRINK); + lufs_table->attach(_dbtp_cap, 0, 1, 1, 2, FILL|EXPAND, SHRINK); + lufs_table->attach(_dbtp_label, 1, 2, 1, 2, FILL|EXPAND, SHRINK); + + uint32_t c[10]; + uint32_t b[4]; + float stp[4]; + + c[0] = UIConfiguration::instance().color ("meter color0"); + c[1] = UIConfiguration::instance().color ("meter color1"); + c[2] = UIConfiguration::instance().color ("meter color2"); + c[3] = UIConfiguration::instance().color ("meter color3"); + c[4] = UIConfiguration::instance().color ("meter color4"); + c[5] = UIConfiguration::instance().color ("meter color5"); + c[6] = UIConfiguration::instance().color ("meter color6"); + c[7] = UIConfiguration::instance().color ("meter color7"); + c[8] = UIConfiguration::instance().color ("meter color8"); + c[9] = UIConfiguration::instance().color ("meter color9"); + b[0] = UIConfiguration::instance().color ("meter background bottom"); + b[1] = UIConfiguration::instance().color ("meter background top"); + b[2] = 0x991122ff; // red highlight gradient Bot + b[3] = 0x551111ff; // red highlight gradient Top + + stp[0] = 115.0 * log_meter0dB (-15); + stp[1] = 115.0 * log_meter0dB (-9); + stp[2] = 115.0 * log_meter0dB (-3); + stp[3] = 115.0; + + // XXX config changed -> update meter style (and size) + + for (int i = 0; i < 12; ++i) { + _meter[i] = new FastMeter ((uint32_t)floor (UIConfiguration::instance ().get_meter_hold ()), + 8, FastMeter::Horizontal, PX_SCALE (100), + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], + b[0], b[1], b[2], b[3], + stp[0], stp[1], stp[2], stp[3], + (UIConfiguration::instance ().get_meter_style_led () ? 3 : 1)); + + _surround_meter_box.pack_start (*_meter[i], false, false, 0); + } + + _binaural_meter_box.pack_start (_meter_ticks1_area, false, false); + for (int i = 12; i < 14; ++i) { + _meter[i] = new FastMeter ((uint32_t)floor (UIConfiguration::instance ().get_meter_hold ()), + 8, FastMeter::Vertical, PX_SCALE (250), + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], + b[0], b[1], b[2], b[3], + stp[0], stp[1], stp[2], stp[3], + (UIConfiguration::instance ().get_meter_style_led () ? 3 : 1)); + + _binaural_meter_box.pack_start (*_meter[i], false, false, 1); + } + _binaural_meter_box.pack_start (_meter_ticks2_area, false, false); + _binaural_meter_box.pack_start (_meter_metric_area, false, false); + + _types.push_back (DataType::AUDIO); + _types.push_back (DataType::AUDIO); + + _meter_metric_area.set_size_request (PX_SCALE(24), -1); + _meter_ticks1_area.set_size_request (PX_SCALE(3), -1); + _meter_ticks2_area.set_size_request (PX_SCALE(3), -1); + + _level_control.set_size_request (PX_SCALE (50), PX_SCALE (50)); + _level_control.set_tooltip_prefix (_("Level: ")); + _level_control.set_name ("monitor section knob"); + + VBox* lcenter_box = manage (new VBox); + lcenter_box->pack_start (_level_control, true, false); + _level_box.pack_start (*lcenter_box, true, false); + _level_box.set_size_request (-1, PX_SCALE (80)); + _level_box.set_name ("AudioBusStripBase"); + lcenter_box->show (); + + _output_button.set_text (_("Output")); + _output_button.set_name ("mixer strip button"); + _output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE); + _output_button.set_layout_ellipsize_width (PX_SCALE (_width) * PANGO_SCALE); + + _comment_button.set_name (X_("mixer strip button")); + _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END); + _comment_button.set_layout_ellipsize_width (PX_SCALE (_width) * PANGO_SCALE); + + _global_vpacker.set_border_width (1); + _global_vpacker.set_spacing (2); + + Gtk::Label* top_spacer = manage (new Gtk::Label); + top_spacer->show (); + + _global_vpacker.pack_start (*top_spacer, false, false, PX_SCALE (3)); + _global_vpacker.pack_start (_name_button, Gtk::PACK_SHRINK); + _global_vpacker.pack_start (_top_box, true, true); // expanding space + + update_spacers (); + +#ifndef MIXBUS + _global_vpacker.pack_end (_spacer, false, false); +#endif + + _binaural_meter_hbox.pack_end (_binaural_meter_box, false, false); + + _global_vpacker.pack_end (_comment_button, Gtk::PACK_SHRINK); + _global_vpacker.pack_end (_output_button, Gtk::PACK_SHRINK); + _global_vpacker.pack_end (_spacer_ctrl, Gtk::PACK_SHRINK); + _global_vpacker.pack_end (_binaural_meter_hbox, Gtk::PACK_SHRINK); + _global_vpacker.pack_end (_spacer_peak, Gtk::PACK_SHRINK); + _global_vpacker.pack_end (*mute_button, false, false); + _global_vpacker.pack_end (_level_box, Gtk::PACK_SHRINK); + _global_vpacker.pack_end (_surround_meter_box, false, false, PX_SCALE (3)); + _global_vpacker.pack_end (*lufs_table, false, false); + + _global_frame.add (_global_vpacker); + _global_frame.set_shadow_type (Gtk::SHADOW_IN); + _global_frame.set_name ("MixerStripFrame"); + add (_global_frame); + + _name_button.signal_button_press_event ().connect (sigc::mem_fun (*this, &SurroundStrip::name_button_button_press), false); + _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor)); + + _meter_metric_area.signal_expose_event().connect (sigc::mem_fun(*this, &SurroundStrip::meter_metrics_expose)); + _meter_ticks1_area.signal_expose_event().connect (sigc::mem_fun(*this, &SurroundStrip::meter_ticks1_expose)); + _meter_ticks2_area.signal_expose_event().connect (sigc::mem_fun(*this, &SurroundStrip::meter_ticks2_expose)); + + + add_events (Gdk::BUTTON_RELEASE_MASK | + Gdk::ENTER_NOTIFY_MASK | + Gdk::KEY_PRESS_MASK | + Gdk::KEY_RELEASE_MASK); + + set_can_focus (); + + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &SurroundStrip::parameter_changed)); + + //PresentationInfo::Change.connect (*this, invalidator (*this), boost::bind (&SurroundStrip::presentation_info_changed, this, _1), gui_context ()); +} + +void +SurroundStrip::update_spacers () +{ + std::string viz = UIConfiguration::instance().get_mixer_strip_visibility (); + + Gtk::Window window (WINDOW_TOPLEVEL); + VBox box; + FocusEntry pk; + HScrollbar scrollbar; + + ArdourButton small_btn ("btn"); + ArdourButton vca_btn (_("-VCAs-")); + + small_btn.set_name ("mixer strip button"); + small_btn.set_size_request (PX_SCALE(15), PX_SCALE(15)); + small_btn.ensure_style (); + + vca_btn.set_name (X_("vca assign button")); + vca_btn.ensure_style (); + + scrollbar.set_name ("MixerWindow"); + scrollbar.ensure_style (); + + pk.set_name ("MixerStripPeakDisplay"); + pk.ensure_style (); + Gtkmm2ext::set_size_request_to_display_given_text (pk, "-80.g", 2, 6); + + box.pack_start (pk); + box.pack_start (small_btn); + box.pack_start (scrollbar); + box.pack_start (vca_btn); + + window.add (box); + window.show_all (); + + _spacer.set_size_request (-1, scrollbar.size_request ().height + 3); + _spacer_peak.set_size_request (-1, pk.size_request ().height + 3); + + int h = small_btn.size_request ().height; + if (viz.find ("VCA") != std::string::npos && !_session->vca_manager().vcas().empty ()) { + h += vca_btn.size_request ().height; + } + _spacer_ctrl.set_size_request (-1, h); +} + +void +SurroundStrip::parameter_changed (std::string const& p) +{ + if (p == "mixer-element-visibility") { + update_spacers (); + } +} + +void +SurroundStrip::set_route (std::shared_ptr r) +{ + assert (r); + RouteUI::set_route (r); + + _output_button.set_route (_route, this); + + _level_control.set_controllable (_route->gain_control ()); + _level_control.show (); + + /* set up metering */ + _route->set_meter_type (MeterPeak0dB); + + _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&SurroundStrip::setup_comment_button, this), gui_context ()); + + _route->gain_control ()->MasterStatusChange.connect (route_connections, invalidator (*this), boost::bind (&SurroundStrip::update_spacers, this), gui_context()); + + /* now force an update of all the various elements */ + name_changed (); + comment_changed (); + setup_comment_button (); + + add_events (Gdk::BUTTON_RELEASE_MASK); + show_all (); +} + +void +SurroundStrip::setup_comment_button () +{ + std::string comment = _route->comment (); + + set_tooltip (_comment_button, comment.empty () ? _("Click to add/edit comments") : _route->comment ()); + + if (comment.empty ()) { + _comment_button.set_name ("generic button"); + _comment_button.set_text (_("Comments")); + return; + } + + _comment_button.set_name ("comment button"); + + std::string::size_type pos = comment.find_first_of (" \t\n"); + if (pos != std::string::npos) { + comment = comment.substr (0, pos); + } + if (comment.empty ()) { + _comment_button.set_text (_("Comments")); + } else { + _comment_button.set_text (comment); + } +} + +Gtk::Menu* +SurroundStrip::build_route_ops_menu () +{ + using namespace Menu_Helpers; + + Menu* menu = manage (new Menu); + MenuList& items = menu->items (); + menu->set_name ("ArdourContextMenu"); + + assert (_route->active ()); + + items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color))); + items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor))); + + items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration))); + + items.push_back (SeparatorElem ()); + + items.push_back (MenuElem (_("Rename..."), sigc::mem_fun (*this, &RouteUI::route_rename))); + + items.push_back (SeparatorElem ()); + + if (!Profile->get_mixbus ()) { + items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection))); + denormal_menu_item = dynamic_cast (&items.back ()); + denormal_menu_item->set_active (_route->denormal_protection ()); + } + + return menu; +} + +bool +SurroundStrip::name_button_button_press (GdkEventButton* ev) +{ + if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) { + Menu* r_menu = build_route_ops_menu (); + r_menu->popup (ev->button, ev->time); + return true; + } + return false; +} + +void +SurroundStrip::fast_update () +{ + std::shared_ptr peak_meter = _route->shared_peak_meter (); + for (uint32_t i = 0; i < 14; ++i) { + const float meter_level = peak_meter->meter_level (i, MeterPeak0dB); + _meter[i]->set (log_meter0dB (meter_level)); + } + + std::shared_ptr sur = _route->surround_return (); + float loud = sur->integrated_loudness(); + if (loud > -90) { + char buf[32]; + sprintf(buf, "%3.1f", loud); + _lufs_label.set_markup (string_compose ("%1", buf)); + } else { + _lufs_label.set_markup ("-"); + } + + float dbtp = sur->max_dbtp(); + if (dbtp > -90) { + char buf[32]; + sprintf(buf, "%3.1f", dbtp); + _dbtp_label.set_markup (string_compose ("%1", buf)); + } else { + _dbtp_label.set_markup ("-"); + } +} + +void +SurroundStrip::route_property_changed (const PropertyChange& what_changed) +{ + if (what_changed.contains (ARDOUR::Properties::name)) { + name_changed (); + } +} + +void +SurroundStrip::name_changed () +{ + _name_button.set_text (_route->name ()); + set_tooltip (_name_button, Gtkmm2ext::markup_escape_text (_route->name ())); +} + +void +SurroundStrip::set_button_names () +{ + mute_button->set_text (_("Mute")); +} + +void +SurroundStrip::hide_spacer (bool yn) +{ + if (!yn) { + _spacer.show (); + } else { + _spacer.hide (); + } +} +gint +SurroundStrip::meter_metrics_expose (GdkEventExpose* ev) +{ + return ArdourMeter::meter_expose_metrics (ev, MeterPeak0dB, _types, &_meter_metric_area); +} + +gint +SurroundStrip::meter_ticks1_expose (GdkEventExpose* ev) +{ + return ArdourMeter::meter_expose_ticks (ev, MeterPeak0dB, _types, &_meter_ticks1_area); +} + +gint +SurroundStrip::meter_ticks2_expose (GdkEventExpose* ev) +{ + return ArdourMeter::meter_expose_ticks (ev, MeterPeak0dB, _types, &_meter_ticks2_area); +} diff --git a/gtk2_ardour/surround_strip.h b/gtk2_ardour/surround_strip.h new file mode 100644 index 0000000000..7730d2c11a --- /dev/null +++ b/gtk2_ardour/surround_strip.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 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. + */ + +#ifndef _gtkardour_surround_strip_ +#define _gtkardour_surround_strip_ + +#include +#include +#include +#include + +#include "ardour/types.h" + +#include "widgets/ardour_button.h" +#include "widgets/ardour_knob.h" +#include "widgets/fastmeter.h" + +#include "io_button.h" +#include "route_ui.h" + +namespace ARDOUR +{ +class Route; +class Session; +} + +class Mixer_UI; + +class SurroundStrip : public RouteUI, public Gtk::EventBox +{ +public: + SurroundStrip (Mixer_UI&, ARDOUR::Session*, std::shared_ptr); + ~SurroundStrip (); + + void fast_update (); + void hide_spacer (bool); + + static PBD::Signal1 CatchDeletion; + +private: + void init (); + void set_route (std::shared_ptr); + void set_button_names (); + void setup_comment_button (); + void name_changed (); + void update_spacers (); + bool name_button_button_press (GdkEventButton*); + void route_property_changed (const PBD::PropertyChange&); + void parameter_changed (std::string const&); + + gint meter_metrics_expose (GdkEventExpose*); + gint meter_ticks1_expose (GdkEventExpose*); + gint meter_ticks2_expose (GdkEventExpose*); + + Gtk::Menu* build_route_ops_menu (); + + uint32_t _width; + Gtk::EventBox _spacer; + Gtk::EventBox _spacer_ctrl; + Gtk::EventBox _spacer_peak; + Gtk::Frame _global_frame; + Gtk::VBox _global_vpacker; + Gtk::VBox _surround_meter_box; + Gtk::HBox _binaural_meter_box; + Gtk::HBox _binaural_meter_hbox; + Gtk::HBox _level_box; + Gtk::HBox _top_box; + IOButton _output_button; + + Gtk::Label _lufs_cap; + Gtk::Label _lufs_label; + Gtk::Label _dbtp_cap; + Gtk::Label _dbtp_label; + + ArdourWidgets::ArdourButton _name_button; + ArdourWidgets::ArdourButton _comment_button; + ArdourWidgets::ArdourKnob _level_control; + ArdourWidgets::FastMeter* _meter[14]; + Gtk::DrawingArea _meter_metric_area; + Gtk::DrawingArea _meter_ticks1_area; + Gtk::DrawingArea _meter_ticks2_area; + + std::vector _types; +}; + +#endif diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index cee20aef67..9f30a29515 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -286,6 +286,7 @@ gtk2_ardour_sources = [ 'strip_silence_dialog.cc', 'stripable_colorpicker.cc', 'stripable_time_axis.cc', + 'surround_strip.cc', 'sys_ex.cc', 'template_dialog.cc', 'tempo_curve.cc', @@ -812,6 +813,9 @@ def build(bld): if bld.is_defined('PTFORMAT'): menus_argv += [ '-DPTFORMAT' ] + if bld.is_defined('MIXBUS') or Options.options.debug: + menus_argv += [ '-DVAPOR' ] + # always build all versions of the menu definitions # so that we can try them out with different program builds. for program in [ 'ardour']: @@ -820,7 +824,7 @@ def build(bld): obj.command_is_external = True obj.no_inputs = True obj.argv = menus_argv - obj.dep_vars = ['PTFORMAT', 'MIXBUS', 'MIXBUS32C', 'WINDOWS'] + obj.dep_vars = ['PTFORMAT', 'MIXBUS', 'MIXBUS32C', 'WINDOWS', 'debug'] obj.stdin = program + '.menus.in' obj.stdout = program + '.menus' bld.install_files (bld.env['CONFDIR'], program + '.menus')