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