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