1 /*
2 * Copyright (C) 2015-2018 Ben Loftis <ben@harrisonconsoles.com>
3 * Copyright (C) 2015-2018 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2016-2019 Robin Gareus <robin@gareus.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <cstdlib>
22 #include <sstream>
23 #include <algorithm>
24
25 #include <stdint.h>
26
27 #include <glibmm/fileutils.h>
28 #include <glibmm/miscutils.h>
29
30 #include "pbd/error.h"
31 #include "pbd/failed_constructor.h"
32 #include "pbd/file_utils.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/compose.h"
35 #include "pbd/xml++.h"
36
37 #include "midi++/port.h"
38
39 #include "ardour/async_midi_port.h"
40 #include "ardour/audioengine.h"
41 #include "ardour/amp.h"
42 #include "ardour/bundle.h"
43 #include "ardour/debug.h"
44 #include "ardour/filesystem_paths.h"
45 #include "ardour/midi_port.h"
46 #include "ardour/midiport_manager.h"
47 #include "ardour/monitor_processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/rc_configuration.h"
50 #include "ardour/record_enable_control.h"
51 #include "ardour/stripable.h"
52 #include "ardour/session.h"
53 #include "ardour/session_configuration.h"
54 #include "ardour/track.h"
55
56 #include "faderport.h"
57
58 using namespace ARDOUR;
59 using namespace ArdourSurface;
60 using namespace PBD;
61 using namespace Glib;
62 using namespace std;
63
64 #include "pbd/i18n.h"
65
66 #include "pbd/abstract_ui.cc" // instantiate template
67
FaderPort(Session & s)68 FaderPort::FaderPort (Session& s)
69 : ControlProtocol (s, _("PreSonus FaderPort"))
70 , AbstractUI<FaderPortRequest> (name())
71 , gui (0)
72 , connection_state (ConnectionState (0))
73 , _device_active (false)
74 , fader_msb (0)
75 , fader_lsb (0)
76 , fader_is_touched (false)
77 , button_state (ButtonState (0))
78 , blink_state (false)
79 , rec_enable_state (false)
80 {
81 last_encoder_time = 0;
82
83 boost::shared_ptr<ARDOUR::Port> inp;
84 boost::shared_ptr<ARDOUR::Port> outp;
85
86 inp = AudioEngine::instance()->register_input_port (DataType::MIDI, "Faderport Recv", true);
87 outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "Faderport Send", true);
88
89 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
90 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
91
92 if (_input_port == 0 || _output_port == 0) {
93 throw failed_constructor();
94 }
95
96 _input_bundle.reset (new ARDOUR::Bundle (_("Faderport Support (Receive)"), true));
97 _output_bundle.reset (new ARDOUR::Bundle (_("Faderport Support (Send)"), false));
98
99 _input_bundle->add_channel (
100 "",
101 ARDOUR::DataType::MIDI,
102 session->engine().make_port_name_non_relative (inp->name())
103 );
104
105 _output_bundle->add_channel (
106 "",
107 ARDOUR::DataType::MIDI,
108 session->engine().make_port_name_non_relative (outp->name())
109 );
110
111 /* Catch port connections and disconnections */
112 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (_port_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::connection_handler, this, _1, _2, _3, _4, _5), this);
113
114 buttons.insert (std::make_pair (Mute, Button (*this, _("Mute"), Mute, 21)));
115 buttons.insert (std::make_pair (Solo, Button (*this, _("Solo"), Solo, 22)));
116 buttons.insert (std::make_pair (Rec, Button (*this, _("Rec"), Rec, 23)));
117 buttons.insert (std::make_pair (Left, Button (*this, _("Left"), Left, 20)));
118 buttons.insert (std::make_pair (Bank, Button (*this, _("Bank"), Bank, 19)));
119 buttons.insert (std::make_pair (Right, Button (*this, _("Right"), Right, 18)));
120 buttons.insert (std::make_pair (Output, Button (*this, _("Output"), Output, 17)));
121 buttons.insert (std::make_pair (FP_Read, Button (*this, _("Read"), FP_Read, 13)));
122 buttons.insert (std::make_pair (FP_Write, Button (*this, _("Write"), FP_Write, 14)));
123 buttons.insert (std::make_pair (FP_Touch, Button (*this, _("Touch"), FP_Touch, 15)));
124 buttons.insert (std::make_pair (FP_Off, Button (*this, _("Off"), FP_Off, 16)));
125 buttons.insert (std::make_pair (Mix, Button (*this, _("Mix"), Mix, 12)));
126 buttons.insert (std::make_pair (Proj, Button (*this, _("Proj"), Proj, 11)));
127 buttons.insert (std::make_pair (Trns, Button (*this, _("Trns"), Trns, 10)));
128 buttons.insert (std::make_pair (Undo, Button (*this, _("Undo"), Undo, 9)));
129 buttons.insert (std::make_pair (Shift, Button (*this, _("Shift"), Shift, 5)));
130 buttons.insert (std::make_pair (Punch, Button (*this, _("Punch"), Punch, 6)));
131 buttons.insert (std::make_pair (User, Button (*this, _("User"), User, 7)));
132 buttons.insert (std::make_pair (Loop, Button (*this, _("Loop"), Loop, 8)));
133 buttons.insert (std::make_pair (Rewind, Button (*this, _("Rewind"), Rewind, 4)));
134 buttons.insert (std::make_pair (Ffwd, Button (*this, _("Ffwd"), Ffwd, 3)));
135 buttons.insert (std::make_pair (Stop, Button (*this, _("Stop"), Stop, 2)));
136 buttons.insert (std::make_pair (Play, Button (*this, _("Play"), Play, 1)));
137 buttons.insert (std::make_pair (RecEnable, Button (*this, _("RecEnable"), RecEnable, 0)));
138 buttons.insert (std::make_pair (Footswitch, Button (*this, _("Footswitch"), Footswitch, -1)));
139 buttons.insert (std::make_pair (FaderTouch, Button (*this, _("Fader (touch)"), FaderTouch, -1)));
140
141 get_button (Shift).set_flash (true);
142 get_button (Mix).set_flash (true);
143 get_button (Proj).set_flash (true);
144 get_button (Trns).set_flash (true);
145 get_button (User).set_flash (true);
146
147 get_button (Left).set_action ( boost::bind (&FaderPort::left, this), true);
148 get_button (Right).set_action ( boost::bind (&FaderPort::right, this), true);
149
150 get_button (Undo).set_action (boost::bind (&FaderPort::undo, this), true);
151 get_button (Undo).set_action (boost::bind (&FaderPort::redo, this), true, ShiftDown);
152 get_button (Undo).set_flash (true);
153
154 get_button (FP_Read).set_action (boost::bind (&FaderPort::read, this), true);
155 get_button (FP_Read).set_action (boost::bind (&FaderPort::off, this), false, LongPress);
156 get_button (FP_Write).set_action (boost::bind (&FaderPort::write, this), true);
157 get_button (FP_Write).set_action (boost::bind (&FaderPort::off, this), false, LongPress);
158 get_button (FP_Touch).set_action (boost::bind (&FaderPort::touch, this), true);
159 get_button (FP_Touch).set_action (boost::bind (&FaderPort::off, this), false, LongPress);
160 get_button (FP_Off).set_action (boost::bind (&FaderPort::off, this), true);
161
162 get_button (Play).set_action (boost::bind (&BasicUI::transport_play, this, true), true);
163 get_button (RecEnable).set_action (boost::bind (&BasicUI::rec_enable_toggle, this), true);
164 /* Stop is a modifier, so we have to use its own button state to get
165 the default action (since StopDown will be set when looking for the
166 action to invoke.
167 */
168 get_button (Stop).set_action (boost::bind (&BasicUI::transport_stop, this), true, StopDown);
169 get_button (Ffwd).set_action (boost::bind (&BasicUI::ffwd, this), true);
170
171 /* See comments about Stop above .. */
172 get_button (Rewind).set_action (boost::bind (&BasicUI::rewind, this), true, RewindDown);
173 get_button (Rewind).set_action (boost::bind (&BasicUI::goto_zero, this), true, ButtonState (RewindDown|StopDown));
174 get_button (Rewind).set_action (boost::bind (&BasicUI::goto_start, this, false), true, ButtonState (RewindDown|ShiftDown));
175
176 get_button (Ffwd).set_action (boost::bind (&BasicUI::ffwd, this), true);
177 get_button (Ffwd).set_action (boost::bind (&BasicUI::goto_end, this), true, ShiftDown);
178
179 get_button (Punch).set_action (boost::bind (&FaderPort::punch, this), true);
180
181 get_button (Loop).set_action (boost::bind (&BasicUI::loop_toggle, this), true);
182 get_button (Loop).set_action (boost::bind (&BasicUI::add_marker, this, string()), true, ShiftDown);
183
184 get_button (Punch).set_action (boost::bind (&BasicUI::prev_marker, this), true, ShiftDown);
185 get_button (User).set_action (boost::bind (&BasicUI::next_marker, this), true, ShiftDown);
186
187 get_button (Mute).set_action (boost::bind (&FaderPort::mute, this), true);
188 get_button (Solo).set_action (boost::bind (&FaderPort::solo, this), true);
189 get_button (Rec).set_action (boost::bind (&FaderPort::rec_enable, this), true);
190
191 get_button (Output).set_action (boost::bind (&FaderPort::use_master, this), true);
192 get_button (Output).set_action (boost::bind (&FaderPort::use_monitor, this), true, ShiftDown);
193 }
194
~FaderPort()195 FaderPort::~FaderPort ()
196 {
197 cerr << "~FP\n";
198
199 close ();
200
201 if (_input_port) {
202 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
203 Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
204 AudioEngine::instance()->unregister_port (_input_port);
205 _input_port.reset ();
206 }
207
208 if (_output_port) {
209 _output_port->drain (10000, 250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
210 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
211 Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
212 AudioEngine::instance()->unregister_port (_output_port);
213 _output_port.reset ();
214 }
215
216 tear_down_gui ();
217
218 /* stop event loop */
219 DEBUG_TRACE (DEBUG::FaderPort, "BaseUI::quit ()\n");
220 BaseUI::quit ();
221 }
222
223 void*
request_factory(uint32_t num_requests)224 FaderPort::request_factory (uint32_t num_requests)
225 {
226 /* AbstractUI<T>::request_buffer_factory() is a template method only
227 instantiated in this source module. To provide something visible for
228 use in the interface/descriptor, we have this static method that is
229 template-free.
230 */
231 return request_buffer_factory (num_requests);
232 }
233
234 void
start_midi_handling()235 FaderPort::start_midi_handling ()
236 {
237 /* handle device inquiry response */
238 _input_port->parser()->sysex.connect_same_thread (midi_connections, boost::bind (&FaderPort::sysex_handler, this, _1, _2, _3));
239 /* handle buttons */
240 _input_port->parser()->poly_pressure.connect_same_thread (midi_connections, boost::bind (&FaderPort::button_handler, this, _1, _2));
241 /* handle encoder */
242 _input_port->parser()->pitchbend.connect_same_thread (midi_connections, boost::bind (&FaderPort::encoder_handler, this, _1, _2));
243 /* handle fader */
244 _input_port->parser()->controller.connect_same_thread (midi_connections, boost::bind (&FaderPort::fader_handler, this, _1, _2));
245
246 /* This connection means that whenever data is ready from the input
247 * port, the relevant thread will invoke our ::midi_input_handler()
248 * method, which will read the data, and invoke the parser.
249 */
250
251 _input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &FaderPort::midi_input_handler), boost::weak_ptr<AsyncMIDIPort> (_input_port)));
252 _input_port->xthread().attach (main_loop()->get_context());
253 }
254
255 void
stop_midi_handling()256 FaderPort::stop_midi_handling ()
257 {
258 midi_connections.drop_connections ();
259
260 /* Note: the input handler is still active at this point, but we're no
261 * longer connected to any of the parser signals
262 */
263 }
264
265 void
do_request(FaderPortRequest * req)266 FaderPort::do_request (FaderPortRequest* req)
267 {
268 if (req->type == CallSlot) {
269
270 call_slot (MISSING_INVALIDATOR, req->the_slot);
271
272 } else if (req->type == Quit) {
273
274 stop ();
275 }
276 }
277
278 int
stop()279 FaderPort::stop ()
280 {
281 BaseUI::quit ();
282
283 return 0;
284 }
285
286 void
thread_init()287 FaderPort::thread_init ()
288 {
289 pthread_set_name (event_loop_name().c_str());
290
291 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
292 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
293
294 set_thread_priority ();
295 }
296
297 void
all_lights_out()298 FaderPort::all_lights_out ()
299 {
300 for (ButtonMap::iterator b = buttons.begin(); b != buttons.end(); ++b) {
301 b->second.set_led_state (_output_port, false);
302 }
303 }
304
305 FaderPort::Button&
get_button(ButtonID id) const306 FaderPort::get_button (ButtonID id) const
307 {
308 ButtonMap::const_iterator b = buttons.find (id);
309 assert (b != buttons.end());
310 return const_cast<Button&>(b->second);
311 }
312
313 bool
button_long_press_timeout(ButtonID id)314 FaderPort::button_long_press_timeout (ButtonID id)
315 {
316 if (buttons_down.find (id) != buttons_down.end()) {
317 if (get_button (id).invoke (ButtonState (LongPress|button_state), false)) {
318 /* whichever button this was, we've used it ... don't invoke the
319 release action.
320 */
321 consumed.insert (id);
322 }
323 } else {
324 /* release happened and somehow we were not cancelled */
325 }
326
327 return false; /* don't get called again */
328 }
329
330 void
start_press_timeout(Button & button,ButtonID id)331 FaderPort::start_press_timeout (Button& button, ButtonID id)
332 {
333 Glib::RefPtr<Glib::TimeoutSource> timeout = Glib::TimeoutSource::create (500); // milliseconds
334 button.timeout_connection = timeout->connect (sigc::bind (sigc::mem_fun (*this, &FaderPort::button_long_press_timeout), id));
335 timeout->attach (main_loop()->get_context());
336 }
337
338 void
button_handler(MIDI::Parser &,MIDI::EventTwoBytes * tb)339 FaderPort::button_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
340 {
341 ButtonID id (ButtonID (tb->controller_number));
342 Button& button (get_button (id));
343
344 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("button event for ID %1 press ? %2\n", (int) tb->controller_number, (tb->value ? "yes" : "no")));
345
346 if (tb->value) {
347 buttons_down.insert (id);
348 } else {
349 buttons_down.erase (id);
350 button.timeout_connection.disconnect ();
351 }
352
353 ButtonState bs (ButtonState (0));
354
355 switch (id) {
356 case Shift:
357 bs = ShiftDown;
358 break;
359 case Stop:
360 bs = StopDown;
361 break;
362 case Rewind:
363 bs = RewindDown;
364 break;
365 case FaderTouch:
366 fader_is_touched = tb->value;
367 if (_current_stripable) {
368 boost::shared_ptr<AutomationControl> gain = _current_stripable->gain_control ();
369 if (gain) {
370 samplepos_t now = session->engine().sample_time();
371 if (tb->value) {
372 gain->start_touch (now);
373 } else {
374 gain->stop_touch (now);
375 }
376 }
377 }
378 break;
379 default:
380 if (tb->value) {
381 start_press_timeout (button, id);
382 }
383 break;
384 }
385
386 if (bs) {
387 button_state = (tb->value ? ButtonState (button_state|bs) : ButtonState (button_state&~bs));
388 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("reset button state to %1 using %2\n", button_state, (int) bs));
389 }
390
391 if (button.uses_flash()) {
392 button.set_led_state (_output_port, (int)tb->value);
393 }
394
395 set<ButtonID>::iterator c = consumed.find (id);
396
397 if (c == consumed.end()) {
398 (void) button.invoke (button_state, tb->value ? true : false);
399 } else {
400 DEBUG_TRACE (DEBUG::FaderPort, "button was consumed, ignored\n");
401 consumed.erase (c);
402 }
403 }
404
405 void
encoder_handler(MIDI::Parser &,MIDI::pitchbend_t pb)406 FaderPort::encoder_handler (MIDI::Parser &, MIDI::pitchbend_t pb)
407 {
408 int delta = 1;
409
410 if (pb >= 8192) {
411 delta = -1;
412 }
413
414 //knob debouncing and hysteresis. The presonus encoder often sends bursts of events, or goes the wrong direction
415 {
416 last_last_encoder_delta = last_encoder_delta;
417 last_encoder_delta = delta;
418 microseconds_t now = get_microseconds ();
419 if ((now - last_encoder_time) < 10*1000) { //require at least 10ms interval between changes, because the device sometimes sends multiple deltas
420 return;
421 }
422 if ((now - last_encoder_time) < 100*1000) { //avoid directional changes while "spinning", 100ms window
423 if ( (delta == last_encoder_delta) && (delta == last_last_encoder_delta) ) {
424 last_good_encoder_delta = delta; //3 in a row, grudgingly accept this as the new direction
425 }
426 if (delta != last_good_encoder_delta) { //otherwise ensure we keep going the same way
427 delta = last_good_encoder_delta;
428 }
429 } else { //we aren't yet in a spin window, just assume this move is really what we want
430 //NOTE: if you are worried about where these get initialized, here it is.
431 last_last_encoder_delta = delta;
432 last_encoder_delta = delta;
433 }
434 last_encoder_time = now;
435 last_good_encoder_delta = delta;
436 }
437
438 if (_current_stripable) {
439
440 ButtonState trim_modifier;
441 ButtonState width_modifier;
442
443 if (Profile->get_mixbus()) {
444 trim_modifier = ShiftDown;
445 width_modifier = ButtonState (0);
446 } else {
447 trim_modifier = UserDown;
448 width_modifier = ShiftDown;
449 }
450
451 if ((button_state & trim_modifier) == trim_modifier ) { // mod+encoder = input trim
452 boost::shared_ptr<AutomationControl> trim = _current_stripable->trim_control ();
453 if (trim) {
454 float val = accurate_coefficient_to_dB (trim->get_value());
455 val += delta * .5f; // use 1/2 dB Steps -20..+20
456 trim->set_value (dB_to_coefficient (val), Controllable::UseGroup);
457 }
458 } else if (width_modifier && ((button_state & width_modifier) == width_modifier)) {
459 pan_width (delta);
460
461 } else { // pan/balance
462 pan_azimuth (delta);
463 }
464 }
465 }
466
467 void
fader_handler(MIDI::Parser &,MIDI::EventTwoBytes * tb)468 FaderPort::fader_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
469 {
470 bool was_fader = false;
471
472 if (tb->controller_number == 0x0) {
473 fader_msb = tb->value;
474 was_fader = true;
475 } else if (tb->controller_number == 0x20) {
476 fader_lsb = tb->value;
477 was_fader = true;
478 }
479
480 if (was_fader) {
481 if (_current_stripable) {
482 boost::shared_ptr<AutomationControl> gain = _current_stripable->gain_control ();
483 if (gain) {
484 int ival = (fader_msb << 7) | fader_lsb;
485 float val = gain->interface_to_internal (ival/16383.0);
486 /* even though the faderport only controls a
487 single stripable at a time, allow the fader to
488 modify the group, if appropriate.
489 */
490 _current_stripable->gain_control()->set_value (val, Controllable::UseGroup);
491 }
492 }
493 }
494 }
495
496 void
sysex_handler(MIDI::Parser & p,MIDI::byte * buf,size_t sz)497 FaderPort::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t sz)
498 {
499 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("sysex message received, size = %1\n", sz));
500
501 if (sz < 17) {
502 return;
503 }
504
505 if (buf[2] != 0x7f ||
506 buf[3] != 0x06 ||
507 buf[4] != 0x02 ||
508 buf[5] != 0x0 ||
509 buf[6] != 0x1 ||
510 buf[7] != 0x06 ||
511 buf[8] != 0x02 ||
512 buf[9] != 0x0 ||
513 buf[10] != 0x01 ||
514 buf[11] != 0x0) {
515 return;
516 }
517
518 _device_active = true;
519
520 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort identified via MIDI Device Inquiry response\n");
521
522 /* put it into native mode */
523
524 MIDI::byte native[3];
525 native[0] = 0x91;
526 native[1] = 0x00;
527 native[2] = 0x64;
528
529 _output_port->write (native, 3, 0);
530
531 all_lights_out ();
532
533 /* catch up on state */
534
535 /* make sure that rec_enable_state is consistent with current device state */
536 get_button (RecEnable).set_led_state (_output_port, rec_enable_state);
537
538 map_transport_state ();
539 map_recenable_state ();
540 }
541
542 int
set_active(bool yn)543 FaderPort::set_active (bool yn)
544 {
545 DEBUG_TRACE (DEBUG::FaderPort, string_compose("Faderport::set_active init with yn: '%1'\n", yn));
546
547 if (yn == active()) {
548 return 0;
549 }
550
551 if (yn) {
552
553 /* start event loop */
554
555 BaseUI::run ();
556
557 connect_session_signals ();
558
559 Glib::RefPtr<Glib::TimeoutSource> blink_timeout = Glib::TimeoutSource::create (200); // milliseconds
560 blink_connection = blink_timeout->connect (sigc::mem_fun (*this, &FaderPort::blink));
561 blink_timeout->attach (main_loop()->get_context());
562
563 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds
564 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &FaderPort::periodic));
565 periodic_timeout->attach (main_loop()->get_context());
566
567 } else {
568
569 BaseUI::quit ();
570 close ();
571
572 }
573
574 ControlProtocol::set_active (yn);
575
576 DEBUG_TRACE (DEBUG::FaderPort, string_compose("Faderport::set_active done with yn: '%1'\n", yn));
577
578 return 0;
579 }
580
581 bool
periodic()582 FaderPort::periodic ()
583 {
584 if (!_current_stripable) {
585 return true;
586 }
587
588 ARDOUR::AutoState gain_state = _current_stripable->gain_control()->automation_state();
589
590 if (gain_state == ARDOUR::Touch || gain_state == ARDOUR::Play) {
591 map_gain ();
592 }
593
594 return true;
595 }
596
597 void
stop_blinking(ButtonID id)598 FaderPort::stop_blinking (ButtonID id)
599 {
600 blinkers.remove (id);
601 get_button (id).set_led_state (_output_port, false);
602 }
603
604 void
start_blinking(ButtonID id)605 FaderPort::start_blinking (ButtonID id)
606 {
607 blinkers.push_back (id);
608 get_button (id).set_led_state (_output_port, true);
609 }
610
611 bool
blink()612 FaderPort::blink ()
613 {
614 blink_state = !blink_state;
615
616 for (Blinkers::iterator b = blinkers.begin(); b != blinkers.end(); b++) {
617 get_button(*b).set_led_state (_output_port, blink_state);
618 }
619
620 map_recenable_state ();
621
622 return true;
623 }
624
625 void
close()626 FaderPort::close ()
627 {
628 all_lights_out ();
629
630 stop_midi_handling ();
631 session_connections.drop_connections ();
632 _port_connection.disconnect ();
633 blink_connection.disconnect ();
634 selection_connection.disconnect ();
635 stripable_connections.drop_connections ();
636 periodic_connection.disconnect ();
637
638 #if 0
639 stripable_connections.drop_connections ();
640 #endif
641 }
642
643 void
map_recenable_state()644 FaderPort::map_recenable_state ()
645 {
646 /* special case for RecEnable because its status can change as a
647 * confluence of unrelated parameters: (a) session rec-enable state (b)
648 * rec-enabled tracks. So we don't add the button to the blinkers list,
649 * we just call this:
650 *
651 * * from the blink callback
652 * * when the session tells us about a status change
653 *
654 * We do the last one so that the button changes state promptly rather
655 * than waiting for the next blink callback. The change in "blinking"
656 * based on having record-enabled tracks isn't urgent, and that happens
657 * during the blink callback.
658 */
659
660 bool onoff;
661
662 switch (session->record_status()) {
663 case Session::Disabled:
664 onoff = false;
665 break;
666 case Session::Enabled:
667 onoff = blink_state;
668 break;
669 case Session::Recording:
670 if (session->have_rec_enabled_track ()) {
671 onoff = true;
672 } else {
673 onoff = blink_state;
674 }
675 break;
676 }
677
678 if (onoff != rec_enable_state) {
679 get_button(RecEnable).set_led_state (_output_port, onoff);
680 rec_enable_state = onoff;
681 }
682 }
683
684 void
map_transport_state()685 FaderPort::map_transport_state ()
686 {
687 get_button (Loop).set_led_state (_output_port, session->get_play_loop());
688
689 float ts = get_transport_speed();
690
691 if (ts == 0) {
692 stop_blinking (Play);
693 } else if (fabs (ts) == 1.0) {
694 stop_blinking (Play);
695 get_button (Play).set_led_state (_output_port, true);
696 } else {
697 start_blinking (Play);
698 }
699
700 get_button (Stop).set_led_state (_output_port, stop_button_onoff());
701 get_button (Rewind).set_led_state (_output_port, rewind_button_onoff ());
702 get_button (Ffwd).set_led_state (_output_port, ffwd_button_onoff());
703 }
704
705 void
parameter_changed(string what)706 FaderPort::parameter_changed (string what)
707 {
708 if (what == "punch-in" || what == "punch-out") {
709 bool in = session->config.get_punch_in ();
710 bool out = session->config.get_punch_out ();
711 if (in && out) {
712 get_button (Punch).set_led_state (_output_port, true);
713 blinkers.remove (Punch);
714 } else if (in || out) {
715 start_blinking (Punch);
716 } else {
717 stop_blinking (Punch);
718 }
719 }
720 }
721
722 void
connect_session_signals()723 FaderPort::connect_session_signals()
724 {
725 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable_state, this), this);
726 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_transport_state, this), this);
727 /* not session, but treat it similarly */
728 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::parameter_changed, this, _1), this);
729 }
730
731 bool
midi_input_handler(Glib::IOCondition ioc,boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)732 FaderPort::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)
733 {
734 boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
735
736 if (!port) {
737 return false;
738 }
739
740 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
741
742 if (ioc & ~IO_IN) {
743 return false;
744 }
745
746 if (ioc & IO_IN) {
747
748 port->clear ();
749 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
750 samplepos_t now = session->engine().sample_time();
751 port->parse (now);
752 }
753
754 return true;
755 }
756
757
758 XMLNode&
get_state()759 FaderPort::get_state ()
760 {
761 XMLNode& node (ControlProtocol::get_state());
762
763 XMLNode* child;
764
765 child = new XMLNode (X_("Input"));
766 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
767 node.add_child_nocopy (*child);
768
769
770 child = new XMLNode (X_("Output"));
771 child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
772 node.add_child_nocopy (*child);
773
774 /* Save action state for Mix, Proj, Trns and User buttons, since these
775 * are user controlled. We can only save named-action operations, since
776 * internal functions are just pointers to functions and hard to
777 * serialize without enumerating them all somewhere.
778 */
779
780 node.add_child_nocopy (get_button (Mix).get_state());
781 node.add_child_nocopy (get_button (Proj).get_state());
782 node.add_child_nocopy (get_button (Trns).get_state());
783 node.add_child_nocopy (get_button (User).get_state());
784 node.add_child_nocopy (get_button (Footswitch).get_state());
785
786 return node;
787 }
788
789 int
set_state(const XMLNode & node,int version)790 FaderPort::set_state (const XMLNode& node, int version)
791 {
792 XMLNodeList nlist;
793 XMLNode const* child;
794
795 if (ControlProtocol::set_state (node, version)) {
796 return -1;
797 }
798
799 if ((child = node.child (X_("Input"))) != 0) {
800 XMLNode* portnode = child->child (Port::state_node_name.c_str());
801 if (portnode) {
802 portnode->remove_property ("name");
803 boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
804 }
805 }
806
807 if ((child = node.child (X_("Output"))) != 0) {
808 XMLNode* portnode = child->child (Port::state_node_name.c_str());
809 if (portnode) {
810 portnode->remove_property ("name");
811 boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
812 }
813 }
814
815 for (XMLNodeList::const_iterator n = node.children().begin(); n != node.children().end(); ++n) {
816 if ((*n)->name() == X_("Button")) {
817 int32_t xid;
818 if (!(*n)->get_property (X_("id"), xid)) {
819 continue;
820 }
821 ButtonMap::iterator b = buttons.find (ButtonID (xid));
822 if (b == buttons.end()) {
823 continue;
824 }
825 b->second.set_state (**n);
826 }
827 }
828
829 return 0;
830 }
831
832 bool
connection_handler(boost::weak_ptr<ARDOUR::Port>,std::string name1,boost::weak_ptr<ARDOUR::Port>,std::string name2,bool yn)833 FaderPort::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
834 {
835 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
836 if (!_input_port || !_output_port) {
837 return false;
838 }
839
840 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
841 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
842
843 if (ni == name1 || ni == name2) {
844 if (yn) {
845 connection_state |= InputConnected;
846 } else {
847 connection_state &= ~InputConnected;
848 }
849 } else if (no == name1 || no == name2) {
850 if (yn) {
851 connection_state |= OutputConnected;
852 } else {
853 connection_state &= ~OutputConnected;
854 }
855 } else {
856 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
857 /* not our ports */
858 return false;
859 }
860
861 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
862
863 /* XXX this is a horrible hack. Without a short sleep here,
864 something prevents the device wakeup messages from being
865 sent and/or the responses from being received.
866 */
867
868 g_usleep (100000);
869 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
870 connected ();
871
872 } else {
873 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
874 _device_active = false;
875 }
876
877 ConnectionChange (); /* emit signal for our GUI */
878
879 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
880
881 return true; /* connection status changed */
882 }
883
884 void
connected()885 FaderPort::connected ()
886 {
887 DEBUG_TRACE (DEBUG::FaderPort, "sending device inquiry message...\n");
888
889 start_midi_handling ();
890
891 /* send device inquiry */
892
893 MIDI::byte buf[6];
894
895 buf[0] = 0xf0;
896 buf[1] = 0x7e;
897 buf[2] = 0x7f;
898 buf[3] = 0x06;
899 buf[4] = 0x01;
900 buf[5] = 0xf7;
901
902 _output_port->write (buf, 6, 0);
903 }
904
905 bool
invoke(FaderPort::ButtonState bs,bool press)906 FaderPort::Button::invoke (FaderPort::ButtonState bs, bool press)
907 {
908 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("invoke button %1 for %2 state %3%4%5\n", id, (press ? "press":"release"), hex, bs, dec));
909
910 ToDoMap::iterator x;
911
912 if (press) {
913 if ((x = on_press.find (bs)) == on_press.end()) {
914 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("no press action for button %1 state %2 @ %3 in %4\n", id, bs, this, &on_press));
915 return false;
916 }
917 } else {
918 if ((x = on_release.find (bs)) == on_release.end()) {
919 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("no release action for button %1 state %2 @%3 in %4\n", id, bs, this, &on_release));
920 return false;
921 }
922 }
923
924 switch (x->second.type) {
925 case NamedAction:
926 if (!x->second.action_name.empty()) {
927 fp.access_action (x->second.action_name);
928 return true;
929 }
930 break;
931 case InternalFunction:
932 if (x->second.function) {
933 x->second.function ();
934 return true;
935 }
936 }
937
938 return false;
939 }
940
941 void
set_action(string const & name,bool when_pressed,FaderPort::ButtonState bs)942 FaderPort::Button::set_action (string const& name, bool when_pressed, FaderPort::ButtonState bs)
943 {
944 ToDo todo;
945
946 todo.type = NamedAction;
947
948 if (when_pressed) {
949 if (name.empty()) {
950 on_press.erase (bs);
951 } else {
952 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("set button %1 to action %2 on press + %3\n", id, name, bs));
953 todo.action_name = name;
954 on_press[bs] = todo;
955 }
956 } else {
957 if (name.empty()) {
958 on_release.erase (bs);
959 } else {
960 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("set button %1 to action %2 on release + %3\n", id, name, bs));
961 todo.action_name = name;
962 on_release[bs] = todo;
963 }
964 }
965 }
966
967 string
get_action(bool press,FaderPort::ButtonState bs)968 FaderPort::Button::get_action (bool press, FaderPort::ButtonState bs)
969 {
970 ToDoMap::iterator x;
971
972 if (press) {
973 if ((x = on_press.find (bs)) == on_press.end()) {
974 return string();
975 }
976 if (x->second.type != NamedAction) {
977 return string ();
978 }
979 return x->second.action_name;
980 } else {
981 if ((x = on_release.find (bs)) == on_release.end()) {
982 return string();
983 }
984 if (x->second.type != NamedAction) {
985 return string ();
986 }
987 return x->second.action_name;
988 }
989 }
990
991 void
set_action(boost::function<void ()> f,bool when_pressed,FaderPort::ButtonState bs)992 FaderPort::Button::set_action (boost::function<void()> f, bool when_pressed, FaderPort::ButtonState bs)
993 {
994 ToDo todo;
995 todo.type = InternalFunction;
996
997 if (when_pressed) {
998 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("set button %1 (%2) @ %5 to some functor on press + %3 in %4\n", id, name, bs, &on_press, this));
999 todo.function = f;
1000 on_press[bs] = todo;
1001 } else {
1002 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("set button %1 (%2) @ %5 to some functor on release + %3\n", id, name, bs, this));
1003 todo.function = f;
1004 on_release[bs] = todo;
1005 }
1006 }
1007
1008 void
set_led_state(boost::shared_ptr<MIDI::Port> port,bool onoff)1009 FaderPort::Button::set_led_state (boost::shared_ptr<MIDI::Port> port, bool onoff)
1010 {
1011 if (out < 0) {
1012 /* fader button ID - no LED */
1013 return;
1014 }
1015
1016 MIDI::byte buf[3];
1017 buf[0] = 0xa0;
1018 buf[1] = out;
1019 buf[2] = onoff ? 1 : 0;
1020 port->write (buf, 3, 0);
1021 }
1022
1023 int
set_state(XMLNode const & node)1024 FaderPort::Button::set_state (XMLNode const& node)
1025 {
1026 int32_t xid;
1027 if (!node.get_property ("id", xid) || xid != id) {
1028 return -1;
1029 }
1030
1031 typedef pair<string,FaderPort::ButtonState> state_pair_t;
1032 vector<state_pair_t> state_pairs;
1033
1034 state_pairs.push_back (make_pair (string ("plain"), ButtonState (0)));
1035 state_pairs.push_back (make_pair (string ("shift"), ShiftDown));
1036 state_pairs.push_back (make_pair (string ("long"), LongPress));
1037
1038 for (vector<state_pair_t>::const_iterator sp = state_pairs.begin(); sp != state_pairs.end(); ++sp) {
1039
1040 string propname = sp->first + X_("-press");
1041 string value;
1042 if (node.get_property (propname.c_str(), value)) {
1043 set_action (value, true, sp->second);
1044 }
1045
1046 propname = sp->first + X_("-release");
1047 if (node.get_property (propname.c_str(), value)) {
1048 set_action (value, false, sp->second);
1049 }
1050 }
1051
1052 return 0;
1053 }
1054
1055 XMLNode&
get_state() const1056 FaderPort::Button::get_state () const
1057 {
1058 XMLNode* node = new XMLNode (X_("Button"));
1059
1060 node->set_property (X_("id"), to_string<int32_t>(id));
1061
1062 ToDoMap::const_iterator x;
1063 ToDo null;
1064 null.type = NamedAction;
1065
1066 typedef pair<string,FaderPort::ButtonState> state_pair_t;
1067 vector<state_pair_t> state_pairs;
1068
1069 state_pairs.push_back (make_pair (string ("plain"), ButtonState (0)));
1070 state_pairs.push_back (make_pair (string ("shift"), ShiftDown));
1071 state_pairs.push_back (make_pair (string ("long"), LongPress));
1072
1073 for (vector<state_pair_t>::const_iterator sp = state_pairs.begin(); sp != state_pairs.end(); ++sp) {
1074 if ((x = on_press.find (sp->second)) != on_press.end()) {
1075 if (x->second.type == NamedAction) {
1076 node->set_property (string (sp->first + X_("-press")).c_str(), x->second.action_name);
1077 }
1078 }
1079
1080 if ((x = on_release.find (sp->second)) != on_release.end()) {
1081 if (x->second.type == NamedAction) {
1082 node->set_property (string (sp->first + X_("-release")).c_str(), x->second.action_name);
1083 }
1084 }
1085 }
1086
1087 return *node;
1088 }
1089
1090 void
stripable_selection_changed()1091 FaderPort::stripable_selection_changed ()
1092 {
1093 set_current_stripable (ControlProtocol::first_selected_stripable());
1094 }
1095
1096 void
drop_current_stripable()1097 FaderPort::drop_current_stripable ()
1098 {
1099 if (_current_stripable) {
1100 if (_current_stripable == session->monitor_out()) {
1101 set_current_stripable (session->master_out());
1102 } else {
1103 set_current_stripable (boost::shared_ptr<Stripable>());
1104 }
1105 }
1106 }
1107
1108 void
set_current_stripable(boost::shared_ptr<Stripable> r)1109 FaderPort::set_current_stripable (boost::shared_ptr<Stripable> r)
1110 {
1111 stripable_connections.drop_connections ();
1112
1113 _current_stripable = r;
1114
1115 /* turn this off. It will be turned on back on in use_master() or
1116 use_monitor() as appropriate.
1117 */
1118 get_button(Output).set_led_state (_output_port, false);
1119
1120 if (_current_stripable) {
1121 _current_stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::drop_current_stripable, this), this);
1122
1123 _current_stripable->mute_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_mute, this), this);
1124 _current_stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_solo, this), this);
1125
1126 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_stripable);
1127 if (t) {
1128 t->rec_enable_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this);
1129 }
1130
1131 boost::shared_ptr<AutomationControl> control = _current_stripable->gain_control ();
1132 if (control) {
1133 control->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_gain, this), this);
1134 control->alist()->automation_state_changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_auto, this), this);
1135 }
1136
1137 boost::shared_ptr<MonitorProcessor> mp = _current_stripable->monitor_control();
1138 if (mp) {
1139 mp->cut_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_cut, this), this);
1140 }
1141 }
1142
1143 //ToDo: subscribe to the fader automation modes so we can light the LEDs
1144
1145 map_stripable_state ();
1146 }
1147
1148 void
map_auto()1149 FaderPort::map_auto ()
1150 {
1151 /* Under no circumstances send a message to "enable" the LED state of
1152 * the Off button, because this will disable the fader.
1153 */
1154
1155 boost::shared_ptr<AutomationControl> control = _current_stripable->gain_control ();
1156 const AutoState as = control->automation_state ();
1157
1158 switch (as) {
1159 case ARDOUR::Play:
1160 get_button (FP_Read).set_led_state (_output_port, true);
1161 get_button (FP_Write).set_led_state (_output_port, false);
1162 get_button (FP_Touch).set_led_state (_output_port, false);
1163 break;
1164 case ARDOUR::Write:
1165 get_button (FP_Read).set_led_state (_output_port, false);
1166 get_button (FP_Write).set_led_state (_output_port, true);
1167 get_button (FP_Touch).set_led_state (_output_port, false);
1168 break;
1169 case ARDOUR::Touch:
1170 case ARDOUR::Latch: // XXX
1171 get_button (FP_Read).set_led_state (_output_port, false);
1172 get_button (FP_Write).set_led_state (_output_port, false);
1173 get_button (FP_Touch).set_led_state (_output_port, true);
1174 break;
1175 case ARDOUR::Off:
1176 get_button (FP_Read).set_led_state (_output_port, false);
1177 get_button (FP_Write).set_led_state (_output_port, false);
1178 get_button (FP_Touch).set_led_state (_output_port, false);
1179 break;
1180 }
1181
1182 }
1183
1184
1185 void
map_cut()1186 FaderPort::map_cut ()
1187 {
1188 boost::shared_ptr<MonitorProcessor> mp = _current_stripable->monitor_control();
1189
1190 if (mp) {
1191 bool yn = mp->cut_all ();
1192 if (yn) {
1193 start_blinking (Mute);
1194 } else {
1195 stop_blinking (Mute);
1196 }
1197 } else {
1198 stop_blinking (Mute);
1199 }
1200 }
1201
1202 void
map_mute()1203 FaderPort::map_mute ()
1204 {
1205 if (_current_stripable) {
1206 if (_current_stripable->mute_control()->muted()) {
1207 stop_blinking (Mute);
1208 get_button (Mute).set_led_state (_output_port, true);
1209 } else if (_current_stripable->mute_control()->muted_by_others_soloing () || _current_stripable->mute_control()->muted_by_masters()) {
1210 start_blinking (Mute);
1211 } else {
1212 stop_blinking (Mute);
1213 }
1214 } else {
1215 stop_blinking (Mute);
1216 }
1217 }
1218
1219 void
map_solo()1220 FaderPort::map_solo ()
1221 {
1222 if (_current_stripable) {
1223 get_button (Solo).set_led_state (_output_port, _current_stripable->solo_control()->soloed());
1224 } else {
1225 get_button (Solo).set_led_state (_output_port, false);
1226 }
1227 }
1228
1229 void
map_recenable()1230 FaderPort::map_recenable ()
1231 {
1232 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_stripable);
1233 if (t) {
1234 get_button (Rec).set_led_state (_output_port, t->rec_enable_control()->get_value());
1235 } else {
1236 get_button (Rec).set_led_state (_output_port, false);
1237 }
1238 }
1239
1240 void
map_gain()1241 FaderPort::map_gain ()
1242 {
1243 if (fader_is_touched) {
1244 /* Do not send fader moves while the user is touching the fader */
1245 return;
1246 }
1247
1248 if (!_current_stripable) {
1249 return;
1250 }
1251
1252 boost::shared_ptr<AutomationControl> control = _current_stripable->gain_control ();
1253 double val;
1254
1255 if (!control) {
1256 val = 0.0;
1257 } else {
1258 val = control->internal_to_interface (control->get_value ());
1259 }
1260
1261 /* Faderport sends fader position with range 0..16384 (though some of
1262 * the least-significant bits at the top end are missing - it may only
1263 * get to 1636X or so).
1264 *
1265 * But ... position must be sent in the range 0..1023.
1266 *
1267 * Thanks, Obama.
1268 */
1269
1270 int ival = (int) lrintf (val * 1023.0);
1271
1272 /* MIDI normalization requires that we send two separate messages here,
1273 * not one single 6 byte one.
1274 */
1275
1276 MIDI::byte buf[3];
1277
1278 buf[0] = 0xb0;
1279 buf[1] = 0x0;
1280 buf[2] = ival >> 7;
1281
1282 _output_port->write (buf, 3, 0);
1283
1284 buf[1] = 0x20;
1285 buf[2] = ival & 0x7f;
1286
1287 _output_port->write (buf, 3, 0);
1288 }
1289
1290 void
map_stripable_state()1291 FaderPort::map_stripable_state ()
1292 {
1293 if (!_current_stripable) {
1294 stop_blinking (Mute);
1295 stop_blinking (Solo);
1296 get_button (Rec).set_led_state (_output_port, false);
1297 } else {
1298 map_solo ();
1299 map_recenable ();
1300 map_gain ();
1301 map_auto ();
1302
1303 if (_current_stripable == session->monitor_out()) {
1304 map_cut ();
1305 } else {
1306 map_mute ();
1307 }
1308 }
1309 }
1310
1311 list<boost::shared_ptr<ARDOUR::Bundle> >
bundles()1312 FaderPort::bundles ()
1313 {
1314 list<boost::shared_ptr<ARDOUR::Bundle> > b;
1315
1316 if (_input_bundle) {
1317 b.push_back (_input_bundle);
1318 b.push_back (_output_bundle);
1319 }
1320
1321 return b;
1322 }
1323
1324 boost::shared_ptr<Port>
output_port()1325 FaderPort::output_port()
1326 {
1327 return _output_port;
1328 }
1329
1330 boost::shared_ptr<Port>
input_port()1331 FaderPort::input_port()
1332 {
1333 return _input_port;
1334 }
1335
1336 void
set_action(ButtonID id,std::string const & action_name,bool on_press,ButtonState bs)1337 FaderPort::set_action (ButtonID id, std::string const& action_name, bool on_press, ButtonState bs)
1338 {
1339 get_button(id).set_action (action_name, on_press, bs);
1340 }
1341
1342 string
get_action(ButtonID id,bool press,ButtonState bs)1343 FaderPort::get_action (ButtonID id, bool press, ButtonState bs)
1344 {
1345 return get_button(id).get_action (press, bs);
1346 }
1347