how about that ... a monitor/main section .. GUI is still unfinished .. several small fixes to processor loading/listen mgmt and a few debug output lines rmeoved. knob images are provisional, and can be found in icons/knob.png and related files

git-svn-id: svn://localhost/ardour2/branches/3.0@6744 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-03-10 17:31:16 +00:00
parent b2bc408cef
commit 325671e20a
29 changed files with 1192 additions and 73 deletions

View File

@ -4792,7 +4792,7 @@ Editor::handle_new_route (RouteList& routes)
for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
boost::shared_ptr<Route> route = (*x);
if (route->is_hidden()) {
if (route->is_hidden() || route->is_control()) {
continue;
}

View File

@ -34,6 +34,7 @@
#include "pbd/signals.h"
#include "ardour/chan_count.h"
#include "ardour/types.h"
#include "ardour/session_handle.h"

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
gtk2_ardour/icons/knob.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -74,13 +74,13 @@ class LevelMeter : public Gtk::HBox, public ARDOUR::SessionHandlePtr
struct MeterInfo {
Gtkmm2ext::FastMeter *meter;
gint16 width;
int length;
int length;
bool packed;
MeterInfo() {
meter = 0;
width = 0;
length = 0;
length = 0;
packed = false;
}
};

View File

@ -42,6 +42,7 @@
#include "keyboard.h"
#include "mixer_ui.h"
#include "mixer_strip.h"
#include "monitor_section.h"
#include "plugin_selector.h"
#include "ardour_ui.h"
#include "prompter.h"
@ -66,6 +67,7 @@ Mixer_UI::Mixer_UI ()
{
_strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
track_menu = 0;
monitor_section = 0;
route_group_context_menu = 0;
no_track_list_redisplay = false;
in_group_row_change = false;
@ -253,6 +255,8 @@ Mixer_UI::Mixer_UI ()
MixerStrip::CatchDeletion.connect (*this, ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context());
MonitorSection::setup_knob_images ();
#ifndef DEFER_PLUGIN_SELECTOR_LOAD
_plugin_selector = new PluginSelector (PluginManager::the_manager ());
#endif
@ -313,9 +317,18 @@ Mixer_UI::add_strip (RouteList& routes)
boost::shared_ptr<Route> route = (*x);
if (route->is_hidden()) {
return;
continue;
}
if (route->is_control()) {
monitor_section = new MonitorSection (_session);
out_packer.pack_end (monitor_section->pack_widget(), false, false);
monitor_section->pack_widget().show_all ();
/* no regular strip */
continue;
}
strip = new MixerStrip (*this, _session, route);
strips.push_back (strip);
@ -508,10 +521,14 @@ Mixer_UI::session_going_away ()
group_model->clear ();
_selection.clear ();
track_model->clear ();
delete monitor_section;
monitor_section = 0;
for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
delete (*i);
}
strips.clear ();
WindowTitle title(Glib::get_application_name());
@ -576,6 +593,10 @@ Mixer_UI::fast_update_strips ()
for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
(*i)->fast_update ();
}
if (monitor_section) {
monitor_section->fast_update ();
}
}
}

View File

