aa13060b35eaa861096e40d0e6b1407ba95fbf20
[ardour.git] / libs / surfaces / control_protocol / basic_ui.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3
4     This program is free software; you can redistribute it
5     and/or modify it under the terms of the GNU Lesser
6     General Public License as published by the Free Software
7     Foundation; either version 2 of the License, or (at your
8     option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include "pbd/pthread_utils.h"
22 #include "pbd/memento_command.h"
23
24 #include "ardour/session.h"
25 #include "ardour/location.h"
26 #include "ardour/tempo.h"
27 #include "ardour/utils.h"
28
29 #include "control_protocol/basic_ui.h"
30
31 #include "pbd/i18n.h"
32
33 using namespace ARDOUR;
34
35 PBD::Signal2<void,std::string,std::string> BasicUI::AccessAction;
36
37 BasicUI::BasicUI (Session& s)
38         : session (&s)
39 {
40 }
41
42 BasicUI::BasicUI ()
43         : session (0)
44 {
45 }
46
47 BasicUI::~BasicUI ()
48 {
49
50 }
51
52 void
53 BasicUI::register_thread (std::string name)
54 {
55         std::string pool_name = name;
56         pool_name += " events";
57
58         SessionEvent::create_per_thread_pool (pool_name, 64);
59 }
60
61 void
62 BasicUI::access_action ( std::string action_path )
63 {
64         int split_at = action_path.find( "/" );
65         std::string group = action_path.substr( 0, split_at );
66         std::string item = action_path.substr( split_at + 1 );
67
68         AccessAction( group, item );
69 }
70
71 void
72 BasicUI::loop_toggle ()
73 {
74         if (!session) {
75                 return;
76         }
77
78         Location * looploc = session->locations()->auto_loop_location();
79
80         if (!looploc) {
81                 return;
82         }
83
84         if (session->get_play_loop()) {
85
86                 /* looping enabled, our job is to disable it */
87
88                 session->request_play_loop (false);
89
90         } else {
91
92                 /* looping not enabled, our job is to enable it.
93
94                    loop-is-NOT-mode: this action always starts the transport rolling.
95                    loop-IS-mode:     this action simply sets the loop play mechanism, but
96                                         does not start transport.
97                 */
98                 if (Config->get_loop_is_mode()) {
99                         session->request_play_loop (true, false);
100                 } else {
101                         session->request_play_loop (true, true);
102                 }
103         }
104
105         //show the loop markers
106         looploc->set_hidden (false, this);
107 }
108
109 void
110 BasicUI::loop_location (framepos_t start, framepos_t end)
111 {
112         Location* tll;
113         if ((tll = session->locations()->auto_loop_location()) == 0) {
114                 Location* loc = new Location (*session, start, end, _("Loop"),  Location::IsAutoLoop);
115                 session->locations()->add (loc, true);
116                 session->set_auto_loop_location (loc);
117         } else {
118                 tll->set_hidden (false, this);
119                 tll->set (start, end);
120         }
121 }
122
123 void
124 BasicUI::goto_start ()
125 {
126         session->goto_start ();
127 }
128
129 void
130 BasicUI::goto_zero ()
131 {
132         session->request_locate (0);
133 }
134
135 void
136 BasicUI::goto_end ()
137 {
138         session->goto_end ();
139 }
140
141 void
142 BasicUI::add_marker (const std::string& markername)
143 {
144         framepos_t where = session->audible_frame();
145         Location *location = new Location (*session, where, where, markername, Location::IsMark);
146         session->begin_reversible_command (_("add marker"));
147         XMLNode &before = session->locations()->get_state();
148         session->locations()->add (location, true);
149         XMLNode &after = session->locations()->get_state();
150         session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
151         session->commit_reversible_command ();
152 }
153
154 void
155 BasicUI::remove_marker_at_playhead ()
156 {
157         if (session) {
158                 //set up for undo
159                 XMLNode &before = session->locations()->get_state();
160                 bool removed = false;
161
162                 //find location(s) at this time
163                 Locations::LocationList locs;
164                 session->locations()->find_all_between (session->audible_frame(), session->audible_frame()+1, locs, Location::Flags(0));
165                 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
166                         if ((*i)->is_mark()) {
167                                 session->locations()->remove (*i);
168                                 removed = true;
169                         }
170                 }
171
172                 //store undo
173                 if (removed) {
174                         session->begin_reversible_command (_("remove marker"));
175                         XMLNode &after = session->locations()->get_state();
176                         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
177                         session->commit_reversible_command ();
178                 }
179         }
180 }
181
182 void
183 BasicUI::rewind ()
184 {
185         session->request_transport_speed (session->transport_speed() - 1.5);
186 }
187
188 void
189 BasicUI::ffwd ()
190 {
191         session->request_transport_speed (session->transport_speed() + 1.5);
192 }
193
194 void
195 BasicUI::transport_stop ()
196 {
197         session->request_transport_speed (0.0);
198 }
199
200 void
201 BasicUI::transport_play (bool from_last_start)
202 {
203         if (!session) {
204                 return;
205         }
206
207         if (session->is_auditioning()) {
208                 return;
209         }
210
211 #if 0
212         if (session->config.get_external_sync()) {
213                 switch (Config->get_sync_source()) {
214                 case Engine:
215                         break;
216                 default:
217                         /* transport controlled by the master */
218                         return;
219                 }
220         }
221 #endif
222
223         bool rolling = session->transport_rolling();
224
225         if (session->get_play_loop()) {
226
227                 /* If loop playback is not a mode, then we should cancel
228                    it when this action is requested. If it is a mode
229                    we just leave it in place.
230                 */
231
232                 if (!Config->get_loop_is_mode()) {
233                         /* XXX it is not possible to just leave seamless loop and keep
234                            playing at present (nov 4th 2009)
235                         */
236                         if (!Config->get_seamless_loop()) {
237                                 /* stop loop playback and stop rolling */
238                                 session->request_play_loop (false, true);
239                         } else if (rolling) {
240                                 /* stop loop playback but keep rolling */
241                                 session->request_play_loop (false, false);
242                         }
243                 }
244
245         } else if (session->get_play_range () ) {
246                 /* stop playing a range if we currently are */
247                 session->request_play_range (0, true);
248         }
249
250         if (!rolling) {
251                 session->request_transport_speed (1.0f);
252         }
253 }
254
255 void
256 BasicUI::rec_enable_toggle ()
257 {
258         switch (session->record_status()) {
259         case Session::Disabled:
260                 if (session->ntracks() == 0) {
261                         // string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu.");
262                         // MessageDialog msg (*editor, txt);
263                         // msg.run ();
264                         return;
265                 }
266                 session->maybe_enable_record ();
267                 break;
268         case Session::Recording:
269         case Session::Enabled:
270                 session->disable_record (false, true);
271         }
272 }
273
274 void
275 BasicUI::all_tracks_rec_in ()
276 {
277         session->set_all_tracks_record_enabled (true);
278 }
279
280 void
281 BasicUI::all_tracks_rec_out ()
282 {
283         session->set_all_tracks_record_enabled (false);
284 }
285
286 void
287 BasicUI::save_state ()
288 {
289         session->save_state ("");
290 }
291
292 void
293 BasicUI::prev_marker ()
294 {
295         framepos_t pos = session->locations()->first_mark_before (session->transport_frame());
296
297         if (pos >= 0) {
298                 session->request_locate (pos, session->transport_rolling());
299         } else {
300                 session->goto_start ();
301         }
302 }
303
304 void
305 BasicUI::next_marker ()
306 {
307         framepos_t pos = session->locations()->first_mark_after (session->transport_frame());
308
309         if (pos >= 0) {
310                 session->request_locate (pos, session->transport_rolling());
311         } else {
312                 session->goto_end();
313         }
314 }
315
316 void
317 BasicUI::set_transport_speed (double speed)
318 {
319         session->request_transport_speed (speed);
320 }
321
322 double
323 BasicUI::get_transport_speed ()
324 {
325         return session->transport_speed ();
326 }
327
328 void
329 BasicUI::undo ()
330 {
331         session->undo (1);
332 }
333
334 void
335 BasicUI::redo ()
336 {
337         session->redo (1);
338 }
339
340 void
341 BasicUI::toggle_all_rec_enables ()
342 {
343         if (session->get_record_enabled()) {
344                 // session->record_disenable_all ();
345         } else {
346                 // session->record_enable_all ();
347         }
348 }
349
350 void
351 BasicUI::toggle_punch_in ()
352 {
353         session->config.set_punch_in (!session->config.get_punch_in());
354 }
355
356 void
357 BasicUI::toggle_punch_out ()
358 {
359         session->config.set_punch_out (!session->config.get_punch_out());
360 }
361
362 bool
363 BasicUI::get_record_enabled ()
364 {
365         return session->get_record_enabled();
366 }
367
368 void
369 BasicUI::set_record_enable (bool yn)
370 {
371         if (yn) {
372                 session->maybe_enable_record ();
373         } else {
374                 session->disable_record (false, true);
375         }
376 }
377
378 framepos_t
379 BasicUI::transport_frame ()
380 {
381         return session->transport_frame();
382 }
383
384 void
385 BasicUI::locate (framepos_t where, bool roll_after_locate)
386 {
387         session->request_locate (where, roll_after_locate);
388 }
389
390 void
391 BasicUI::jump_by_seconds (double secs)
392 {
393         framepos_t current = session->transport_frame();
394         double s = (double) current / (double) session->nominal_frame_rate();
395
396         s+= secs;
397         if (s < 0) current = 0;
398         s = s * session->nominal_frame_rate();
399
400         session->request_locate ( floor(s) );
401 }
402
403 void
404 BasicUI::jump_by_bars (double bars)
405 {
406         TempoMap& tmap (session->tempo_map());
407         Timecode::BBT_Time bbt (tmap.bbt_at_frame (session->transport_frame()));
408
409         bars += bbt.bars;
410         if (bars < 0) bars = 0;
411
412         AnyTime any;
413         any.type = AnyTime::BBT;
414         any.bbt.bars = bars;
415
416         session->request_locate ( session->convert_to_frames (any) );
417 }
418
419 void BasicUI::mark_in () { access_action("Editor/start-range-from-playhead"); }
420 void BasicUI::mark_out () { access_action("Editor/finish-range-from-playhead"); }
421
422 void BasicUI::toggle_click () { access_action("Transport/ToggleClick"); }
423 void BasicUI::midi_panic () { access_action("MIDI/panic"); }
424 void BasicUI::toggle_roll () { access_action("Transport/ToggleRoll"); }
425 void BasicUI::stop_forget () { access_action("Transport/ToggleRollForgetCapture"); }
426
427 void BasicUI::set_punch_range () { access_action("Editor/set-punch-from-edit-range"); }
428 void BasicUI::set_loop_range () { access_action("Editor/set-loop-from-edit-range"); }
429 void BasicUI::set_session_range () { access_action("Editor/set-session-from-edit-range"); }
430
431 void BasicUI::toggle_monitor_mute () { /*access_action("Editor/toggle_monitor_mute");  */ }
432 void BasicUI::toggle_monitor_dim () {  /*access_action("Editor/toggle_monitor_dim");  */ }
433 void BasicUI::toggle_monitor_mono () { /*access_action("Editor/toggle_monitor_mono");  */ }
434
435 void BasicUI::quick_snapshot_stay () { access_action("Main/QuickSnapshotStay"); }
436 void BasicUI::quick_snapshot_switch () { access_action("Main/QuickSnapshotSwitch"); }
437
438 void BasicUI::fit_1_track() { access_action("Editor/fit_1_track"); }
439 void BasicUI::fit_2_tracks() { access_action("Editor/fit_2_tracks"); }
440 void BasicUI::fit_4_tracks() { access_action("Editor/fit_4_tracks"); }
441 void BasicUI::fit_8_tracks() { access_action("Editor/fit_8_tracks"); }
442 void BasicUI::fit_16_tracks() { access_action("Editor/fit_16_tracks"); }
443 void BasicUI::fit_32_tracks() { access_action("Editor/fit_32_tracks"); }
444 void BasicUI::fit_all_tracks() { access_action("Editor/fit_all_tracks"); }
445
446 void BasicUI::zoom_10_ms() { access_action("Editor/zoom_10_ms"); }
447 void BasicUI::zoom_100_ms() { access_action("Editor/zoom_100_ms"); }
448 void BasicUI::zoom_1_sec() { access_action("Editor/zoom_1_sec"); }
449 void BasicUI::zoom_10_sec() { access_action("Editor/zoom_10_sec"); }
450 void BasicUI::zoom_1_min() { access_action("Editor/zoom_1_min"); }
451 void BasicUI::zoom_5_min() { access_action("Editor/zoom_5_min"); }
452 void BasicUI::zoom_10_min() { access_action("Editor/zoom_10_min"); }
453 void BasicUI::zoom_to_session() { access_action("Editor/zoom-to-session"); }
454 void BasicUI::temporal_zoom_in() { access_action("Editor/temporal-zoom-in"); }
455 void BasicUI::temporal_zoom_out() { access_action("Editor/temporal-zoom-out"); }
456
457 void BasicUI::scroll_up_1_track() { access_action("Editor/step-tracks-up"); }
458 void BasicUI::scroll_dn_1_track() { access_action("Editor/step-tracks-down"); }
459 void BasicUI::scroll_up_1_page() { access_action("Editor/scroll-tracks-up"); }
460 void BasicUI::scroll_dn_1_page() { access_action("Editor/scroll-tracks-down"); }
461
462
463 bool
464 BasicUI::locating ()
465 {
466         return session->locate_pending();
467 }
468
469 bool
470 BasicUI::locked ()
471 {
472         return session->transport_locked ();
473 }
474
475 ARDOUR::framecnt_t
476 BasicUI::timecode_frames_per_hour ()
477 {
478         return session->timecode_frames_per_hour ();
479 }
480
481 void
482 BasicUI::timecode_time (framepos_t where, Timecode::Time& timecode)
483 {
484         session->timecode_time (where, *((Timecode::Time *) &timecode));
485 }
486
487 void
488 BasicUI::timecode_to_sample (Timecode::Time& timecode, framepos_t & sample, bool use_offset, bool use_subframes) const
489 {
490         session->timecode_to_sample (*((Timecode::Time*)&timecode), sample, use_offset, use_subframes);
491 }
492
493 void
494 BasicUI::sample_to_timecode (framepos_t sample, Timecode::Time& timecode, bool use_offset, bool use_subframes) const
495 {
496         session->sample_to_timecode (sample, *((Timecode::Time*)&timecode), use_offset, use_subframes);
497 }
498
499 void
500 BasicUI::toggle_selection (PresentationInfo::order_t o, PresentationInfo::Flag flags)
501 {
502         boost::shared_ptr<Stripable> s = session->get_remote_nth_stripable (o, flags);
503
504         if (s) {
505                 s->presentation_info().set_selected (!s->presentation_info().selected());
506         }
507 }
508
509 void
510 BasicUI::clear_stripable_selection ()
511 {
512         session->clear_stripable_selection ();
513 }
514
515 void
516 BasicUI::toggle_stripable_selection (boost::shared_ptr<Stripable> s)
517 {
518         session->toggle_stripable_selection (s);
519 }
520
521 void
522 BasicUI::add_stripable_selection (boost::shared_ptr<Stripable> s)
523 {
524         session->add_stripable_selection (s);
525 }
526
527 void
528 BasicUI::set_stripable_selection (boost::shared_ptr<Stripable> s)
529 {
530         session->set_stripable_selection (s);
531 }
532
533
534 void
535 BasicUI::cancel_all_solo ()
536 {
537         if (session) {
538                 session->cancel_all_solo ();
539         }
540 }
541
542 #if 0
543 this stuff is waiting to go in so that all UIs can offer complex solo/mute functionality
544
545 void
546 BasicUI::solo_release (boost::shared_ptr<Route> r)
547 {
548 }
549
550 void
551 BasicUI::solo_press (boost::shared_ptr<Route> r, bool momentary, bool global, bool exclusive, bool isolate, bool solo_group)
552 {
553         if (momentary) {
554                 _solo_release = new SoloMuteRelease (_route->soloed());
555         }
556
557         if (global) {
558
559                 if (_solo_release) {
560                         _solo_release->routes = _session->get_routes ();
561                 }
562
563                 if (Config->get_solo_control_is_listen_control()) {
564                         _session->set_listen (_session->get_routes(), !_route->listening(),  Session::rt_cleanup, true);
565                 } else {
566                         _session->set_solo (_session->get_routes(), !_route->soloed(),  Session::rt_cleanup, true);
567                 }
568
569         } else if (exclusive) {
570
571                 if (_solo_release) {
572                         _solo_release->exclusive = true;
573
574                         boost::shared_ptr<RouteList> routes = _session->get_routes();
575
576                         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
577                                 if ((*i)->soloed ()) {
578                                         _solo_release->routes_on->push_back (*i);
579                                 } else {
580                                         _solo_release->routes_off->push_back (*i);
581                                 }
582                         }
583                 }
584
585                 if (Config->get_solo_control_is_listen_control()) {
586                         /* ??? we need a just_one_listen() method */
587                 } else {
588                         _session->set_just_one_solo (_route, true);
589                 }
590
591         } else if (isolate) {
592
593                 // shift-click: toggle solo isolated status
594
595                 _route->set_solo_isolated (!_route->solo_isolated(), this);
596                 delete _solo_release;
597                 _solo_release = 0;
598
599         } else if (solo_group) {
600
601                 /* Primary-button1: solo mix group.
602                    NOTE: Primary-button2 is MIDI learn.
603                 */
604
605                 if (_route->route_group()) {
606
607                         if (_solo_release) {
608                                 _solo_release->routes = _route->route_group()->route_list();
609                         }
610
611                         if (Config->get_solo_control_is_listen_control()) {
612                                 _session->set_listen (_route->route_group()->route_list(), !_route->listening(),  Session::rt_cleanup, true);
613                         } else {
614                                 _session->set_solo (_route->route_group()->route_list(), !_route->soloed(),  Session::rt_cleanup, true);
615                         }
616                 }
617
618         } else {
619
620                 /* click: solo this route */
621
622                 boost::shared_ptr<RouteList> rl (new RouteList);
623                 rl->push_back (route());
624
625                 if (_solo_release) {
626                         _solo_release->routes = rl;
627                 }
628
629                 if (Config->get_solo_control_is_listen_control()) {
630                         _session->set_listen (rl, !_route->listening());
631                 } else {
632                         _session->set_solo (rl, !_route->soloed());
633                 }
634         }
635 }
636 #endif