View Issue Details

IDProjectCategoryView StatusLast Update
0004199ardourfeaturespublic2011-09-29 21:25
Reportergrabner Assigned Topaul  
PrioritynormalSeverityfeatureReproducibilityalways
Status assignedResolutionopen 
Product Version2.8.11 
Target Version2.8.12 
Summary0004199: patch: touch automation with Behringer BCF 2000
DescriptionThe Behringer BCF 2000 device doesn't have touch sensitive faders and can therefore not be used immediately in touch automation mode. The attached patch provides a workaround (limited to gain automation): any of the buttons of the BCF 2000 can be used to start/stop touching (just as pressing/releasing the left mouse button on the fader image on screen). See below for more details.
Additional InformationThis patch does the following:
*) maps a MIDI controller to start/stop touching (whether the button acts in toggle or on/off mode depends on the BCF 2000 configuration, see the device manual for details)
*) provides a learning mode to select the MIDI controller to be used for touching: hold the SHIFT and CTRL keys, press the middle mouse button on the fader image, and press the button on the BCF 2000
*) IMO, 0.5 should be added to the controller value before converting the number to a byte in "midicontrollable.c" to avoid roundoff errors. However, this doesn't affect the touch feature.
TagsNo tags attached.

Activities

2011-07-18 22:52

 

ardour-midi-touch.patch (8,778 bytes)   
diff -aur ardour-2.8.11.orig/gtk2_ardour/gain_meter.cc ardour-2.8.11/gtk2_ardour/gain_meter.cc
--- ardour-2.8.11.orig/gtk2_ardour/gain_meter.cc	2010-04-20 19:28:37.000000000 +0200
+++ ardour-2.8.11/gtk2_ardour/gain_meter.cc	2011-07-18 22:36:49.000000000 +0200
@@ -89,11 +89,11 @@
 	if (horizontal) {
 		gain_slider = manage (new HSliderController (pix,
 							     &gain_adjustment,
-							     false));
+							     NULL));
 	} else {
 		gain_slider = manage (new VSliderController (pix,
 							     &gain_adjustment,
-							     false));
+							     NULL));
 	}
 
 	level_meter = new LevelMeter(_session);
@@ -168,6 +168,7 @@
 
 	level_meter->set_io (_io);
 	gain_slider->set_controllable (&_io->gain_control());
+	gain_slider->set_touch_controllable (&_io->touch_control());
 
 	boost::shared_ptr<Route> r;
 	
diff -aur ardour-2.8.11.orig/libs/ardour/ardour/io.h ardour-2.8.11/libs/ardour/ardour/io.h
--- ardour-2.8.11.orig/libs/ardour/ardour/io.h	2010-04-22 18:03:25.000000000 +0200
+++ ardour-2.8.11/libs/ardour/ardour/io.h	2011-07-18 22:36:49.000000000 +0200
@@ -196,6 +196,14 @@
 		return _gain_control;
 	}
 	
+	PBD::Controllable& touch_control() {
+		return _touch_control;
+	}
+
+	const PBD::Controllable& touch_control() const {
+		return _touch_control;
+	}
+	
 	/* Peak metering */
 
 	float peak_input_power (uint32_t n) { 
@@ -273,6 +281,8 @@
 	void start_gain_touch ();
 	void end_gain_touch ();
 
+	bool gain_touching() const { return _gain_automation_curve.touching(); }
+
 	void start_pan_touch (uint32_t which);
 	void end_pan_touch (uint32_t which);
 
@@ -329,7 +339,17 @@
 	    IO& io;
 	};
 
+	struct TouchControllable : public PBD::Controllable {
+	    TouchControllable (std::string name, IO& i) : Controllable (name), io (i) {}
+	 
+	    void set_value (float val);
+		  float get_value (void) const;
+   
+	    IO& io;
+	};
+
 	GainControllable _gain_control;
+	TouchControllable _touch_control;
 
 	nframes_t last_automation_snapshot;
 	static nframes_t _automation_interval;
