globally change all use of "frame" to refer to audio into "sample".
[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 (samplepos_t start, samplepos_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 (bool and_roll)
125 {
126         session->goto_start (and_roll);
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         samplepos_t where = session->audible_sample();
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_sample(), session->audible_sample()+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         samplepos_t pos = session->locations()->first_mark_before (session->transport_sample());
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         samplepos_t pos = session->locations()->first_mark_after (session->transport_sample());
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         access_action ("Editor/undo");
332 }
333
334 void
335 BasicUI::redo ()
336 {
337         access_action ("Editor/redo");
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 samplepos_t
379 BasicUI::transport_sample ()
380 {
381         return session->transport_sample();
382 }
383
384 void
385 BasicUI::locate (samplepos_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         samplepos_t current = session->transport_sample();
394         double s = (double) current / (double) session->nominal_sample_rate();
395
396         s+= secs;
397         if (s < 0) {
398                 s = 0;
399         }
400         s = s * session->nominal_sample_rate();
401
402         session->request_locate ( floor(s) );
403 }
404
405 void
406 BasicUI::jump_by_bars (double bars)
407 {
408         TempoMap& tmap (session->tempo_map());
409         Timecode::BBT_Time bbt (tmap.bbt_at_sample (session->transport_sample()));
410
411         bars += bbt.bars;
412         if (bars < 0) {
413                 bars = 0;
414         }
415
416         AnyTime any;
417         any.type = AnyTime::BBT;
418         any.bbt.bars = bars;
419
420         session->request_locate ( session->convert_to_samples (any) );
421 }
422
423 void
424 BasicUI::toggle_monitor_mute ()
425 {
426         if (session->monitor_out()) {
427                 boost::shared_ptr<MonitorProcessor> mon = session->monitor_out()->monitor_control();
428                 if (mon->cut_all ()) {
429                         mon->set_cut_all (false);
430                 } else {
431                         mon->set_cut_all (true);
432                 }
433         }
434 }
435
436 void
437 BasicUI::toggle_monitor_dim ()
438 {
439         if (session->monitor_out()) {
440                 boost::shared_ptr<MonitorProcessor> mon = session->monitor_out()->monitor_control();
441                 if (mon->dim_all ()) {
442                         mon->set_dim_all (false);
443                 } else {
444                         mon->set_dim_all (true);
445                 }
446         }
447 }
448
449 void
450 BasicUI::toggle_monitor_mono ()
451 {
452         if (session->monitor_out()) {
453                 boost::shared_ptr<MonitorProcessor> mon = session->monitor_out()->monitor_control();
454                 if (mon->mono()) {
455                         mon->set_mono (false);
456                 } else {
457                         mon->set_mono (true);
458                 }
459         }
460 }
461
462 void
463 BasicUI::midi_panic ()
464 {
465         session->midi_panic ();
466 }
467
468 void
469 BasicUI::toggle_click ()
470 {
471         bool state = !Config->get_clicking();
472         Config->set_clicking (state);
473 }
474
475 void
476 BasicUI::toggle_roll ()
477 {
478         if (session->transport_rolling()) {
479                 transport_stop ();
480         } else {
481                 transport_play (false);
482         }
483 }
484
485 void
486 BasicUI::stop_forget ()
487 {
488         session->request_stop (true, true);
489 }
490
491 void BasicUI::mark_in () { access_action("Common/start-range-from-playhead"); }
492 void BasicUI::mark_out () { access_action("Common/finish-range-from-playhead"); }
493
494 void BasicUI::set_punch_range () { access_action("Editor/set-punch-from-edit-range"); }
495 void BasicUI::set_loop_range () { access_action("Editor/set-loop-from-edit-range"); }
496 void BasicUI::set_session_range () { access_action("Editor/set-session-from-edit-range"); }
497
498 void BasicUI::quick_snapshot_stay () { access_action("Main/QuickSnapshotStay"); }
499 void BasicUI::quick_snapshot_switch () { access_action("Main/QuickSnapshotSwitch"); }
500
501 void BasicUI::fit_1_track() { access_action("Editor/fit_1_track"); }
502 void BasicUI::fit_2_tracks() { access_action("Editor/fit_2_tracks"); }
503 void BasicUI::fit_4_tracks() { access_action("Editor/fit_4_tracks"); }
504 void BasicUI::fit_8_tracks() { access_action("Editor/fit_8_tracks"); }
505 void BasicUI::fit_16_tracks() { access_action("Editor/fit_16_tracks"); }
506 void BasicUI::fit_32_tracks() { access_action("Editor/fit_32_tracks"); }
507 void BasicUI::fit_all_tracks() { access_action("Editor/fit_all_tracks"); }
508
509 void BasicUI::zoom_10_ms() { access_action("Editor/zoom_10_ms"); }
510 void BasicUI::zoom_100_ms() { access_action("Editor/zoom_100_ms"); }
511 void BasicUI::zoom_1_sec() { access_action("Editor/zoom_1_sec"); }
512 void BasicUI::zoom_10_sec() { access_action("Editor/zoom_10_sec"); }
513 void BasicUI::zoom_1_min() { access_action("Editor/zoom_1_min"); }
514 void BasicUI::zoom_5_min() { access_action("Editor/zoom_5_min"); }
515 void BasicUI::zoom_10_min() { access_action("Editor/zoom_10_min"); }
516 void BasicUI::zoom_to_session() { access_action("Editor/zoom-to-session"); }
517 void BasicUI::temporal_zoom_in() { access_action("Editor/temporal-zoom-in"); }
518 void BasicUI::temporal_zoom_out() { access_action("Editor/temporal-zoom-out"); }
519
520 void BasicUI::scroll_up_1_track() { access_action("Editor/step-tracks-up"); }
521 void BasicUI::scroll_dn_1_track() { access_action("Editor/step-tracks-down"); }
522 void BasicUI::scroll_up_1_page() { access_action("Editor/scroll-tracks-up"); }
523 void BasicUI::scroll_dn_1_page() { access_action("Editor/scroll-tracks-down"); }
524
525
526 bool
527 BasicUI::locating ()
528 {
529         return session->locate_pending();
530 }
531
532 bool
533 BasicUI::locked ()
534 {
535         return session->transport_locked ();
536 }
537
538 ARDOUR::samplecnt_t
539 BasicUI::timecode_frames_per_hour ()
540 {
541         return session->timecode_frames_per_hour ();
542 }
543
544 void
545 BasicUI::timecode_time (samplepos_t where, Timecode::Time& timecode)
546 {
547         session->timecode_time (where, *((Timecode::Time *) &timecode));
548 }
549
550 void
551 BasicUI::timecode_to_sample (Timecode::Time& timecode, samplepos_t & sample, bool use_offset, bool use_subframes) const
552 {
553         session->timecode_to_sample (*((Timecode::Time*)&timecode), sample, use_offset, use_subframes);
554 }
555
556 void
557 BasicUI::sample_to_timecode (samplepos_t sample, Timecode::Time& timecode, bool use_offset, bool use_subframes) const
558 {
559         session->sample_to_timecode (sample, *((Timecode::Time*)&timecode), use_offset, use_subframes);
560 }
561
562 void
563 BasicUI::cancel_all_solo ()
564 {
565         if (session) {
566                 session->cancel_all_solo ();
567         }
568 }
569
570 struct SortLocationsByPosition {
571     bool operator() (Location* a, Location* b) {
572             return a->start() < b->start();
573     }
574 };
575
576 void
577 BasicUI::goto_nth_marker (int n)
578 {
579         if (!session) {
580                 return;
581         }
582
583         const Locations::LocationList& l (session->locations()->list());
584         Locations::LocationList ordered;
585         ordered = l;
586
587         SortLocationsByPosition cmp;
588         ordered.sort (cmp);
589
590         for (Locations::LocationList::iterator i = ordered.begin(); n >= 0 && i != ordered.end(); ++i) {
591                 if ((*i)->is_mark() && !(*i)->is_hidden() && !(*i)->is_session_range()) {
592                         if (n == 0) {
593                                 session->request_locate ((*i)->start(), session->transport_rolling());
594                                 break;
595                         }
596                         --n;
597                 }
598         }
599 }
600
601 #if 0
602 this stuff is waiting to go in so that all UIs can offer complex solo/mute functionality
603
604 void
605 BasicUI::solo_release (boost::shared_ptr<Route> r)
606 {
607 }
608
609 void
610 BasicUI::solo_press (boost::shared_ptr<Route> r, bool momentary, bool global, bool exclusive, bool isolate, bool solo_group)
611 {
612         if (momentary) {
613                 _solo_release = new SoloMuteRelease (_route->soloed());
614         }
615
616         if (global) {
617
618                 if (_solo_release) {
619                         _solo_release->routes = _session->get_routes ();
620                 }
621
622                 if (Config->get_solo_control_is_listen_control()) {
623                         _session->set_listen (_session->get_routes(), !_route->listening(),  Session::rt_cleanup, true);
624                 } else {
625                         _session->set_solo (_session->get_routes(), !_route->soloed(),  Session::rt_cleanup, true);
626                 }
627
628         } else if (exclusive) {
629
630                 if (_solo_release) {
631                         _solo_release->exclusive = true;
632
633                         boost::shared_ptr<RouteList> routes = _session->get_routes();
634
635                         for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
636                                 if ((*i)->soloed ()) {
637                                         _solo_release->routes_on->push_back (*i);
638                                 } else {
639                                         _solo_release->routes_off->push_back (*i);
640                                 }
641                         }
642                 }
643
644                 if (Config->get_solo_control_is_listen_control()) {
645                         /* ??? we need a just_one_listen() method */
646                 } else {
647                         _session->set_just_one_solo (_route, true);
648                 }
649
650         } else if (isolate) {
651
652                 // shift-click: toggle solo isolated status
653
654                 _route->set_solo_isolated (!_route->solo_isolated(), this);
655                 delete _solo_release;
656                 _solo_release = 0;
657
658         } else if (solo_group) {
659
660                 /* Primary-button1: solo mix group.
661                    NOTE: Primary-button2 is MIDI learn.
662                 */
663
664                 if (_route->route_group()) {
665
666                         if (_solo_release) {
667                                 _solo_release->routes = _route->route_group()->route_list();
668                         }
669
670                         if (Config->get_solo_control_is_listen_control()) {
671                                 _session->set_listen (_route->route_group()->route_list(), !_route->listening(),  Session::rt_cleanup, true);
672                         } else {
673                                 _session->set_solo (_route->route_group()->route_list(), !_route->soloed(),  Session::rt_cleanup, true);
674                         }
675                 }
676
677         } else {
678
679                 /* click: solo this route */
680
681                 boost::shared_ptr<RouteList> rl (new RouteList);
682                 rl->push_back (route());
683
684                 if (_solo_release) {
685                         _solo_release->routes = rl;
686                 }
687
688                 if (Config->get_solo_control_is_listen_control()) {
689                         _session->set_listen (rl, !_route->listening());
690                 } else {
691                         _session->set_solo (rl, !_route->soloed());
692                 }
693         }
694 }
695 #endif