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