@ -50,6 +50,7 @@ namespace ARDOUR {
class MixerStrip;
class PluginSelector;
class MixerGroupTabs;
class MonitorSection;
class Mixer_UI : public Gtk::Window, public PBD::ScopedConnectionList, public ARDOUR::SessionHandlePtr
{
@ -194,6 +195,8 @@ class Mixer_UI : public Gtk::Window, public PBD::ScopedConnectionList, public AR
void track_column_click (gint);
void build_track_menu ();
MonitorSection* monitor_section;
PluginSelector *_plugin_selector;
void strip_property_changed (const PBD::PropertyChange&, MixerStrip *);

View File

@ -0,0 +1,562 @@
#include <gdkmm/pixbuf.h>
#include "pbd/compose.h"
#include "pbd/error.h"
#include "gtkmm2ext/bindable_button.h"
#include "gtkmm2ext/tearoff.h"
#include "gtkmm2ext/actions.h"
#include "ardour/dB.h"
#include "ardour/monitor_processor.h"
#include "ardour/route.h"
#include "ardour/utils.h"
#include "monitor_section.h"
#include "utils.h"
#include "volume_controller.h"
#include "i18n.h"
using namespace ARDOUR;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace PBD;
using namespace std;
Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
Glib::RefPtr<Gdk::Pixbuf> MonitorSection::big_knob_pixbuf;
Glib::RefPtr<Gdk::Pixbuf> MonitorSection::little_knob_pixbuf;
MonitorSection::MonitorSection (Session* s)
: AxisView (s)
, RouteUI (s)
, main_table (2, 3)
, meter (s)
, _tearoff (0)
, gain_adjustment (1.0, 0.0, 2.0, 0.01, 0.1)
, gain_control (0)
, dim_adjustment (0.2, 0.0, 1.0, 0.01, 0.1)
, dim_control (0)
, solo_boost_adjustment (1.0, 1.0, 2.0, 0.01, 0.1)
, solo_boost_control (0)
, solo_in_place_button (solo_model_group, _("SiP"))
, afl_button (solo_model_group, _("AFL"))
, pfl_button (solo_model_group, _("PFL"))
, cut_all_button (_("MUTE"))
, dim_all_button (_("DIM"))
{
Glib::RefPtr<Action> act;
if (!monitor_actions) {
/* do some static stuff */
register_actions ();
}
_route = _session->control_out ();
if (!_route) {
throw failed_constructor ();
}
_monitor = _route->monitor_control ();
if (!_monitor) {
throw failed_constructor ();
}
HBox* sub_knob_packer = manage (new HBox);
sub_knob_packer->set_spacing (12);
VBox* spin_packer;
Label* spin_label;
gain_control = new VolumeController (big_knob_pixbuf, &gain_adjustment, true);
gain_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::gain_value_changed));
gain_control->spinner().signal_output().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::nonlinear_gain_printer),
&gain_control->spinner()));
HBox* center_gain_control = manage (new HBox);
center_gain_control->pack_start (*gain_control, true, true);
spin_label = manage (new Label (_("Gain (dB)")));
spin_packer = manage (new VBox);
spin_packer->set_spacing (6);
spin_packer->pack_start (*center_gain_control, false, false);
spin_packer->pack_start (*spin_label, false, false);
knob_packer.pack_start (*spin_packer, false, false);
dim_control = new VolumeController (little_knob_pixbuf, &dim_adjustment, true, 30, 30);
dim_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::dim_level_changed));
dim_control->spinner().signal_output().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::linear_gain_printer),
&dim_control->spinner()));
spin_label = manage (new Label (_("Dim Cut (dB)")));
spin_packer = manage (new VBox);
spin_packer->set_spacing (6);
spin_packer->pack_start (*dim_control, false, false);
spin_packer->pack_start (*spin_label, false, false);
sub_knob_packer->pack_start (*spin_packer, false, true);
solo_boost_control = new VolumeController (little_knob_pixbuf, &solo_boost_adjustment, true, 30, 30);
solo_boost_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::solo_boost_changed));
solo_boost_control->spinner().signal_output().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::linear_gain_printer),
&solo_boost_control->spinner()));
spin_label = manage (new Label (_("Solo Boost (dB)")));
spin_packer = manage (new VBox);
spin_packer->set_spacing (6);
spin_packer->pack_start (*solo_boost_control, false, false);
spin_packer->pack_start (*spin_label, false, false);
sub_knob_packer->pack_start (*spin_packer, false, true);
knob_packer.pack_start (*sub_knob_packer, false, true);
sub_knob_packer->show ();
knob_packer.show ();
gain_control->show_all ();
dim_control->show_all ();
solo_boost_control->show_all ();
meter.set_meter (&_route->peak_meter());
meter.setup_meters (300, 5);
table_knob_packer.pack_start (main_table, true, true);
table_knob_packer.pack_start (knob_packer, false, false);
table_knob_packer.show ();
solo_model_box.set_spacing (12);
solo_model_box.pack_start (solo_in_place_button, false, false);
solo_model_box.pack_start (afl_button, false, false);
solo_model_box.pack_start (pfl_button, false, false);
solo_in_place_button.show ();
afl_button.show ();
pfl_button.show ();
solo_model_box.show ();
upper_packer.pack_start (solo_model_box, false, false);
act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
if (act) {
act->connect_proxy (cut_all_button);
}
act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
if (act) {
act->connect_proxy (dim_all_button);
}
cut_all_button.show ();
dim_all_button.show ();
lower_packer.pack_start (dim_all_button, false, false);
lower_packer.pack_start (cut_all_button, false, false);
vpacker.set_border_width (12);
vpacker.set_spacing (12);
vpacker.pack_start (upper_packer, false, false);
vpacker.pack_start (table_knob_packer, false, false);
vpacker.pack_start (lower_packer, false, false);
VBox* keep_meter_under_control = manage (new VBox);
keep_meter_under_control->pack_start (meter, false, false);
keep_meter_under_control->show ();
hpacker.set_border_width (12);
hpacker.set_spacing (12);
hpacker.pack_start (*keep_meter_under_control, false, false);
hpacker.pack_start (vpacker, true, true);
main_table.show ();
hpacker.show ();
upper_packer.show ();
lower_packer.show ();
vpacker.show ();
meter.show_all ();
populate_buttons ();
map_state ();
_tearoff = new TearOff (hpacker);
/* if torn off, make this a normal window */
_tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
_tearoff->tearoff_window().set_title (X_("Monitor"));
}
MonitorSection::~MonitorSection ()
{
delete _tearoff;
delete gain_control;
delete dim_control;
delete solo_boost_control;
}
void
MonitorSection::populate_buttons ()
{
Glib::RefPtr<Action> act;
uint32_t nchans = _route->monitor_control()->output_streams().n_audio();
main_table.resize (nchans+1, 5);
main_table.set_col_spacings (6);
main_table.set_row_spacings (6);
main_table.set_homogeneous (true);
Label* l1 = manage (new Label (X_("out")));
main_table.attach (*l1, 0, 1, 0, 1, SHRINK|FILL, SHRINK|FILL);
l1 = manage (new Label (X_("cut")));
main_table.attach (*l1, 1, 2, 0, 1, SHRINK|FILL, SHRINK|FILL);
l1 = manage (new Label (X_("dim")));
main_table.attach (*l1, 2, 3, 0, 1, SHRINK|FILL, SHRINK|FILL);
l1 = manage (new Label (X_("solo")));
main_table.attach (*l1, 3, 4, 0, 1, SHRINK|FILL, SHRINK|FILL);
l1 = manage (new Label (X_("inv")));
main_table.attach (*l1, 4, 5, 0, 1, SHRINK|FILL, SHRINK|FILL);
#if 0
/* the "all" buttons for cut & dim */
Label *la = manage (new Label (X_("all")));
main_table.attach (*la, 0, 1, 1, 2, SHRINK|FILL, SHRINK|FILL);
/* cut all */
BindableToggleButton* ca = manage (new BindableToggleButton (X_("")));
ca->set_name (X_("MixerMuteButton"));
gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (ca->gobj()), false);
main_table.attach (*ca, 1, 2, 1, 2, SHRINK|FILL, SHRINK|FILL);
act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
if (act) {
act->connect_proxy (*ca);
}
/* dim all */
BindableToggleButton* da = manage (new BindableToggleButton (X_("")));
da->set_name (X_("MixerMuteButton"));
gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (da->gobj()), false);
main_table.attach (*da, 2, 3, 1, 2, SHRINK|FILL, SHRINK|FILL);
act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
if (act) {
act->connect_proxy (*da);
}
uint32_t row_offset = 2;
#else
uint32_t row_offset = 1;
#endif
for (uint32_t i = 0; i < nchans; ++i) {
string l;
char buf[64];
if (nchans == 2) {
if (i == 0) {
l = "L";
} else {
l = "R";
}
} else {
char buf[32];
snprintf (buf, sizeof (buf), "%d", i+1);
l = buf;
}
Label* c1 = manage (new Label (l));
main_table.attach (*c1, 0, 1, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
/* Cut */
BindableToggleButton* c2 = manage (new BindableToggleButton (X_("")));
c2->set_name (X_("MixerMuteButton"));
gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
main_table.attach (*c2, 1, 2, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
act->connect_proxy (*c2);
}
/* Dim */
BindableToggleButton* c3 = manage (new BindableToggleButton (X_("")));
c3->set_name (X_("MixerMuteButton"));
gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
main_table.attach (*c3, 2, 3, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
act->connect_proxy (*c3);
}
/* Solo */
BindableToggleButton* c4 = manage (new BindableToggleButton (X_("")));
c4->set_name (X_("MixerSoloButton"));
gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
main_table.attach (*c4, 3, 4, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
act->connect_proxy (*c4);
}
/* Invert (Polarity/Phase) */
BindableToggleButton* c5 = manage (new BindableToggleButton (X_("")));
c5->set_name (X_("MixerPhaseInvertButton"));
gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
main_table.attach (*c5, 4, 5, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
act->connect_proxy (*c5);
}
}
main_table.show_all ();
}
void
MonitorSection::set_button_names ()
{
rec_enable_button_label.set_text ("rec");
mute_button_label.set_text ("rec");
solo_button_label.set_text ("rec");
}
Widget&
MonitorSection::pack_widget () const
{
return *_tearoff;
}
void
MonitorSection::dim_all ()
{
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
if (act) {
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
_monitor->set_dim_all (tact->get_active());
}
}
void
MonitorSection::cut_all ()
{
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
if (act) {
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
_monitor->set_cut_all (tact->get_active());
}
}
void
MonitorSection::cut_channel (uint32_t chn)
{
char buf[64];
snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
--chn; // 0-based in backend
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
_monitor->set_cut (chn, tact->get_active());
}
}
void
MonitorSection::dim_channel (uint32_t chn)
{
char buf[64];
snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
--chn; // 0-based in backend
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
_monitor->set_dim (chn, tact->get_active());
}
}
void
MonitorSection::solo_channel (uint32_t chn)
{
char buf[64];
snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
--chn; // 0-based in backend
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
_monitor->set_solo (chn, tact->get_active());
}
}
void
MonitorSection::invert_channel (uint32_t chn)
{
char buf[64];
--chn; // 0-based in backend
snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
if (act) {
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
_monitor->set_polarity (chn, tact->get_active());
}
}
void
MonitorSection::register_actions ()
{
string action_name;
string action_descr;
monitor_actions = ActionGroup::create (X_("Monitor"));
ActionManager::add_action_group (monitor_actions);
ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "",
sigc::mem_fun (*this, &MonitorSection::cut_all));
ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "",
sigc::mem_fun (*this, &MonitorSection::dim_all));
/* note the 1-based counting for naming vs. 0-based for action */
for (uint32_t chn = 1; chn <= 16; ++chn) {
/* for the time being, do not use the action description because it always
shows up in the buttons, which is undesirable.
*/
action_name = string_compose (X_("monitor-cut-%1"), chn);
action_descr = string_compose (_("Cut Monitor Chn %1"), chn);
ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "",
sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
action_name = string_compose (X_("monitor-dim-%1"), chn);
action_descr = string_compose (_("Dim Monitor Chn %1"), chn+1);
ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "",
sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
action_name = string_compose (X_("monitor-solo-%1"), chn);
action_descr = string_compose (_("Solo Monitor Chn %1"), chn);
ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "",
sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
action_name = string_compose (X_("monitor-invert-%1"), chn);
action_descr = string_compose (_("Invert Monitor Chn %1"), chn);
ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "",
sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
}
}
void
MonitorSection::fast_update ()
{
meter.update_meters ();
}
void
MonitorSection::setup_knob_images ()
{
try {
big_knob_pixbuf = ::get_icon ("knob");
} catch (...) {
error << "No knob image found (or not loadable) at "
<< " .... "
<< endmsg;
throw failed_constructor ();
}
try {
little_knob_pixbuf = ::get_icon ("littleknob");
} catch (...) {
error << "No knob image found (or not loadable) at "
<< " .... "
<< endmsg;
throw failed_constructor ();
}
}
void
MonitorSection::gain_value_changed ()
{
_route->set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
}
void
MonitorSection::dim_level_changed ()
{
_monitor->set_dim_level (dim_adjustment.get_value());
}
void
MonitorSection::solo_boost_changed ()
{
_monitor->set_solo_boost_level (solo_boost_adjustment.get_value());
}
bool
MonitorSection::nonlinear_gain_printer (SpinButton* button)
{
double val = button->get_adjustment()->get_value();
char buf[16];
snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (val)));
button->set_text (buf);
return true;
}
bool
MonitorSection::linear_gain_printer (SpinButton* button)
{
double val = button->get_adjustment()->get_value();
char buf[16];
snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (val));
button->set_text (buf);
return true;
}
void
MonitorSection::map_state ()
{
cerr << "route gain = " << _route->gain_control()->get_value() << endl;
gain_control->get_adjustment()->set_value (gain_to_slider_position (_route->gain_control()->get_value()));
dim_control->get_adjustment()->set_value (_monitor->dim_level());
solo_boost_control->get_adjustment()->set_value (_monitor->solo_boost_level());
}

