Add AutomationControl::parameter() for terseness.
[ardour.git] / libs / ardour / location.cc
1 /*
2     Copyright (C) 2000 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <set>
22 #include <cstdio> /* for sprintf */
23 #include <unistd.h>
24 #include <cerrno>
25 #include <ctime>
26 #include <sigc++/bind.h>
27
28 #include <pbd/stl_delete.h>
29 #include <pbd/xml++.h>
30 #include <pbd/enumwriter.h>
31
32 #include <ardour/location.h>
33 #include <ardour/session.h>
34 #include <ardour/audiofilesource.h>
35
36 #include "i18n.h"
37
38 #define SUFFIX_MAX 32
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace sigc;
43 using namespace PBD;
44
45 Location::Location (const Location& other)
46         : _name (other._name),
47           _start (other._start),
48           _end (other._end),
49           _flags (other._flags)
50 {
51         /* start and end flags can never be copied, because there can only ever be one of each */
52
53         _flags = Flags (_flags & ~IsStart);
54         _flags = Flags (_flags & ~IsEnd);
55 }
56
57 Location::Location (const XMLNode& node)
58 {
59         if (set_state (node)) {
60                 throw failed_constructor ();
61         }
62 }
63
64 Location*
65 Location::operator= (const Location& other)
66 {
67         if (this == &other) {
68                 return this;
69         }
70
71         _name = other._name;
72         _start = other._start;
73         _end = other._end;
74         _flags = other._flags;
75
76         /* "changed" not emitted on purpose */
77         
78         return this;
79 }
80
81 int
82 Location::set_start (nframes_t s)
83 {
84         if (is_mark()) {
85                 if (_start != s) {
86
87                         _start = s;
88                         _end = s;
89
90                         start_changed(this); /* EMIT SIGNAL */
91
92                         if ( is_start() ) {
93
94                                 Session::StartTimeChanged (); /* EMIT SIGNAL */
95                                 AudioFileSource::set_header_position_offset ( s );
96                         }
97
98                         if ( is_end() ) {
99                                 Session::EndTimeChanged (); /* EMIT SIGNAL */
100                         }
101                 }
102                 return 0;
103         }
104
105         if (((is_auto_punch() || is_auto_loop()) && s >= _end) || s > _end) {
106                 return -1;
107         }
108
109         if (s != _start) {
110                 _start = s; 
111                 start_changed(this); /* EMIT SIGNAL */
112         }
113
114         return 0;
115 }
116
117 int
118 Location::set_end (nframes_t e)
119 {
120         if (is_mark()) {
121                 if (_start != e) {
122                         _start = e;
123                         _end = e;
124                         end_changed(this); /* EMIT SIGNAL */
125                 }
126                 return 0;
127         }
128
129         if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
130                 return -1;
131         }
132
133         if (e != _end) {
134                 _end = e; 
135                  end_changed(this); /* EMIT SIGNAL */
136         }
137         return 0;
138 }
139
140 int
141 Location::set (nframes_t start, nframes_t end)
142 {
143         if (is_mark() && start != end) {
144                 return -1;
145         } else if (((is_auto_punch() || is_auto_loop()) && start >= end) || (start > end)) {
146                 return -1;
147         }
148         
149         if (_start != start) {
150                 _start = start;
151                 start_changed(this); /* EMIT SIGNAL */
152         }
153
154         if (_end != end) {
155                 _end = end;
156                 end_changed(this); /* EMIT SIGNAL */
157         }
158         return 0;
159 }
160
161 void
162 Location::set_hidden (bool yn, void *src)
163 {
164         if (set_flag_internal (yn, IsHidden)) {
165                  FlagsChanged (this, src); /* EMIT SIGNAL */
166         }
167 }
168
169 void
170 Location::set_cd (bool yn, void *src)
171 {
172         if (set_flag_internal (yn, IsCDMarker)) {
173                  FlagsChanged (this, src); /* EMIT SIGNAL */
174         }
175 }
176
177 void
178 Location::set_is_end (bool yn, void *src)
179 {
180         if (set_flag_internal (yn, IsEnd)) {
181                  FlagsChanged (this, src); /* EMIT SIGNAL */
182         }
183 }
184
185 void
186 Location::set_is_start (bool yn, void *src)
187 {
188         if (set_flag_internal (yn, IsStart)) {
189                  FlagsChanged (this, src); /* EMIT SIGNAL */
190         }
191 }
192
193 void
194 Location::set_auto_punch (bool yn, void *src) 
195 {
196         if (is_mark() || _start == _end) {
197                 return;
198         }
199
200         if (set_flag_internal (yn, IsAutoPunch)) {
201                  FlagsChanged (this, src); /* EMIT SIGNAL */
202         }
203 }
204
205 void
206 Location::set_auto_loop (bool yn, void *src) 
207 {
208         if (is_mark() || _start == _end) {
209                 return;
210         }
211
212         if (set_flag_internal (yn, IsAutoLoop)) {
213                  FlagsChanged (this, src); /* EMIT SIGNAL */
214         }
215 }
216
217 bool
218 Location::set_flag_internal (bool yn, Flags flag)
219 {
220         if (yn) {
221                 if (!(_flags & flag)) {
222                         _flags = Flags (_flags | flag);
223                         return true;
224                 }
225         } else {
226                 if (_flags & flag) {
227                         _flags = Flags (_flags & ~flag);
228                         return true;
229                 }
230         }
231         return false;
232 }
233
234 void
235 Location::set_mark (bool yn)
236 {
237         /* This function is private, and so does not emit signals */
238
239         if (_start != _end) {
240                 return;
241         }
242         
243         set_flag_internal (yn, IsMark);
244 }
245
246
247 XMLNode&
248 Location::cd_info_node(const string & name, const string & value)
249 {
250         XMLNode* root = new XMLNode("CD-Info");
251
252         root->add_property("name", name);
253         root->add_property("value", value);
254         
255         return *root;
256 }
257
258  
259 XMLNode&
260 Location::get_state (void)
261 {
262         XMLNode *node = new XMLNode ("Location");
263         char buf[64];
264
265         typedef map<string, string>::const_iterator CI;
266
267         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
268                 node->add_child_nocopy(cd_info_node(m->first, m->second));
269         }
270
271         id().print (buf, sizeof (buf));
272         node->add_property("id", buf);
273         node->add_property ("name", name());
274         snprintf (buf, sizeof (buf), "%u", start());
275         node->add_property ("start", buf);
276         snprintf (buf, sizeof (buf), "%u", end());
277         node->add_property ("end", buf);
278         node->add_property ("flags", enum_2_string (_flags));
279
280         return *node;
281 }
282
283 int
284 Location::set_state (const XMLNode& node)
285 {
286         const XMLProperty *prop;
287
288         XMLNodeList cd_list = node.children();
289         XMLNodeConstIterator cd_iter;
290         XMLNode *cd_node;
291         
292         string cd_name;
293         string cd_value;
294
295         if (node.name() != "Location") {
296                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
297                 return -1;
298         }
299
300         if ((prop = node.property ("id")) == 0) {
301                 warning << _("XML node for Location has no ID information") << endmsg;
302         } else {
303                 _id = prop->value ();
304         }
305
306         if ((prop = node.property ("name")) == 0) {
307                 error << _("XML node for Location has no name information") << endmsg;
308                 return -1;
309         }
310                 
311         set_name (prop->value());
312                 
313         if ((prop = node.property ("start")) == 0) {
314                 error << _("XML node for Location has no start information") << endmsg; 
315                 return -1;
316         }
317                 
318                 /* can't use set_start() here, because _end
319                    may make the value of _start illegal.
320                 */
321                 
322         _start = atoi (prop->value().c_str());
323                 
324         if ((prop = node.property ("end")) == 0) {
325                   error << _("XML node for Location has no end information") << endmsg; 
326                   return -1;
327         }
328                 
329         _end = atoi (prop->value().c_str());
330                 
331         if ((prop = node.property ("flags")) == 0) {
332                   error << _("XML node for Location has no flags information") << endmsg; 
333                   return -1;
334         }
335                 
336         _flags = Flags (string_2_enum (prop->value(), _flags));
337
338         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
339                   
340                   cd_node = *cd_iter;
341                   
342                   if (cd_node->name() != "CD-Info") {
343                     continue;
344                   }
345                   
346                   if ((prop = cd_node->property ("name")) != 0) {
347                     cd_name = prop->value();
348                   } else {
349                     throw failed_constructor ();
350                   }
351                   
352                   if ((prop = cd_node->property ("value")) != 0) {
353                     cd_value = prop->value();
354                   } else {
355                     throw failed_constructor ();
356                   }
357                   
358                   
359                   cd_info[cd_name] = cd_value;
360                   
361         }
362
363         changed(this); /* EMIT SIGNAL */
364                 
365         return 0;
366 }
367
368 /*---------------------------------------------------------------------- */
369
370 Locations::Locations ()
371
372 {
373         current_location = 0;
374 }
375
376 Locations::~Locations () 
377 {
378         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
379                 LocationList::iterator tmp = i;
380                 ++tmp;
381                 delete *i;
382                 i = tmp;
383         }
384 }
385
386 int
387 Locations::set_current (Location *loc, bool want_lock)
388
389 {
390         int ret;
391
392         if (want_lock) {
393                 Glib::Mutex::Lock lm (lock);
394                 ret = set_current_unlocked (loc);
395         } else {
396                 ret = set_current_unlocked (loc);
397         }
398
399         if (ret == 0) {
400                  current_changed (current_location); /* EMIT SIGNAL */
401         }
402         return ret;
403 }
404
405 int
406 Locations::next_available_name(string& result,string base)
407 {
408         LocationList::iterator i;
409         Location* location;
410         string temp;
411         string::size_type l;
412         int suffix;
413         char buf[32];
414         bool available[SUFFIX_MAX+1];
415
416         result = base;
417         for (int k=1; k<SUFFIX_MAX; k++) {
418                 available[k] = true;
419         }
420         l = base.length();
421         for (i = locations.begin(); i != locations.end(); ++i) {
422                 location =* i;
423                 temp = location->name();
424                 if (l && !temp.find(base,0)) {
425                         suffix = atoi(temp.substr(l,3).c_str());
426                         if (suffix) available[suffix] = false;
427                 }
428         }
429         for (int k=1; k<=SUFFIX_MAX; k++) {
430                 if (available[k]) { 
431                         snprintf (buf, 31, "%d", k);
432                         result += buf;
433                         return 1;
434                 }
435         }
436         return 0;
437 }
438
439 int
440 Locations::set_current_unlocked (Location *loc)
441 {
442         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
443                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
444                 return -1;
445         }
446         
447         current_location = loc;
448         return 0;
449 }
450
451 void
452 Locations::clear ()
453 {
454         {
455                 Glib::Mutex::Lock lm (lock);
456
457                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
458
459                         LocationList::iterator tmp = i;
460                         ++tmp;
461
462                         if (!(*i)->is_end() && !(*i)->is_start()) {
463                                 locations.erase (i);
464                         }
465
466                         i = tmp;
467                 }
468
469                 current_location = 0;
470         }
471
472         changed (); /* EMIT SIGNAL */
473         current_changed (0); /* EMIT SIGNAL */
474 }       
475
476 void
477 Locations::clear_markers ()
478 {
479         {
480                 Glib::Mutex::Lock lm (lock);
481                 LocationList::iterator tmp;
482
483                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
484                         tmp = i;
485                         ++tmp;
486
487                         if ((*i)->is_mark() && !(*i)->is_end() && !(*i)->is_start()) {
488                                 locations.erase (i);
489                         }
490
491                         i = tmp;
492                 }
493         }
494
495         changed (); /* EMIT SIGNAL */
496 }       
497
498 void
499 Locations::clear_ranges ()
500 {
501         {
502                 Glib::Mutex::Lock lm (lock);
503                 LocationList::iterator tmp;
504                 
505                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
506
507                         tmp = i;
508                         ++tmp;
509
510                         if (!(*i)->is_mark()) {
511                                 locations.erase (i);
512
513                         }
514
515                         i = tmp;
516                 }
517
518                 current_location = 0;
519         }
520
521         changed (); /* EMIT SIGNAL */
522         current_changed (0); /* EMIT SIGNAL */
523 }       
524
525 void
526 Locations::add (Location *loc, bool make_current)
527 {
528         {
529                 Glib::Mutex::Lock lm (lock);
530                 locations.push_back (loc);
531
532                 if (make_current) {
533                         current_location = loc;
534                 }
535         }
536         
537         added (loc); /* EMIT SIGNAL */
538
539         if (make_current) {
540                  current_changed (current_location); /* EMIT SIGNAL */
541         } 
542 }
543
544 void
545 Locations::remove (Location *loc)
546
547 {
548         bool was_removed = false;
549         bool was_current = false;
550         LocationList::iterator i;
551
552         if (loc->is_end() || loc->is_start()) {
553                 return;
554         }
555
556         {
557                 Glib::Mutex::Lock lm (lock);
558
559                 for (i = locations.begin(); i != locations.end(); ++i) {
560                         if ((*i) == loc) {
561                                 locations.erase (i);
562                                 was_removed = true;
563                                 if (current_location == loc) {
564                                         current_location = 0;
565                                         was_current = true;
566                                 }
567                                 break;
568                         }
569                 }
570         }
571         
572         if (was_removed) {
573                 
574                 removed (loc); /* EMIT SIGNAL */
575
576                 if (was_current) {
577                          current_changed (0); /* EMIT SIGNAL */
578                 }
579
580                 changed (); /* EMIT_SIGNAL */
581         }
582 }
583
584 void
585 Locations::location_changed (Location* loc)
586 {
587         changed (); /* EMIT SIGNAL */
588 }
589
590 XMLNode&
591 Locations::get_state ()
592 {
593         XMLNode *node = new XMLNode ("Locations");
594         LocationList::iterator iter;
595         Glib::Mutex::Lock lm (lock);
596        
597         for (iter  = locations.begin(); iter != locations.end(); ++iter) {
598                 node->add_child_nocopy ((*iter)->get_state ());
599         }
600
601         return *node;
602 }       
603
604 int
605 Locations::set_state (const XMLNode& node)
606 {
607         XMLNodeList nlist;
608         XMLNodeConstIterator niter;
609
610         if (node.name() != "Locations") {
611                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
612                 return -1;
613         }
614         
615         nlist = node.children();
616
617         locations.clear ();
618         current_location = 0;
619
620         {
621                 Glib::Mutex::Lock lm (lock);
622
623                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
624                         
625                         try {
626
627                                 Location *loc = new Location (**niter);
628                                 locations.push_back (loc);
629                         }
630
631                         catch (failed_constructor& err) {
632                                 error << _("could not load location from session file - ignored") << endmsg;
633                         }
634                 }
635                 
636                 if (locations.size()) {
637
638                         current_location = locations.front();
639                 } else {
640                         current_location = 0;
641                 }
642         }
643
644         changed (); /* EMIT SIGNAL */
645          
646         return 0;
647 }       
648
649 struct LocationStartEarlierComparison 
650 {
651     bool operator() (Location *a, Location *b) {
652         return a->start() < b->start();
653     }
654 };
655
656 struct LocationStartLaterComparison 
657 {
658     bool operator() (Location *a, Location *b) {
659         return a->start() > b->start();
660     }
661 };
662
663 Location *
664 Locations::first_location_before (nframes_t frame, bool include_special_ranges)
665 {
666         LocationList locs;
667
668         {
669                 Glib::Mutex::Lock lm (lock);
670                 locs = locations;
671         }
672
673         LocationStartLaterComparison cmp;
674         locs.sort (cmp);
675
676         /* locs is now sorted latest..earliest */
677         
678         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
679                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
680                         continue;
681                 }
682                 if (!(*i)->is_hidden() && (*i)->start() < frame) {
683                         return (*i);
684                 }
685         }
686
687         return 0;
688 }
689
690 Location *
691 Locations::first_location_after (nframes_t frame, bool include_special_ranges)
692 {
693         LocationList locs;
694
695         {
696                 Glib::Mutex::Lock lm (lock);
697                 locs = locations;
698         }
699
700         LocationStartEarlierComparison cmp;
701         locs.sort (cmp);
702
703         /* locs is now sorted earliest..latest */
704         
705         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
706                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
707                         continue;
708                 }
709                 if (!(*i)->is_hidden() && (*i)->start() > frame) {
710                         return (*i);
711                 }
712         }
713
714         return 0;
715 }
716
717 nframes_t
718 Locations::first_mark_before (nframes_t frame, bool include_special_ranges)
719 {
720         LocationList locs;
721
722         {
723         Glib::Mutex::Lock lm (lock);
724                 locs = locations;
725         }
726
727         LocationStartLaterComparison cmp;
728         locs.sort (cmp);
729
730         /* locs is now sorted latest..earliest */
731         
732         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
733                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
734                         continue;
735                 }
736                 if (!(*i)->is_hidden()) {
737                         if ((*i)->is_mark()) {
738                                 /* MARK: start == end */
739                                 if ((*i)->start() < frame) {
740                                         return (*i)->start();
741                                 }
742                         } else {
743                                 /* RANGE: start != end, compare start and end */
744                                 if ((*i)->end() < frame) {
745                                         return (*i)->end();
746                                 }
747                                 if ((*i)->start () < frame) {
748                                         return (*i)->start();
749                                 }
750                         }
751                 }
752         }
753
754         return 0;
755 }
756
757 nframes_t
758 Locations::first_mark_after (nframes_t frame, bool include_special_ranges)
759 {
760         LocationList locs;
761
762         {
763         Glib::Mutex::Lock lm (lock);
764                 locs = locations;
765         }
766
767         LocationStartEarlierComparison cmp;
768         locs.sort (cmp);
769
770         /* locs is now sorted earliest..latest */
771         
772         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
773                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
774                         continue;
775                 }
776                 if (!(*i)->is_hidden()) {
777                         if ((*i)->is_mark()) {
778                                 /* MARK, start == end so just compare start */
779                                 if ((*i)->start() > frame) {
780                                         return (*i)->start();
781                                 }
782                         } else {
783                                 /* RANGE, start != end, compare start and end */
784                                 if ((*i)->start() > frame ) {
785                                         return (*i)->start ();
786                                 }
787                                 if ((*i)->end() > frame) {
788                                         return (*i)->end ();
789                                 }
790                         }
791                 }
792         }
793
794         return max_frames;
795 }
796
797 Location*
798 Locations::end_location () const
799 {
800         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
801                 if ((*i)->is_end()) {
802                         return const_cast<Location*> (*i);
803                 }
804         }
805         return 0;
806 }       
807
808 Location*
809 Locations::start_location () const
810 {
811         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
812                 if ((*i)->is_start()) {
813                         return const_cast<Location*> (*i);
814                 }
815         }
816         return 0;
817 }       
818
819 Location*
820 Locations::auto_loop_location () const
821 {
822         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
823                 if ((*i)->is_auto_loop()) {
824                         return const_cast<Location*> (*i);
825                 }
826         }
827         return 0;
828 }       
829
830 Location*
831 Locations::auto_punch_location () const
832 {
833         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
834                 if ((*i)->is_auto_punch()) {
835                         return const_cast<Location*> (*i);
836                 }
837         }
838        return 0;
839 }       
840
841 uint32_t
842 Locations::num_range_markers () const
843 {
844         uint32_t cnt = 0;
845         Glib::Mutex::Lock lm (lock);
846         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
847                 if ((*i)->is_range_marker()) {
848                         ++cnt;
849                 }
850         }
851         return cnt;
852 }
853
854 Location *
855 Locations::get_location_by_id(PBD::ID id)
856 {
857     LocationList::iterator it;
858     for (it  = locations.begin(); it != locations.end(); it++)
859         if (id == (*it)->id())
860             return *it;
861
862     return 0;
863 }