add initial implementation of explicit monitor (input|disk) control. some behaviour to be worked out, still

git-svn-id: svn://localhost/ardour2/branches/3.0@10256 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2011-10-20 18:50:29 +00:00
parent 49b459f8d3
commit 3764eedca0
12 changed files with 272 additions and 41 deletions

View file

@ -148,6 +148,27 @@ style "mixer_track_rec_enable_button_active" = "track_rec_enable_button_active"
ythickness = 0
}
style "monitor_input_button" = "small_button"
{
fg[NORMAL] = darker(@@COLPREFIX@_fg)
fg[PRELIGHT] = darker(@@COLPREFIX@_fg)
bg[NORMAL] = mix(0.1,@@COLPREFIX@_bright_indicator,darker(@@COLPREFIX@_bg))
bg[PRELIGHT] = mix(0.1,@@COLPREFIX@_bright_indicator,darker(@@COLPREFIX@_bg))
fg[ACTIVE] = @@COLPREFIX@_fg
bg[ACTIVE] = @@COLPREFIX@_bright_indicator
}
style "monitor_disk_button" = "small_button"
{
fg[NORMAL] = darker(@@COLPREFIX@_fg)
fg[PRELIGHT] = darker(@@COLPREFIX@_fg)
bg[NORMAL] = mix(0.1,@@COLPREFIX@_bright_indicator,darker(@@COLPREFIX@_bg))
bg[PRELIGHT] = mix(0.1,@@COLPREFIX@_bright_indicator,darker(@@COLPREFIX@_bg))
fg[ACTIVE] = @@COLPREFIX@_fg
bg[ACTIVE] = @@COLPREFIX@_bright_indicator
}
style "solo_button" = "small_button"
{
@ -247,6 +268,18 @@ style "mixer_solo_button_active" = "solo_button_active"
ythickness = 0
}
style "mixer_monitor_input_button" = "monitor_input_button"
{
xthickness = 0
ythickness = 0
}
style "mixer_monitor_disk_button" = "monitor_disk_button"
{
xthickness = 0
ythickness = 0
}
style "monitor_opt_button" = "small_button"
{
bg[NORMAL] = mix(0.1,@@COLPREFIX@_not_so_bright_indicator,@@COLPREFIX@_bg)

View file

@ -80,6 +80,8 @@ widget "*MixerSoloButton" style:highest "mixer_solo_button"
widget "*MixerSoloButton-alternate" style:highest "mixer_solo_button_alternate"
widget "*MixerSoloButton-alternate2" style:highest "mixer_solo_button_alternate2"
widget "*MixerSoloButton-active" style:highest "mixer_solo_button_active"
widget "*MixerMonitorInputButton" style:highest "mixer_monitor_input_button"
widget "*MixerMonitorDiskButton" style:highest "mixer_monitor_disk_button"
widget "*TrackLoopButton*" style:highest "track_loop_button"
widget "*PanAutomationLineSelector*" style:highest "multiline_combo"
widget "*EditorTimeButton*" style:highest "time_button"

View file

@ -87,7 +87,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
, panners (sess)
, button_table (3, 1)
, solo_led_table (2, 2)
, middle_button_table (1, 2)
, middle_button_table (2, 2)
, bottom_button_table (1, 2)
, meter_point_label (_("pre"))
, midi_input_enable_button (0)
@ -112,7 +112,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt
, gpm (sess, 250)
, panners (sess)
, button_table (3, 1)
, middle_button_table (1, 2)
, middle_button_table (2, 2)
, bottom_button_table (1, 2)
, meter_point_label (_("pre"))
, midi_input_enable_button (0)
@ -184,6 +184,9 @@ MixerStrip::init ()
mute_button->set_name ("MixerMuteButton");
solo_button->set_name ("MixerSoloButton");
monitor_input_button->set_name ("MixerMonitorInputButton");
monitor_disk_button->set_name ("MixerMonitorInputButton");
solo_isolated_led = manage (new LED);
solo_isolated_led->show ();
solo_isolated_led->set_diameter (6);
@ -235,6 +238,8 @@ MixerStrip::init ()
middle_button_table.set_spacings (0);
middle_button_table.attach (*mute_button, 0, 1, 0, 1);
middle_button_table.attach (*solo_button, 1, 2, 0, 1);
middle_button_table.attach (*monitor_input_button, 0, 1, 1, 2);
middle_button_table.attach (*monitor_disk_button, 1, 2, 1, 2);
bottom_button_table.set_col_spacings (0);
bottom_button_table.set_homogeneous (true);
@ -396,6 +401,14 @@ MixerStrip::set_route (boost::shared_ptr<Route> rt)
global_vpacker.pack_start (*spacer, false, false);
}
if (is_track()) {
monitor_input_button->show ();
monitor_disk_button->show ();
} else {
monitor_input_button->hide();
monitor_disk_button->hide ();
}
if (is_midi_track()) {
if (midi_input_enable_button == 0) {
Image* img = manage (new Image (get_icon (X_("midi_socket_small"))));
@ -1643,6 +1656,8 @@ MixerStrip::drop_send ()
rec_enable_button->set_sensitive (true);
solo_isolated_led->set_sensitive (true);
solo_safe_led->set_sensitive (true);
monitor_input_button->set_sensitive (true);
monitor_disk_button->set_sensitive (true);
}
void
@ -1679,6 +1694,8 @@ MixerStrip::show_send (boost::shared_ptr<Send> send)
rec_enable_button->set_sensitive (false);
solo_isolated_led->set_sensitive (false);
solo_safe_led->set_sensitive (false);
monitor_input_button->set_sensitive (false);
monitor_disk_button->set_sensitive (false);
if (boost::dynamic_pointer_cast<InternalSend>(send)) {
output_button.set_sensitive (false);
@ -1714,6 +1731,8 @@ MixerStrip::set_button_names ()
case Wide:
rec_enable_button_label.set_text (_("Rec"));
mute_button_label.set_text (_("Mute"));
monitor_input_button_label.set_text (_("In"));
monitor_disk_button_label.set_text (_("Disk"));
if (_route && _route->solo_safe()) {
solo_button_label.set_text (X_("!"));
} else {
@ -1735,6 +1754,8 @@ MixerStrip::set_button_names ()
default:
rec_enable_button_label.set_text (_("R"));
mute_button_label.set_text (_("M"));
monitor_input_button_label.set_text (_("I"));
monitor_disk_button_label.set_text (_("D"));
if (_route && _route->solo_safe()) {
solo_button_label.set_text (X_("!"));
if (!Config->get_solo_control_is_listen_control()) {

View file

@ -137,6 +137,22 @@ RouteUI::init ()
// show_sends_button->set_self_managed (true);
UI::instance()->set_tip (show_sends_button, _("make mixer strips show sends to this bus"), "");
monitor_input_button = manage (new BindableToggleButton ());
// monitor_input_button->set_self_managed (true);
monitor_input_button->set_name ("MonitorInputButton");
monitor_input_button->add (monitor_input_button_label);
monitor_input_button_label.show ();
UI::instance()->set_tip (monitor_input_button, _("Monitor input"), "");
monitor_input_button->set_no_show_all (true);
monitor_disk_button = manage (new BindableToggleButton ());
// monitor_disk_button->set_self_managed (true);
monitor_disk_button->set_name ("MonitorDiskButton");
monitor_disk_button->add (monitor_disk_button_label);
monitor_disk_button_label.show ();
UI::instance()->set_tip (monitor_disk_button, _("Monitor playback"), "");
monitor_disk_button->set_no_show_all (true);
_session->SoloChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::solo_changed_so_update_mute, this), gui_context());
_session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::check_rec_enable_sensitivity, this), gui_context());
_session->RecordStateChanged.connect (_session_connections, invalidator (*this), boost::bind (&RouteUI::session_rec_enable_changed, this), gui_context());
@ -153,6 +169,12 @@ RouteUI::init ()
solo_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::solo_release), false);
mute_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::mute_press), false);
mute_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::mute_release), false);
monitor_input_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_input_press), false);
monitor_input_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_input_release), false);
monitor_disk_button->signal_button_press_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_disk_press), false);
monitor_disk_button->signal_button_release_event().connect (sigc::mem_fun(*this, &RouteUI::monitor_disk_release), false);
}
void
@ -211,7 +233,7 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
t->RecordEnableChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
rec_enable_button->show();
rec_enable_button->set_controllable (t->rec_enable_control());
@ -224,6 +246,11 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
}
if (is_track()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
t->MonitoringChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::monitoring_changed, this), gui_context());
}
mute_button->unset_flags (Gtk::CAN_FOCUS);
solo_button->unset_flags (Gtk::CAN_FOCUS);
@ -555,6 +582,96 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
return true;
}
void
RouteUI::monitoring_changed ()
{
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
if (!t) {
return;
}
MonitorChoice mc = t->monitoring();
if (mc & MonitorInput) {
monitor_input_button->set_active (true);
} else {
monitor_input_button->set_active (false);
}
if (mc & MonitorDisk) {
monitor_disk_button->set_active (true);
} else {
monitor_disk_button->set_active (false);
}
}
bool
RouteUI::monitor_input_press(GdkEventButton* ev)
{
return true;
}
bool
RouteUI::monitor_input_release(GdkEventButton* ev)
{
return monitor_release (ev, MonitorInput);
}
bool
RouteUI::monitor_disk_press (GdkEventButton* ev)
{
return true;
}
bool
RouteUI::monitor_disk_release (GdkEventButton* ev)
{
return monitor_release (ev, MonitorDisk);
}
bool
RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
{
if (ev->button != 1) {
return false;
}
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
if (!t) {
return true;
}
MonitorChoice mc;
boost::shared_ptr<RouteList> rl;
/* XXX for now, monitoring choices are orthogonal. cue monitoring
will follow in 3.X but requires mixing the input and playback (disk)
signal together, which requires yet more buffers.
*/
if (t->monitoring() & monitor_choice) {
mc = MonitorChoice (t->monitoring() & ~monitor_choice);
} else {
/* this line will change when the options are non-orthogonal */
// mc = MonitorChoice (t->monitoring() | monitor_choice);
mc = monitor_choice;
}
if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
rl = _session->get_routes ();
} else {
rl.reset (new RouteList);
rl->push_back (route());
}
_session->set_monitoring (rl, mc, Session::rt_cleanup, true);
return true;
}
void
RouteUI::build_record_menu ()
{

View file

@ -94,6 +94,8 @@ class RouteUI : public virtual AxisView
BindableToggleButton* solo_button;
BindableToggleButton* rec_enable_button; /* audio tracks */
BindableToggleButton* show_sends_button; /* busses */
BindableToggleButton* monitor_input_button;
BindableToggleButton* monitor_disk_button;
LED* solo_safe_led;
LED* solo_isolated_led;
@ -101,6 +103,8 @@ class RouteUI : public virtual AxisView
Gtk::Label solo_button_label;
Gtk::Label mute_button_label;
Gtk::Label rec_enable_button_label;
Gtk::Label monitor_input_button_label;
Gtk::Label monitor_disk_button_label;
void send_blink (bool);
sigc::connection send_blink_connection;
@ -121,6 +125,13 @@ class RouteUI : public virtual AxisView
bool show_sends_press(GdkEventButton*);
bool show_sends_release(GdkEventButton*);
bool monitor_release(GdkEventButton*, ARDOUR::MonitorChoice);
bool monitor_input_press(GdkEventButton*);
bool monitor_input_release(GdkEventButton*);
bool monitor_disk_press(GdkEventButton*);
bool monitor_disk_release(GdkEventButton*);
void monitoring_changed ();
void step_gain_up ();
void step_gain_down ();
void page_gain_up ();

View file

@ -37,6 +37,7 @@
#include <glibmm/thread.h>
#include "pbd/error.h"
#include "pbd/event_loop.h"
#include "pbd/rcu.h"
#include "pbd/statefuldestructible.h"
#include "pbd/signals.h"
@ -622,6 +623,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void set_record_enabled (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
void set_solo_isolated (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
void set_exclusive_input_active (boost::shared_ptr<Route> rt, bool others_on);
void set_monitoring (boost::shared_ptr<RouteList>, MonitorChoice, SessionEvent::RTeventCallback after = rt_cleanup, bool group_override = false);
PBD::Signal1<void,bool> SoloActive;
PBD::Signal0<void> SoloChanged;
@ -1454,10 +1456,16 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
static int ask_about_playlist_deletion (boost::shared_ptr<Playlist>);
/* realtime "apply to set of routes" operations */
SessionEvent* get_rt_event (
boost::shared_ptr<RouteList> rl, bool yn,
SessionEvent::RTeventCallback after, bool group_override,
void (Session::*method) (boost::shared_ptr<RouteList>, bool, bool));
template<typename T> SessionEvent*
get_rt_event (boost::shared_ptr<RouteList> rl, T targ, SessionEvent::RTeventCallback after, bool group_override,
void (Session::*method) (boost::shared_ptr<RouteList>, T, bool)) {
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = boost::bind (method, this, rl, targ, group_override);
ev->rt_return = after;
ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
return ev;
}
void rt_set_solo (boost::shared_ptr<RouteList>, bool yn, bool group_override);
void rt_set_just_one_solo (boost::shared_ptr<RouteList>, bool yn, bool /* ignored*/ );
@ -1465,6 +1473,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void rt_set_listen (boost::shared_ptr<RouteList>, bool yn, bool group_override);
void rt_set_solo_isolated (boost::shared_ptr<RouteList>, bool yn, bool group_override);
void rt_set_record_enabled (boost::shared_ptr<RouteList>, bool yn, bool group_override);
void rt_set_monitoring (boost::shared_ptr<RouteList>, MonitorChoice, bool group_override);
/** temporary list of Diskstreams used only during load of 2.X sessions */
std::list<boost::shared_ptr<Diskstream> > _diskstreams_2X;

View file

@ -48,6 +48,10 @@ class Track : public Route, public PublicDiskstream
virtual bool can_use_mode (TrackMode /*m*/, bool& /*bounce_required*/) { return false; }
PBD::Signal0<void> TrackModeChanged;
virtual void set_monitoring (MonitorChoice);
MonitorChoice monitoring() const { return _monitoring; }
PBD::Signal0<void> MonitoringChanged;
virtual int no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
bool state_changing, bool can_record);
@ -161,9 +165,10 @@ class Track : public Route, public PublicDiskstream
virtual XMLNode& state (bool full) = 0;
boost::shared_ptr<Diskstream> _diskstream;
MeterPoint _saved_meter_point;
TrackMode _mode;
bool _needs_butler;
MeterPoint _saved_meter_point;
TrackMode _mode;
bool _needs_butler;
MonitorChoice _monitoring;
//private: (FIXME)
struct FreezeRecordProcessorInfo {

View file

@ -358,6 +358,13 @@ namespace ARDOUR {
ExternalMonitoring
};
enum MonitorChoice {
MonitorAuto = 0,
MonitorInput = 0x1,
MonitorDisk = 0x2,
MonitorCue = 0x4,
};
enum PFLPosition {
/** PFL signals come from before pre-fader processors */
PFLFromBeforeProcessors,

View file

@ -403,12 +403,16 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
_input->process_input (_meter, start_frame, end_frame, nframes);
}
if (diskstream->record_enabled() && !can_record && !_session.config.get_auto_input()) {
if ((_monitoring & MonitorInput) ||
(!(_monitoring & MonitorDisk) &&
(diskstream->record_enabled() && !can_record && !_session.config.get_auto_input()))) {
/* not actually recording, but we want to hear the input material anyway,
at least potentially (depending on monitoring options)
*/
cerr << name() << " do the passthru thing with monitoring = " << enum_2_string (_monitoring) << endl;
passthru (start_frame, end_frame, nframes, false);
} else if ((b = diskstream->playback_buffer(0)) != 0) {
@ -734,3 +738,5 @@ AudioTrack::bounceable () const
{
return n_inputs().n_audio() >= n_outputs().n_audio();
}

View file

@ -66,6 +66,7 @@ setup_enum_writer ()
RegionPoint _RegionPoint;
Placement _Placement;
MonitorModel _MonitorModel;
MonitorChoice _MonitorChoice;
PFLPosition _PFLPosition;
AFLPosition _AFLPosition;
RemoteModel _RemoteModel;
@ -228,6 +229,12 @@ setup_enum_writer ()
REGISTER_ENUM (ExternalMonitoring);
REGISTER (_MonitorModel);
REGISTER_ENUM (MonitorInput);
REGISTER_ENUM (MonitorDisk);
REGISTER_ENUM (MonitorAuto);
REGISTER_ENUM (MonitorCue);
REGISTER_BITS (_MonitorChoice);
REGISTER_ENUM (PFLFromBeforeProcessors);
REGISTER_ENUM (PFLFromAfterProcessors);
REGISTER (_PFLPosition);

View file

@ -32,16 +32,25 @@ using namespace PBD;
using namespace ARDOUR;
using namespace Glib;
SessionEvent*
Session::get_rt_event (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, bool group_override,
void (Session::*method) (boost::shared_ptr<RouteList>, bool, bool))
void
Session::set_monitoring (boost::shared_ptr<RouteList> rl, MonitorChoice mc, SessionEvent::RTeventCallback after, bool group_override)
{
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = boost::bind (method, this, rl, yn, group_override);
ev->rt_return = after;
ev->event_loop = EventLoop::get_event_loop_for_thread ();
queue_event (get_rt_event (rl, mc, after, group_override, &Session::rt_set_monitoring));
}
return ev;
void
Session::rt_set_monitoring (boost::shared_ptr<RouteList> rl, MonitorChoice mc, bool /* group_override */)
{
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_hidden()) {
boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
if (t) {
t->set_monitoring (mc);
}
}
}
set_dirty();
}
void

View file

@ -43,6 +43,7 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data
: Route (sess, name, flag, default_type)
, _saved_meter_point (_meter_point)
, _mode (mode)
, _monitoring (MonitorAuto)
, _rec_enable_control (new RecEnableControllable(*this))
{
_freeze_record.state = NoFreeze;
@ -640,36 +641,31 @@ Track::adjust_capture_buffering ()
bool
Track::send_silence () const
{
/*
ADATs work in a strange way..
they monitor input always when stopped.and auto-input is engaged.
Other machines switch to input on stop if the track is record enabled,
regardless of the auto input setting (auto input only changes the
monitoring state when the transport is rolling)
*/
bool send_silence;
if (!Config->get_tape_machine_mode()) {
/*
ADATs work in a strange way..
they monitor input always when stopped.and auto-input is engaged.
if (Config->get_tape_machine_mode()) {
/* ADATs work in a strange way..
they monitor input always when stopped.and auto-input is engaged.
*/
if ((Config->get_monitoring_model() == SoftwareMonitoring)
&& (_session.config.get_auto_input () || _diskstream->record_enabled())) {
send_silence = false;
&& ((_monitoring & MonitorInput) || (_diskstream->record_enabled()))) {
send_silence = false;
} else {
send_silence = true;
}
} else {
/*
Other machines switch to input on stop if the track is record enabled,
regardless of the auto input setting (auto input only changes the
monitoring state when the transport is rolling)
/* Other machines switch to input on stop if the track is record enabled,
regardless of the auto input setting (auto input only changes the
monitoring state when the transport is rolling)
*/
if ((Config->get_monitoring_model() == SoftwareMonitoring)
&& _diskstream->record_enabled()) {
&& (!(_monitoring & MonitorDisk) && (_session.config.get_auto_input () || _diskstream->record_enabled()))) {
send_silence = false;
} else {
send_silence = true;
@ -732,6 +728,14 @@ Track::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
}
return nframes;
return nframes;
}
void
Track::set_monitoring (MonitorChoice mc)
{
if (mc != _monitoring) {
_monitoring = mc;
MonitoringChanged (); /* EMIT SIGNAL */
}
}