View File

@ -0,0 +1,98 @@
/*
Copyright (C) 2010 Paul Davis
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <gtkmm/box.h>
#include <gtkmm/table.h>
#include "gtkmm2ext/bindable_button.h"
#include "axis_view.h"
#include "level_meter.h"
#include "route_ui.h"
namespace Gtkmm2ext {
class TearOff;
}
class VolumeController;
class MonitorSection : public RouteUI
{
public:
MonitorSection (ARDOUR::Session*);
~MonitorSection ();
Gtk::Widget& pack_widget () const;
void fast_update ();
static void setup_knob_images ();
private:
Gtk::VBox vpacker;
Gtk::HBox hpacker;
Gtk::Table main_table;
Gtk::VBox upper_packer;
Gtk::VBox lower_packer;
Gtk::VBox table_knob_packer;
Gtk::VBox knob_packer;
LevelMeter meter;
Gtkmm2ext::TearOff* _tearoff;
Gtk::Adjustment gain_adjustment;
VolumeController* gain_control;
Gtk::Adjustment dim_adjustment;
VolumeController* dim_control;
Gtk::Adjustment solo_boost_adjustment;
VolumeController* solo_boost_control;
void populate_buttons ();
void set_button_names ();
void map_state ();
boost::shared_ptr<ARDOUR::MonitorProcessor> _monitor;
boost::shared_ptr<ARDOUR::Route> _route;
static Glib::RefPtr<Gtk::ActionGroup> monitor_actions;
void register_actions ();
static Glib::RefPtr<Gdk::Pixbuf> big_knob_pixbuf;
static Glib::RefPtr<Gdk::Pixbuf> little_knob_pixbuf;
void cut_channel (uint32_t);
void dim_channel (uint32_t);
void solo_channel (uint32_t);
void invert_channel (uint32_t);
void dim_all ();
void cut_all ();
void mono ();
void dim_level_changed ();
void solo_boost_changed ();
void gain_value_changed ();
bool nonlinear_gain_printer (Gtk::SpinButton*);
bool linear_gain_printer (Gtk::SpinButton*);
Gtk::RadioButtonGroup solo_model_group;
Gtk::RadioButton solo_in_place_button;
Gtk::RadioButton afl_button;
Gtk::RadioButton pfl_button;
Gtk::HBox solo_model_box;
BindableToggleButton cut_all_button;
BindableToggleButton dim_all_button;
};

View File

@ -738,6 +738,7 @@ public:
h->pack_start (*l, false, false);
h->pack_start (*_db_slider, false, false);
h->pack_start (_db_display, false, false);
h->show_all ();
set_size_request_to_display_given_text (_db_display, "-99.0", 12, 12);

View File

@ -766,13 +766,13 @@ RouteUI::update_solo_display ()
void
RouteUI::solo_changed_so_update_mute ()
{
Gtkmm2ext::UI::instance()->call_slot (boost::bind (&RouteUI::update_mute_display, this));
update_mute_display ();
}
void
RouteUI::mute_changed(void* /*src*/)
{
Gtkmm2ext::UI::instance()->call_slot (boost::bind (&RouteUI::update_mute_display, this));
update_mute_display ();
}
int
@ -812,6 +812,10 @@ RouteUI::mute_visual_state (Session* s, boost::shared_ptr<Route> r)
void
RouteUI::update_mute_display ()
{
if (!_route) {
return;
}
bool model = _route->muted();
bool view = mute_button->get_active();
@ -837,17 +841,13 @@ RouteUI::route_rec_enable_changed ()
void
RouteUI::session_rec_enable_changed ()
{
if (!rec_enable_button) {
return;
}
Gtkmm2ext::UI::instance()->call_slot (boost::bind (&RouteUI::update_rec_display, this));
update_rec_display ();
}
void
RouteUI::update_rec_display ()
{
if (!rec_enable_button) {
if (!rec_enable_button || !_route) {
return;
}

View File

@ -137,6 +137,7 @@ gtk2_ardour_sources = [
'mixer_group_tabs.cc',
'mixer_strip.cc',
'mixer_ui.cc',
'monitor_section.cc',
'nag.cc',
'option_editor.cc',
'opts.cc',
@ -197,6 +198,7 @@ gtk2_ardour_sources = [
'ui_config.cc',
'utils.cc',
'version.cc',
'volume_controller.cc',
'waveview.cc',
]

View File

@ -179,10 +179,10 @@ Amp::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, n
void
Amp::apply_gain (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t target)
{
/** Apply a (potentially) declicked gain to the audio buffers of @a bufs
/** Apply a (potentially) declicked gain to the buffers of @a bufs
*/
if (nframes == 0 || bufs.count().n_audio() == 0) {
if (nframes == 0 || bufs.count().n_total() == 0) {
return;
}
@ -196,7 +196,6 @@ Amp::apply_gain (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t targ
gain_t delta;
double fractional_shift = -1.0/declick;
double fractional_pos;
gain_t polscale = 1.0f;
if (target < initial) {
/* fade out: remove more and more of delta from initial */
@ -232,7 +231,7 @@ Amp::apply_gain (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t targ
fractional_pos = 1.0;
for (nframes_t nx = 0; nx < declick; ++nx) {
buffer[nx] *= polscale * (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
buffer[nx] *= (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
fractional_pos += fractional_shift;
}
@ -249,6 +248,57 @@ Amp::apply_gain (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t targ
}
}
void
Amp::apply_gain (AudioBuffer& buf, nframes_t nframes, gain_t initial, gain_t target)
{
/** Apply a (potentially) declicked gain to the contents of @a buf
*/
if (nframes == 0) {
return;
}
// if we don't need to declick, defer to apply_simple_gain
if (initial == target) {
apply_simple_gain (buf, nframes, target);
return;
}
const nframes_t declick = std::min ((nframes_t)128, nframes);
gain_t delta;
double fractional_shift = -1.0/declick;
double fractional_pos;
if (target < initial) {
/* fade out: remove more and more of delta from initial */
delta = -(initial - target);
} else {
/* fade in: add more and more of delta from initial */
delta = target - initial;
}
Sample* const buffer = buf.data();
fractional_pos = 1.0;
for (nframes_t nx = 0; nx < declick; ++nx) {
buffer[nx] *= (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
fractional_pos += fractional_shift;
}
/* now ensure the rest of the buffer has the target value applied, if necessary. */
if (declick != nframes) {
if (target == 0.0) {
memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
} else if (target != 1.0) {
apply_gain_to_buffer (&buffer[declick], nframes - declick, target);
}
}
}
void
Amp::apply_simple_gain (BufferSet& bufs, nframes_t nframes, gain_t target)
{
@ -288,6 +338,16 @@ Amp::apply_simple_gain (BufferSet& bufs, nframes_t nframes, gain_t target)
}
}
void
Amp::apply_simple_gain (AudioBuffer& buf, nframes_t nframes, gain_t target)
{
if (target == 0.0) {
memset (buf.data(), 0, sizeof (Sample) * nframes);
} else if (target != 1.0) {
apply_gain_to_buffer (buf.data(), nframes, target);
}
}
void
Amp::inc_gain (gain_t factor, void *src)
{

View File

@ -59,6 +59,9 @@ public:
static void apply_gain (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t target);
static void apply_simple_gain(BufferSet& bufs, nframes_t nframes, gain_t target);
static void apply_gain (AudioBuffer& buf, nframes_t nframes, gain_t initial, gain_t target);
static void apply_simple_gain(AudioBuffer& buf, nframes_t nframes, gain_t target);
gain_t gain () const { return _gain_control->user_float(); }

View File

@ -47,6 +47,7 @@ class InternalSend : public Send
void set_block_size (nframes_t);
boost::shared_ptr<Route> target_route() const { return _send_to; }
const PBD::ID& target_id() const { return _send_to_id; }
private:
BufferSet mixbufs;

View File

@ -0,0 +1,87 @@
/*
Copyright (C) 2010 Paul Davis
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_monitor_processor_h__
#define __ardour_monitor_processor_h__
#include <vector>
#include "pbd/signals.h"
#include "ardour/types.h"
#include "ardour/processor.h"
class XMLNode;
namespace ARDOUR {
class Session;
class MonitorProcessor : public Processor
{
public:
MonitorProcessor (Session&);
MonitorProcessor (Session&, const XMLNode& name);
bool display_to_user() const;
void run (BufferSet& /*bufs*/, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t /*nframes*/, bool /*result_required*/);
XMLNode& state (bool full);
int set_state (const XMLNode&, int /* version */);
bool configure_io (ChanCount in, ChanCount out);
bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
void set_cut_all (bool);
void set_dim_all (bool);
void set_polarity (uint32_t, bool invert);
void set_cut (uint32_t, bool cut);
void set_dim (uint32_t, bool dim);
void set_solo (uint32_t, bool);
void set_dim_level (gain_t);
void set_solo_boost_level (gain_t);
gain_t dim_level() const { return _dim_level; }
gain_t solo_boost_level() const { return _solo_boost_level; }
bool dimmed (uint32_t chn) const;
bool soloed (uint32_t chn) const;
bool inverted (uint32_t chn) const;
bool cut (uint32_t chn) const;
PBD::Signal0<void> Changed;
private:
std::vector<gain_t> current_gain;
std::vector<gain_t> _cut;
std::vector<bool> _dim;
std::vector<gain_t> _polarity;
std::vector<bool> _soloed;
uint32_t solo_cnt;
bool _dim_all;
bool _cut_all;
volatile gain_t _dim_level;
volatile gain_t _solo_boost_level;
};
} /* namespace */
#endif /* __ardour_monitor_processor_h__ */

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 2000 Paul Davis
Copyright (C) 2009-2010 Paul Davis
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

View File

@ -54,6 +54,7 @@ class Processor;
class RouteGroup;
class Send;
class InternalReturn;
class MonitorProcessor;
class Route : public SessionObject, public AutomatableControls, public RouteGroupMember
{
@ -80,7 +81,6 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
bool active() const { return _active; }
void set_active (bool yn);
static std::string ensure_track_or_route_name(std::string, Session &);
std::string comment() { return _comment; }
@ -190,10 +190,11 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
/* special processors */
boost::shared_ptr<Delivery> control_outs() const { return _control_outs; }
boost::shared_ptr<Delivery> main_outs() const { return _main_outs; }
boost::shared_ptr<InternalReturn> internal_return() const { return _intreturn; }
boost::shared_ptr<Send> internal_send_for (boost::shared_ptr<const Route> target) const;
boost::shared_ptr<Delivery> control_outs() const { return _control_outs; }
boost::shared_ptr<Delivery> main_outs() const { return _main_outs; }
boost::shared_ptr<InternalReturn> internal_return() const { return _intreturn; }
boost::shared_ptr<MonitorProcessor> monitor_control() const { return _monitor_control; }
boost::shared_ptr<Send> internal_send_for (boost::shared_ptr<const Route> target) const;
void add_internal_return ();
BufferSet* get_return_buffer () const;
void release_return_buffer () const;
@ -210,7 +211,7 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
};
int add_processor (boost::shared_ptr<Processor>, Placement placement, ProcessorStreams* err = 0);
int add_processor (boost::shared_ptr<Processor>, ProcessorList::iterator iter, ProcessorStreams* err = 0);
int add_processor (boost::shared_ptr<Processor>, ProcessorList::iterator iter, ProcessorStreams* err = 0, bool activation_allowed = true);
int add_processors (const ProcessorList&, boost::shared_ptr<Processor> before, ProcessorStreams* err = 0);
int add_processors (const ProcessorList&, ProcessorList::iterator iter, ProcessorStreams* err = 0);
int remove_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0);
@ -364,6 +365,7 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
boost::shared_ptr<Delivery> _main_outs;
boost::shared_ptr<Delivery> _control_outs;
boost::shared_ptr<InternalReturn> _intreturn;
boost::shared_ptr<MonitorProcessor> _monitor_control;
Flag _flags;
int _pending_declick;

View File

@ -993,8 +993,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
boost::scoped_ptr<SessionDirectory> _session_dir;
void hookup_io ();
void when_engine_running ();
void hookup_io (bool new_session);
void when_engine_running (bool new_session);
void graph_reordered ();
std::string _current_snapshot_name;

View File

@ -0,0 +1,221 @@
#include "pbd/xml++.h"
#include "ardour/amp.h"
#include "ardour/dB.h"
#include "ardour/monitor_processor.h"
#include "ardour/session.h"
#include "i18n.h"
using namespace ARDOUR;
using namespace std;
MonitorProcessor::MonitorProcessor (Session& s)
: Processor (s, X_("MonitorOut"))
{
solo_cnt = 0;
_cut_all = false;
_dim_all = false;
_dim_level = 0.2;
_solo_boost_level = 1.0;
}
MonitorProcessor::MonitorProcessor (Session& s, const XMLNode& node)
: Processor (s, node)
{
set_state (node, Stateful::loading_state_version);
}
int
MonitorProcessor::set_state (const XMLNode& node, int version)
{
return Processor::set_state (node, version);
}
XMLNode&
MonitorProcessor::state (bool full)
{
XMLNode& node (Processor::state (full));
/* this replaces any existing "type" property */
node.add_property (X_("type"), X_("monitor"));
return node;
}
void
MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
{
uint32_t chn = 0;
gain_t target_gain;
gain_t dim_level_this_time = _dim_level;
gain_t global_cut = (_cut_all ? 0.0f : 1.0f);
gain_t global_dim = (_dim_all ? dim_level_this_time : 1.0f);
gain_t solo_boost;
if (_session.listening() || _session.soloing()) {
solo_boost = _solo_boost_level;
} else {
solo_boost = 1.0;
}
for (BufferSet::audio_iterator b = bufs.audio_begin(); b != bufs.audio_end(); ++b) {
/* don't double-scale by both track dim and global dim coefficients */
gain_t dim_level = (global_dim == 1.0 ? (_dim[chn] ? dim_level_this_time : 1.0) : 1.0);
if (_soloed[chn]) {
target_gain = _polarity[chn] * _cut[chn] * dim_level * global_cut * global_dim * solo_boost;
} else {
if (solo_cnt == 0) {
target_gain = _polarity[chn] * _cut[chn] * dim_level * global_cut * global_dim * solo_boost;
} else {
target_gain = 0.0;
}
}
if (target_gain != current_gain[chn] || target_gain != 1.0f) {
Amp::apply_gain (*b, nframes, current_gain[chn], target_gain);
current_gain[chn] = target_gain;
}
++chn;
}
}
bool
MonitorProcessor::configure_io (ChanCount in, ChanCount out)
{
uint32_t needed = in.n_audio();
while (current_gain.size() > needed) {
current_gain.pop_back ();
_dim.pop_back ();
_cut.pop_back ();
_polarity.pop_back ();
if (_soloed.back()) {
if (solo_cnt > 0) {
--solo_cnt;
}
}
_soloed.pop_back ();
}
while (current_gain.size() < needed) {
current_gain.push_back (1.0);
_dim.push_back (false);
_cut.push_back (1.0);
_polarity.push_back (1.0);
_soloed.push_back (false);
}
return Processor::configure_io (in, out);
}
bool
MonitorProcessor::can_support_io_configuration (const ChanCount& in, ChanCount& out) const
{
return in == out;
}
void
MonitorProcessor::set_polarity (uint32_t chn, bool invert)
{
if (invert) {
_polarity[chn] = -1.0f;
} else {
_polarity[chn] = 1.0f;
}
}
void
MonitorProcessor::set_dim (uint32_t chn, bool yn)
{
_dim[chn] = yn;
}
void
MonitorProcessor::set_cut (uint32_t chn, bool yn)
{
if (yn) {
_cut[chn] = 0.0f;
} else {
_cut[chn] = 1.0f;
}
}
void
MonitorProcessor::set_solo (uint32_t chn, bool solo)
{
_soloed[chn] = solo;
if (solo) {
solo_cnt++;
} else {
if (solo_cnt > 0) {
solo_cnt--;
}
}
}
void
MonitorProcessor::set_cut_all (bool yn)
{
_cut_all = yn;
}
void
MonitorProcessor::set_dim_all (bool yn)
{
_dim_all = yn;
}
bool
MonitorProcessor::display_to_user () const
{
return false;
}
void
MonitorProcessor::set_dim_level (gain_t val)
{
_dim_level = val;
}
void
MonitorProcessor::set_solo_boost_level (gain_t val)
{
_solo_boost_level = val;
}
bool
MonitorProcessor::soloed (uint32_t chn) const
{
return _soloed[chn];
}
bool
MonitorProcessor::inverted (uint32_t chn) const
{
return _polarity[chn] < 0.0f;
}
bool
MonitorProcessor::cut (uint32_t chn) const
{
return _cut[chn] == 0.0f;
}
bool
MonitorProcessor::dimmed (uint32_t chn) const
{
return _dim[chn];
}

View File

@ -84,7 +84,6 @@ Processor::Processor (Session& session, const XMLNode& node)
, _display_to_user (true)
{
set_state (node, Stateful::loading_state_version);
_pending_active = _active;
}
XMLNode&
@ -167,6 +166,7 @@ Processor::set_state_2X (const XMLNode & node, int /*version*/)
if ((prop = (*i)->property ("active")) != 0) {
if (_active != string_is_affirmative (prop->value())) {
_active = !_active;
_pending_active = _active;
ActiveChanged (); /* EMIT_SIGNAL */
}
}
@ -238,8 +238,6 @@ Processor::set_state (const XMLNode& node, int version)
}
if ((prop = node.property ("active")) == 0) {
warning << _("XML node describing a processor is missing the `active' field,"
"trying legacy active flag from child node") << endmsg;
if (legacy_active) {
prop = legacy_active;
} else {
@ -250,7 +248,8 @@ Processor::set_state (const XMLNode& node, int version)
if (_active != string_is_affirmative (prop->value())) {
_active = !_active;
ActiveChanged (); /* EMIT_SIGNAL */
_pending_active = _active;
ActiveChanged (); /* EMIT_SIGNAL */
}
return 0;

View File

@ -45,6 +45,7 @@
#include "ardour/ladspa_plugin.h"
#include "ardour/meter.h"
#include "ardour/mix.h"
#include "ardour/monitor_processor.h"
#include "ardour/panner.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
@ -84,12 +85,16 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
_meter.reset (new PeakMeter (_session));
_meter->set_display_to_user (false);
add_processor (_meter, PostFader);
if (_flags & ControlOut) {
if (is_control()) {
/* where we listen to tracks */
_intreturn.reset (new InternalReturn (_session));
add_processor (_intreturn, PreFader);
_monitor_control.reset (new MonitorProcessor (_session));
add_processor (_monitor_control, PostFader);
}
_main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
@ -485,7 +490,6 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
feeding the listen "stream". data will "arrive" into the
route from the intreturn processor element.
*/
bufs.silence (nframes, 0);
} else {
@ -745,7 +749,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
* @a position is used.
*/
int
Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err)
Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
{
ChanCount old_pms = processor_max_streams;
@ -803,8 +807,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
}
if (_control_outs != processor) {
// XXX: do we want to emit the signal here ? change call order.
if (activation_allowed && (processor != _control_outs)) {
processor->activate ();
}
@ -861,6 +864,20 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
_meter->set_display_to_user (_meter_point == MeterCustom);
processor = _meter;
} else if (prop->value() == "monitor") {
if (_monitor_control) {
if (_monitor_control->set_state (node, Stateful::loading_state_version)) {
return false;
} else {
return true;
}
}
_monitor_control.reset (new MonitorProcessor (_session));
_monitor_control->set_state (node, Stateful::loading_state_version);
processor = _monitor_control;
} else if (prop->value() == "amp") {
/* amp always exists */
@ -875,10 +892,25 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
} else if (prop->value() == "intsend") {
processor.reset (new InternalSend (_session, _mute_master, node));
InternalSend* isend = new InternalSend (_session, _mute_master, node);
if (_session.control_out() && (isend->target_id() == _session.control_out()->id())) {
_control_outs.reset (isend);
if (_control_outs->active()) {
_control_outs->set_solo_level (1);
} else {
_control_outs->set_solo_level (0);
}
}
processor.reset (isend);
} else if (prop->value() == "intreturn") {
/* a route only has one internal return. If it exists already
just set its state, and return
*/
if (_intreturn) {
if (_intreturn->set_state (node, Stateful::loading_state_version)) {
return false;
@ -919,7 +951,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
iter = p;
}
return (add_processor (processor, iter) == 0);
return (add_processor (processor, iter, 0, false) == 0);
} else {
error << _("Processor XML node has no type property") << endmsg;
@ -1979,10 +2011,13 @@ Route::_set_state_2X (const XMLNode& node, int version)
_meter.reset (new PeakMeter (_session));
add_processor (_meter, PreFader);
if (_flags & ControlOut) {
if (is_control()) {
/* where we listen to tracks */
_intreturn.reset (new InternalReturn (_session));
add_processor (_intreturn, PreFader);
_monitor_control.reset (new MonitorProcessor (_session));
add_processor (_monitor_control, PostFader);
}
_main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
@ -2351,6 +2386,11 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*a
if (route == _session.control_out()) {
_control_outs = boost::dynamic_pointer_cast<Delivery>(d);
if (_control_outs->active()) {
_control_outs->set_solo_level (1);
} else {
_control_outs->set_solo_level (0);
}
}
/* already listening via the specified IO: do nothing */
@ -2385,11 +2425,8 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*a
if (route == _session.control_out()) {
_control_outs = listener;
/* send to control/listen/monitor bus is active by default */
listener->activate ();
}
add_processor (listener, placement);
return 0;

View File

@ -162,7 +162,6 @@ Send::set_state (const XMLNode& node, int version)
_bitslot = _session.next_send_id();
} else {
sscanf (prop->value().c_str(), "%" PRIu32, &_bitslot);
cerr << this << " scanned " << prop->value() << " to get " << _bitslot << endl;
_session.mark_send_id (_bitslot);
}

View File

@ -483,7 +483,7 @@ Session::set_worst_io_latencies ()
}
void
Session::when_engine_running ()
Session::when_engine_running (bool new_session)
{
string first_physical_output;
@ -644,9 +644,9 @@ Session::when_engine_running ()
BootMessage (_("Setup signal flow and plugins"));
hookup_io ();
hookup_io (new_session);
if (!no_auto_connect()) {
if (new_session && !no_auto_connect()) {
if (_master_out && Config->get_auto_connect_standard_busses()) {
@ -758,7 +758,7 @@ Session::when_engine_running ()
}
void
Session::hookup_io ()
Session::hookup_io (bool new_session)
{
/* stop graph reordering notifications from
causing resorts, etc.
@ -771,7 +771,6 @@ Session::hookup_io ()
/* we delay creating the auditioner till now because
it makes its own connections to ports.
the engine has to be running for this to work.
*/
try {
@ -798,30 +797,34 @@ Session::hookup_io ()
Delivery::reset_panners ();
/* Connect tracks to listen/solo etc. busses XXX generalize this beyond control_out */
if (_control_out) {
/* Connect tracks to monitor/listen bus if there is one.
Note that in an existing session, the internal sends will
already exist, but we want the routes to notice that
they connect to the control out specifically.
*/
if (_control_out) {
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
if ((*x)->is_control()) {
/* relax */
} else if ((*x)->is_master()) {
(*x)->listen_via (_control_out, PostFader, false, false);
for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
if ((*x)->is_control()) {
/* relax */
} else if ((*x)->is_master()) {
/* relax */
} else {
cerr << "Connecting route " << (*x)->name() << " to control outs\n";
(*x)->listen_via (_control_out,
(Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
false, false);
}
}
}
}
}
/* Anyone who cares about input state, wake up and do something */
@ -2078,7 +2081,7 @@ Session::add_routes (RouteList& new_routes, bool save)
if ((*x)->is_control()) {
/* relax */
} else if ((*x)->is_master()) {
(*x)->listen_via (_control_out, PostFader, false, false);
/* relax */
} else {
(*x)->listen_via (_control_out,
(Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
@ -2394,9 +2397,10 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
void
Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
{
/* now figure out if anything that matters is soloed */
/* now figure out if anything that matters is soloed (or is "listening")*/
bool something_soloed = false;
uint32_t listeners = 0;
if (!r) {
r = routes.reader();
@ -2407,14 +2411,20 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
something_soloed = true;
break;
}
if (!(*i)->is_hidden() && (*i)->listening()) {
listeners++;
}
}
cerr << "something soloed ? " << something_soloed << endl;
if (something_soloed != _non_soloed_outs_muted) {
_non_soloed_outs_muted = something_soloed;
SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
}
if (something_soloed != _non_soloed_outs_muted) {
_non_soloed_outs_muted = something_soloed;
SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
}
if (listeners) {
_listen_cnt = listeners;
}
}
boost::shared_ptr<RouteList>
@ -3291,6 +3301,12 @@ Session::cancel_audition ()
bool
Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
{
if (a->is_control()) {
return true;
}
if (b->is_control()) {
return false;
}
return a->order_key(N_("signal")) < b->order_key(N_("signal"));
}

View File

@ -780,8 +780,6 @@ Session::send_full_time_code(nframes_t /*nframes*/)
msg[7] = timecode.seconds;
msg[8] = timecode.frames;
cerr << "MTC: Sending full time code at " << outbound_mtc_timecode_frame << endl;
// Send message at offset 0, sent time is for the start of this cycle
if (_mtc_port->midimsg (msg, sizeof (msg), 0)) {
error << _("Session: could not send full MIDI time code") << endmsg;

View File

@ -339,7 +339,7 @@ Session::second_stage_init (bool new_session)
_engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
try {
when_engine_running();
when_engine_running (new_session);
}
/* handle this one in a different way than all others, so that its clear what happened */
@ -1046,6 +1046,12 @@ Session::state(bool full_state)
RouteList public_order (*r);
public_order.sort (cmp);
/* the sort should have put control outs first */
if (_control_out) {
assert (_control_out == public_order.front());
}
for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) {
if (!(*i)->is_hidden()) {
if (full_state) {

View File

@ -123,6 +123,7 @@ libardour_sources = [
'midi_track.cc',
'midi_ui.cc',
'mix.cc',
'monitor_processor.cc',
'mtc_slave.cc',
'mtdm.cc',
'mute_master.cc',

View File

@ -34,6 +34,7 @@ gtkmm2ext_sources = [
'gtk_ui.cc',
'idle_adjustment.cc',
'keyboard.cc',
'motionfeedback.cc',
'pixfader.cc',
'pixscroller.cc',
'popup.cc',