diff -aur ardour-2.8.11.orig/libs/ardour/io.cc ardour-2.8.11/libs/ardour/io.cc
--- ardour-2.8.11.orig/libs/ardour/io.cc	2010-04-22 20:33:41.000000000 +0200
+++ ardour-2.8.11/libs/ardour/io.cc	2011-07-18 22:36:49.000000000 +0200
@@ -142,6 +142,7 @@
 	  _name (name),
 	  _default_type(default_type),
 	  _gain_control (X_("gaincontrol"), *this),
+	  _touch_control (X_("touchcontrol"), *this),
 	  _gain_automation_curve (0.0, 2.0, 1.0),
 	  _input_minimum (input_min),
 	  _input_maximum (input_max),
@@ -179,12 +180,14 @@
 	CycleStart.connect (mem_fun (*this, &IO::cycle_start));
 
 	_session.add_controllable (&_gain_control);
+	_session.add_controllable (&_touch_control);
 }
 
 IO::IO (Session& s, const XMLNode& node, DataType dt)
 	: _session (s),
 	  _default_type (dt),
 	  _gain_control (X_("gaincontrol"), *this),
+	  _touch_control (X_("touchcontrol"), *this),
 	  _gain_automation_curve (0, 0, 0) // all reset in set_state()
 {
 	_panner = 0;
@@ -214,6 +217,7 @@
 	CycleStart.connect (mem_fun (*this, &IO::cycle_start));
 
 	_session.add_controllable (&_gain_control);
+	_session.add_controllable (&_touch_control);
 }
 
 IO::~IO ()
@@ -1542,6 +1546,7 @@
 
 	node->add_child_nocopy (_panner->state (full_state));
 	node->add_child_nocopy (_gain_control.get_state ());
+	node->add_child_nocopy (_touch_control.get_state ());
 
 	snprintf (buf, sizeof(buf), "%2.12f", gain());
 	node->add_property ("gain", buf);
@@ -1632,8 +1637,13 @@
 		}
 
 		if ((*iter)->name() == X_("controllable")) {
-			if ((prop = (*iter)->property("name")) != 0 && prop->value() == "gaincontrol") {
-				_gain_control.set_state (**iter);
+			if ((prop = (*iter)->property("name")) != 0) {
+				if (prop->value() == "gaincontrol") {
+					_gain_control.set_state (**iter);
+				}
+				else if (prop->value() == "touchcontrol") {
+					_touch_control.set_state (**iter);
+				}
 			}
 		}
 	}
@@ -2484,6 +2494,21 @@
 }
 
 void
