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