+IO::TouchControllable::set_value (float val)
+{
+	if (val)
+		io.start_gain_touch();
+	else
+		io.end_gain_touch();
+}
+
+float
+IO::TouchControllable::get_value (void) const
+{
+	return io.gain_touching() ? 1.0f : 0.0f;
+}
+
+void
 IO::reset_peak_meters ()
 {
 	uint32_t limit = max (_ninputs, _noutputs);
@@ -2669,6 +2694,7 @@
 void
 IO::start_gain_touch ()
 {
+	set_gain (_effective_gain, this);
 	_gain_automation_curve.start_touch (_session.transport_frame());
 }
 
diff -aur ardour-2.8.11.orig/libs/gtkmm2ext/binding_proxy.cc ardour-2.8.11/libs/gtkmm2ext/binding_proxy.cc
--- ardour-2.8.11.orig/libs/gtkmm2ext/binding_proxy.cc	2009-02-24 13:37:45.000000000 +0100
+++ ardour-2.8.11/libs/gtkmm2ext/binding_proxy.cc	2011-07-18 22:36:49.000000000 +0200
@@ -80,7 +80,7 @@
 bool
 BindingProxy::button_press_handler (GdkEventButton *ev)
 {
-	if ((ev->state & bind_statemask) && ev->button == bind_button) { 
+	if ((controllable != NULL) && ((ev->state & bind_statemask) == bind_statemask) && ev->button == bind_button) { 
 		if (Controllable::StartLearning (controllable)) {
 			string prompt = _("operate controller now");
 			if (prompter == 0) {
diff -aur ardour-2.8.11.orig/libs/gtkmm2ext/gtkmm2ext/slider_controller.h ardour-2.8.11/libs/gtkmm2ext/gtkmm2ext/slider_controller.h
--- ardour-2.8.11.orig/libs/gtkmm2ext/gtkmm2ext/slider_controller.h	2009-02-24 13:37:45.000000000 +0100
+++ ardour-2.8.11/libs/gtkmm2ext/gtkmm2ext/slider_controller.h	2011-07-18 22:36:49.000000000 +0200
@@ -52,8 +52,11 @@
 
 	void set_controllable (PBD::Controllable* c) { binding_proxy.set_controllable (c); }
 
+	void set_touch_controllable (PBD::Controllable* c) { binding_proxy_touch.set_controllable (c); }
+
   protected:
 	BindingProxy binding_proxy;
+	BindingProxy binding_proxy_touch;
 	Glib::RefPtr<Gdk::Pixbuf> slider;
 	Glib::RefPtr<Gdk::Pixbuf> rail;
 	Gtk::SpinButton     spin;
diff -aur ardour-2.8.11.orig/libs/gtkmm2ext/slider_controller.cc ardour-2.8.11/libs/gtkmm2ext/slider_controller.cc
--- ardour-2.8.11.orig/libs/gtkmm2ext/slider_controller.cc	2009-02-24 13:37:45.000000000 +0100
+++ ardour-2.8.11/libs/gtkmm2ext/slider_controller.cc	2011-07-18 22:36:49.000000000 +0200
@@ -35,12 +35,14 @@
 
 	: PixFader (image, *adj, orientation),
 	  binding_proxy (c),
+	  binding_proxy_touch (NULL),
 	  spin (*adj, 0, 2)
 {			  
 	spin.set_name ("SliderControllerValue");
 	spin.set_size_request (70,-1); // should be based on font size somehow
 	spin.set_numeric (true);
 	spin.set_snap_to_ticks (false);
+	binding_proxy_touch.set_bind_button_state (2, Gdk::SHIFT_MASK | Gdk::CONTROL_MASK);
 }
 
 void
@@ -52,6 +54,9 @@
 bool 
 SliderController::on_button_press_event (GdkEventButton *ev) 
 {
+	if (binding_proxy_touch.button_press_handler (ev)) {
+		return true;
+	}
 	if (binding_proxy.button_press_handler (ev)) {
 		return true;
 	}
diff -aur ardour-2.8.11.orig/libs/surfaces/generic_midi/midicontrollable.cc ardour-2.8.11/libs/surfaces/generic_midi/midicontrollable.cc
--- ardour-2.8.11.orig/libs/surfaces/generic_midi/midicontrollable.cc	2009-02-24 13:38:19.000000000 +0100
+++ ardour-2.8.11/libs/surfaces/generic_midi/midicontrollable.cc	2011-07-18 22:36:49.000000000 +0200
@@ -141,7 +141,7 @@
 		}
 	}
 
-	last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights
+	last_value = (MIDI::byte) (controllable.get_value() * 127.0 + 0.5); // to prevent feedback fights
 }
 
 void
@@ -158,7 +158,7 @@
 			}
 		}
 
-		last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights
+		last_value = (MIDI::byte) (controllable.get_value() * 127.0 + 0.5); // to prevent feedback fights
 	}
 }
 
@@ -169,7 +169,7 @@
 
 	if (!bistate) {
 		controllable.set_value (msg/127.0);
-		last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights
+		last_value = (MIDI::byte) (controllable.get_value() * 127.0 + 0.5); // to prevent feedback fights
 	} 
 }
 
@@ -181,7 +181,7 @@
 	/* XXX gack - get rid of assumption about typeof pitchbend_t */
 
 	controllable.set_value ((pb/(float) SHRT_MAX));
-	last_value = (MIDI::byte) (controllable.get_value() * 127.0); // to prevent feedback fights
+	last_value = (MIDI::byte) (controllable.get_value() * 127.0 + 0.5); // to prevent feedback fights
 }			
 
 void
@@ -297,7 +297,7 @@
 
 	msg[0] = (control_type & 0xF0) | (control_channel & 0xF); 
 	msg[1] = control_additional;
-	msg[2] = (byte) (controllable.get_value() * 127.0f);
+	msg[2] = (byte) (controllable.get_value() * 127.0f + 0.5f);
 
 	_port.write (msg, 3);
 }
@@ -307,7 +307,7 @@
 {
 	if (control_type != none && feedback && bufsize > 2) {
 
-		MIDI::byte gm = (MIDI::byte) (controllable.get_value() * 127.0);
+		MIDI::byte gm = (MIDI::byte) (controllable.get_value() * 127.0 + 0.5);
 		
 		if (gm != last_value) {
 			*buf++ = (0xF0 & control_type) | (0xF & control_channel);
ardour-midi-touch.patch (8,778 bytes)   

globin

2011-09-28 21:45

reporter   ~0011615

After discussion with Seablade came up with this idea:
defining a button on the BCF2000 to change from read to write when pressed on the next fader that gets moved and reverts if loosed

seablade

2011-09-28 21:49

manager   ~0011616

I am not convinced this is the best way to approach this issue, for multiple reasons, not the least of which is putting a bandaid on less than capable hardware of course.

That being said, the possible larger thing to me is that conceptually I think this might be the wrong way to address it, vs implementing an ability to have a combined momentary and latching button to switch to WRITE mode, which would more accurately reflect what is happening and likely would be slightly less intrusive of a patch. My suggestion would be to have, preferably per track, a bindable button that if pressed and released(NoteON/NoteOFF received within .25 seconds of each other) would either cycle through the available automation modes on the track, or at the very least just activate write mode, but alternatively if pressed and held would put the track in WRITE mode in a non-latching fashion so that when released it goes back into whatever the previous mode was.

I personally have a preference of one button per track, though I could be swayed to one button for the entire surface.

At any rate assigning to Paul in case he wants to take a look at the patch. Keep in mind that at this time A2 is feature frozen so there is no guarantee about this progressing, submitting an updated patch for A3 is advisable.

   Seablade

grabner

2011-09-29 21:25

reporter   ~0011622

I think one button for the entire surface won't work. Consider the following cases:

*) You want to edit the gain curves of several tracks at once. While the fact that you need to operate both a button and a fader to edit a single track is certainly a limitation, you could for example edit three adjacent drum tracks by pressing the buttons with the left hand while moving the faders with the right hand to increase or decrease the overall volume of these tracks.

*) You have a slightly increasing gain on one track, but are not satisfied with the rate of the increase. If there is only a single button for the entire surface, there is no way to tell the system which fader's motor to switch off. And while the fader is replaying the (incorrect) gain curve, you won't be able to move the fader by hand unless applying considerable force, which doesn't feel healthy to the BCF2000 (and I'm not sure whether the device reports manual changes at all while the motor is switched on). Moreover, if one button is assigned per track, you could also use the "motor off" feature of the BCF2000, which immediately switches off a fader's motor when pressing a particular button (according to the manual, I didn't try this so far).

I chose mapping a button to touch on/off events since I assume this is the way it is done by hardware which supports "true" touch automation (notifying the software when the user touches and releases a fader). However, this is just a guess since I don't have such hardware.

Markus

Issue History

Date Modified Username Field Change
2011-07-18 22:52 grabner New Issue
2011-07-18 22:52 grabner File Added: ardour-midi-touch.patch
2011-07-18 23:01 cth103 cost => 0.00
2011-07-18 23:01 cth103 Target Version => 2.8.12
2011-09-28 21:45 globin Note Added: 0011615
2011-09-28 21:49 seablade Note Added: 0011616
2011-09-28 21:49 seablade Status new => assigned
2011-09-28 21:49 seablade Assigned To => paul
2011-09-29 21:25 grabner Note Added: 0011622