Collect plugin runtime profile statistics.
[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 #include "pbd/types_convert.h"
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/midi_scene_change.h"
35 #include "ardour/session.h"
36 #include "ardour/audiofilesource.h"
37 #include "ardour/tempo.h"
38 #include "ardour/types_convert.h"
39
40 #include "pbd/i18n.h"
41
42 namespace PBD {
43         DEFINE_ENUM_CONVERT(ARDOUR::Location::Flags);
44 }
45
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49
50 PBD::Signal0<void> Location::scene_changed;
51 PBD::Signal1<void,Location*> Location::name_changed;
52 PBD::Signal1<void,Location*> Location::end_changed;
53 PBD::Signal1<void,Location*> Location::start_changed;
54 PBD::Signal1<void,Location*> Location::flags_changed;
55 PBD::Signal1<void,Location*> Location::lock_changed;
56 PBD::Signal1<void,Location*> Location::position_lock_style_changed;
57 PBD::Signal1<void,Location*> Location::changed;
58
59 Location::Location (Session& s)
60         : SessionHandleRef (s)
61         , _start (0)
62         , _start_beat (0.0)
63         , _end (0)
64         , _end_beat (0.0)
65         , _flags (Flags (0))
66         , _locked (false)
67         , _position_lock_style (AudioTime)
68 {
69         assert (_start >= 0);
70         assert (_end >= 0);
71 }
72
73 /** Construct a new Location, giving it the position lock style determined by glue-new-markers-to-bars-and-beats */
74 Location::Location (Session& s, samplepos_t sample_start, samplepos_t sample_end, const std::string &name, Flags bits, const uint32_t sub_num)
75         : SessionHandleRef (s)
76         , _name (name)
77         , _start (sample_start)
78         , _end (sample_end)
79         , _flags (bits)
80         , _locked (false)
81         , _position_lock_style (s.config.get_glue_new_markers_to_bars_and_beats() ? MusicTime : AudioTime)
82
83 {
84         recompute_beat_from_samples (sub_num);
85
86         assert (_start >= 0);
87         assert (_end >= 0);
88 }
89
90 Location::Location (const Location& other)
91         : SessionHandleRef (other._session)
92         , StatefulDestructible()
93         , _name (other._name)
94         , _start (other._start)
95         , _start_beat (other._start_beat)
96         , _end (other._end)
97         , _end_beat (other._end_beat)
98         , _flags (other._flags)
99         , _position_lock_style (other._position_lock_style)
100
101 {
102         /* copy is not locked even if original was */
103
104         _locked = false;
105
106         assert (_start >= 0);
107         assert (_end >= 0);
108
109         /* scene change is NOT COPIED */
110 }
111
112 Location::Location (Session& s, const XMLNode& node)
113         : SessionHandleRef (s)
114         , _flags (Flags (0))
115         , _position_lock_style (AudioTime)
116 {
117         /* Note: _position_lock_style is initialised above in case set_state doesn't set it
118            (for 2.X session file compatibility).
119         */
120
121         if (set_state (node, Stateful::loading_state_version)) {
122                 throw failed_constructor ();
123         }
124
125         assert (_start >= 0);
126         assert (_end >= 0);
127 }
128
129 bool
130 Location::operator== (const Location& other)
131 {
132         if (_name != other._name ||
133             _start != other._start ||
134             _end != other._end ||
135             _start_beat != other._start_beat ||
136             _end_beat != other._end_beat ||
137             _flags != other._flags ||
138             _position_lock_style != other._position_lock_style) {
139                 return false;
140         }
141         return true;
142 }
143
144 Location*
145 Location::operator= (const Location& other)
146 {
147         if (this == &other) {
148                 return this;
149         }
150
151         _name = other._name;
152         _start = other._start;
153         _start_beat = other._start_beat;
154         _end = other._end;
155         _end_beat = other._end_beat;
156         _flags = other._flags;
157         _position_lock_style = other._position_lock_style;
158
159         /* XXX need to copy scene change */
160
161         /* copy is not locked even if original was */
162
163         _locked = false;
164
165         /* "changed" not emitted on purpose */
166
167         assert (_start >= 0);
168         assert (_end >= 0);
169
170         return this;
171 }
172
173 /** Set location name
174  */
175
176 void
177 Location::set_name (const std::string& str)
178 {
179         _name = str;
180
181         name_changed (this); /* EMIT SIGNAL */
182         NameChanged  (); /* EMIT SIGNAL */
183 }
184
185 /** Set start position.
186  *  @param s New start.
187  *  @param force true to force setting, even if the given new start is after the current end.
188  *  @param allow_beat_recompute True to recompute BEAT start time from the new given start time.
189  */
190 int
191 Location::set_start (samplepos_t s, bool force, bool allow_beat_recompute, const uint32_t sub_num)
192 {
193         if (s < 0) {
194                 return -1;
195         }
196
197         if (_locked) {
198                 return -1;
199         }
200
201         if (!force) {
202                 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || (!is_mark() && s > _end)) {
203                         return -1;
204                 }
205         }
206
207         if (is_mark()) {
208                 if (_start != s) {
209                         _start = s;
210                         _end = s;
211                         if (allow_beat_recompute) {
212                                 recompute_beat_from_samples (sub_num);
213                         }
214
215                         start_changed (this); /* EMIT SIGNAL */
216                         StartChanged (); /* EMIT SIGNAL */
217                         //end_changed (this); /* EMIT SIGNAL */
218                         //EndChanged (); /* EMIT SIGNAL */
219                 }
220
221                 /* moving the start (position) of a marker with a scene change
222                    requires an update in the Scene Changer.
223                 */
224
225                 if (_scene_change) {
226                         scene_changed (); /* EMIT SIGNAL */
227                 }
228
229                 assert (_start >= 0);
230                 assert (_end >= 0);
231
232                 return 0;
233         } else if (!force) {
234                 /* range locations must exceed a minimum duration */
235                 if (_end - s < Config->get_range_location_minimum()) {
236                         return -1;
237                 }
238         }
239
240         if (s != _start) {
241
242                 samplepos_t const old = _start;
243
244                 _start = s;
245                 if (allow_beat_recompute) {
246                         recompute_beat_from_samples (sub_num);
247                 }
248                 start_changed (this); /* EMIT SIGNAL */
249                 StartChanged (); /* EMIT SIGNAL */
250
251                 if (is_session_range ()) {
252                         Session::StartTimeChanged (old); /* EMIT SIGNAL */
253                         AudioFileSource::set_header_position_offset (s);
254                 }
255         }
256
257         assert (_start >= 0);
258
259         return 0;
260 }
261
262 /** Set end position.
263  *  @param s New end.
264  *  @param force true to force setting, even if the given new end is before the current start.
265  *  @param allow_beat_recompute True to recompute BEAT end time from the new given end time.
266  */
267 int
268 Location::set_end (samplepos_t e, bool force, bool allow_beat_recompute, const uint32_t sub_num)
269 {
270         if (e < 0) {
271                 return -1;
272         }
273
274         if (_locked) {
275                 return -1;
276         }
277
278         if (!force) {
279                 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
280                         return -1;
281                 }
282         }
283
284         if (is_mark()) {
285                 if (_start != e) {
286                         _start = e;
287                         _end = e;
288                         if (allow_beat_recompute) {
289                                 recompute_beat_from_samples (sub_num);
290                         }
291                         //start_changed (this); /* EMIT SIGNAL */
292                         //StartChanged (); /* EMIT SIGNAL */
293                         end_changed (this); /* EMIT SIGNAL */
294                         EndChanged (); /* EMIT SIGNAL */
295                 }
296
297                 assert (_start >= 0);
298                 assert (_end >= 0);
299
300                 return 0;
301         } else if (!force) {
302                 /* range locations must exceed a minimum duration */
303                 if (e - _start < Config->get_range_location_minimum()) {
304                         return -1;
305                 }
306         }
307
308         if (e != _end) {
309
310                 samplepos_t const old = _end;
311
312                 _end = e;
313                 if (allow_beat_recompute) {
314                         recompute_beat_from_samples (sub_num);
315                 }
316
317                 end_changed(this); /* EMIT SIGNAL */
318                 EndChanged(); /* EMIT SIGNAL */
319
320                 if (is_session_range()) {
321                         Session::EndTimeChanged (old); /* EMIT SIGNAL */
322                 }
323         }
324
325         assert (_end >= 0);
326
327         return 0;
328 }
329
330 int
331 Location::set (samplepos_t s, samplepos_t e, bool allow_beat_recompute, const uint32_t sub_num)
332 {
333         if (s < 0 || e < 0) {
334                 return -1;
335         }
336
337         /* check validity */
338         if (((is_auto_punch() || is_auto_loop()) && s >= e) || (!is_mark() && s > e)) {
339                 return -1;
340         }
341
342         bool start_change = false;
343         bool end_change = false;
344
345         if (is_mark()) {
346
347                 if (_start != s) {
348                         _start = s;
349                         _end = s;
350
351                         if (allow_beat_recompute) {
352                                 recompute_beat_from_samples (sub_num);
353                         }
354
355                         start_change = true;
356                         end_change = true;
357                 }
358
359                 assert (_start >= 0);
360                 assert (_end >= 0);
361
362         } else {
363
364                 /* range locations must exceed a minimum duration */
365                 if (e - s < Config->get_range_location_minimum()) {
366                         return -1;
367                 }
368
369                 if (s != _start) {
370
371                         samplepos_t const old = _start;
372                         _start = s;
373
374                         if (allow_beat_recompute) {
375                                 recompute_beat_from_samples (sub_num);
376                         }
377
378                         start_change = true;
379
380                         if (is_session_range ()) {
381                                 Session::StartTimeChanged (old); /* EMIT SIGNAL */
382                                 AudioFileSource::set_header_position_offset (s);
383                         }
384                 }
385
386
387                 if (e != _end) {
388
389                         samplepos_t const old = _end;
390                         _end = e;
391
392                         if (allow_beat_recompute) {
393                                 recompute_beat_from_samples (sub_num);
394                         }
395
396                         end_change = true;
397
398                         if (is_session_range()) {
399                                 Session::EndTimeChanged (old); /* EMIT SIGNAL */
400                         }
401                 }
402
403                 assert (_end >= 0);
404         }
405
406         if (start_change && end_change) {
407                 changed (this);
408                 Changed ();
409         } else if (start_change) {
410                 start_changed(this); /* EMIT SIGNAL */
411                 StartChanged(); /* EMIT SIGNAL */
412         } else if (end_change) {
413                 end_changed(this); /* EMIT SIGNAL */
414                 EndChanged(); /* EMIT SIGNAL */
415         }
416
417         return 0;
418 }
419
420 int
421 Location::move_to (samplepos_t pos, const uint32_t sub_num)
422 {
423         if (pos < 0) {
424                 return -1;
425         }
426
427         if (_locked) {
428                 return -1;
429         }
430
431         if (_start != pos) {
432                 _start = pos;
433                 _end = _start + length();
434                 recompute_beat_from_samples (sub_num);
435
436                 changed (this); /* EMIT SIGNAL */
437                 Changed (); /* EMIT SIGNAL */
438         }
439
440         assert (_start >= 0);
441         assert (_end >= 0);
442
443         return 0;
444 }
445
446 void
447 Location::set_hidden (bool yn, void*)
448 {
449         if (set_flag_internal (yn, IsHidden)) {
450                 flags_changed (this); /* EMIT SIGNAL */
451                 FlagsChanged ();
452         }
453 }
454
455 void
456 Location::set_cd (bool yn, void*)
457 {
458         // XXX this really needs to be session start
459         // but its not available here - leave to GUI
460
461         if (yn && _start == 0) {
462                 error << _("You cannot put a CD marker at this position") << endmsg;
463                 return;
464         }
465
466         if (set_flag_internal (yn, IsCDMarker)) {
467                 flags_changed (this); /* EMIT SIGNAL */
468                 FlagsChanged ();
469         }
470 }
471
472 void
473 Location::set_is_range_marker (bool yn, void*)
474 {
475         if (set_flag_internal (yn, IsRangeMarker)) {
476                 flags_changed (this);
477                 FlagsChanged (); /* EMIT SIGNAL */
478         }
479 }
480
481 void
482 Location::set_is_clock_origin (bool yn, void*)
483 {
484         if (set_flag_internal (yn, IsClockOrigin)) {
485                 flags_changed (this);
486                 FlagsChanged (); /* EMIT SIGNAL */
487         }
488 }
489
490 void
491 Location::set_skip (bool yn)
492 {
493         if (is_range_marker() && length() > 0) {
494                 if (set_flag_internal (yn, IsSkip)) {
495                         flags_changed (this);
496                         FlagsChanged ();
497                 }
498         }
499 }
500
501 void
502 Location::set_skipping (bool yn)
503 {
504         if (is_range_marker() && is_skip() && length() > 0) {
505                 if (set_flag_internal (yn, IsSkipping)) {
506                         flags_changed (this);
507                         FlagsChanged ();
508                 }
509         }
510 }
511
512 void
513 Location::set_auto_punch (bool yn, void*)
514 {
515         if (is_mark() || _start == _end) {
516                 return;
517         }
518
519         if (set_flag_internal (yn, IsAutoPunch)) {
520                 flags_changed (this); /* EMIT SIGNAL */
521                 FlagsChanged (); /* EMIT SIGNAL */
522         }
523 }
524
525 void
526 Location::set_auto_loop (bool yn, void*)
527 {
528         if (is_mark() || _start == _end) {
529                 return;
530         }
531
532         if (set_flag_internal (yn, IsAutoLoop)) {
533                 flags_changed (this); /* EMIT SIGNAL */
534                 FlagsChanged (); /* EMIT SIGNAL */
535         }
536 }
537
538 bool
539 Location::set_flag_internal (bool yn, Flags flag)
540 {
541         if (yn) {
542                 if (!(_flags & flag)) {
543                         _flags = Flags (_flags | flag);
544                         return true;
545                 }
546         } else {
547                 if (_flags & flag) {
548                         _flags = Flags (_flags & ~flag);
549                         return true;
550                 }
551         }
552         return false;
553 }
554
555 void
556 Location::set_mark (bool yn)
557 {
558         /* This function is private, and so does not emit signals */
559
560         if (_start != _end) {
561                 return;
562         }
563
564         set_flag_internal (yn, IsMark);
565 }
566
567
568 XMLNode&
569 Location::cd_info_node(const string & name, const string & value)
570 {
571         XMLNode* root = new XMLNode("CD-Info");
572
573         root->set_property("name", name);
574         root->set_property("value", value);
575
576         return *root;
577 }
578
579
580 XMLNode&
581 Location::get_state ()
582 {
583         XMLNode *node = new XMLNode ("Location");
584
585         typedef map<string, string>::const_iterator CI;
586
587         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
588                 node->add_child_nocopy(cd_info_node(m->first, m->second));
589         }
590
591         node->set_property ("id", id ());
592         node->set_property ("name", name());
593         node->set_property ("start", start());
594         node->set_property ("end", end());
595         if (position_lock_style() == MusicTime) {
596                 node->set_property ("start-beat", _start_beat);
597                 node->set_property ("end-beat", _end_beat);
598         }
599         node->set_property ("flags", _flags);
600         node->set_property ("locked", _locked);
601         node->set_property ("position-lock-style", _position_lock_style);
602
603         if (_scene_change) {
604                 node->add_child_nocopy (_scene_change->get_state());
605         }
606
607         return *node;
608 }
609
610 int
611 Location::set_state (const XMLNode& node, int version)
612 {
613         XMLNodeList cd_list = node.children();
614         XMLNodeConstIterator cd_iter;
615         XMLNode *cd_node;
616
617         string cd_name;
618         string cd_value;
619
620         if (node.name() != "Location") {
621                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
622                 return -1;
623         }
624
625         if (!set_id (node)) {
626                 warning << _("XML node for Location has no ID information") << endmsg;
627         }
628
629         std::string str;
630         if (!node.get_property ("name", str)) {
631                 error << _("XML node for Location has no name information") << endmsg;
632                 return -1;
633         }
634
635         set_name (str);
636
637         /* can't use set_start() here, because _end
638            may make the value of _start illegal.
639         */
640
641         if (!node.get_property ("start", _start)) {
642                 error << _("XML node for Location has no start information") << endmsg;
643                 return -1;
644         }
645
646         if (!node.get_property ("end", _end)) {
647                 error << _("XML node for Location has no end information") << endmsg;
648                 return -1;
649         }
650
651         Flags old_flags (_flags);
652
653         if (!node.get_property ("flags", _flags)) {
654                 error << _("XML node for Location has no flags information") << endmsg;
655                 return -1;
656         }
657
658         if (old_flags != _flags) {
659                 FlagsChanged ();
660         }
661
662         if (!node.get_property ("locked", _locked)) {
663                 _locked = false;
664         }
665
666         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
667
668                 cd_node = *cd_iter;
669
670                 if (cd_node->name() != "CD-Info") {
671                         continue;
672                 }
673
674                 if (!cd_node->get_property ("name", cd_name)) {
675                         throw failed_constructor ();
676                 }
677
678                 if (!cd_node->get_property ("value", cd_value)) {
679                         throw failed_constructor ();
680                 }
681
682                 cd_info[cd_name] = cd_value;
683         }
684
685         node.get_property ("position-lock-style", _position_lock_style);
686
687         XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
688
689         if (scene_child) {
690                 _scene_change = SceneChange::factory (*scene_child, version);
691         }
692
693         if (position_lock_style() == AudioTime) {
694                 recompute_beat_from_samples (0);
695         } else{
696                 /* music */
697                 if (!node.get_property ("start-beat", _start_beat) ||
698                     !node.get_property ("end-beat", _end_beat)) {
699                         recompute_beat_from_samples (0);
700                 }
701         }
702
703
704         changed (this); /* EMIT SIGNAL */
705         Changed (); /* EMIT SIGNAL */
706
707         assert (_start >= 0);
708         assert (_end >= 0);
709
710         return 0;
711 }
712
713 void
714 Location::set_position_lock_style (PositionLockStyle ps)
715 {
716         if (_position_lock_style == ps) {
717                 return;
718         }
719
720         _position_lock_style = ps;
721
722         if (ps == MusicTime) {
723                 recompute_beat_from_samples (0);
724         }
725
726         position_lock_style_changed (this); /* EMIT SIGNAL */
727         PositionLockStyleChanged (); /* EMIT SIGNAL */
728 }
729
730 void
731 Location::recompute_beat_from_samples (const uint32_t sub_num)
732 {
733         _start_beat = _session.tempo_map().exact_beat_at_sample (_start, sub_num);
734         _end_beat = _session.tempo_map().exact_beat_at_sample (_end, sub_num);
735 }
736
737 void
738 Location::recompute_samples_from_beat ()
739 {
740         if (_position_lock_style != MusicTime) {
741                 return;
742         }
743
744         TempoMap& map (_session.tempo_map());
745         set (map.sample_at_beat (_start_beat), map.sample_at_beat (_end_beat), false);
746 }
747
748 void
749 Location::lock ()
750 {
751         _locked = true;
752         lock_changed (this);
753         LockChanged ();
754 }
755
756 void
757 Location::unlock ()
758 {
759         _locked = false;
760         lock_changed (this);
761         LockChanged ();
762 }
763
764 void
765 Location::set_scene_change (boost::shared_ptr<SceneChange>  sc)
766 {
767         if (_scene_change != sc) {
768                 _scene_change = sc;
769                 _session.set_dirty ();
770
771                 scene_changed (); /* EMIT SIGNAL */
772                 SceneChangeChanged (); /* EMIT SIGNAL */
773         }
774 }
775
776 /*---------------------------------------------------------------------- */
777
778 Locations::Locations (Session& s)
779         : SessionHandleRef (s)
780 {
781         current_location = 0;
782 }
783
784 Locations::~Locations ()
785 {
786         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
787                 LocationList::iterator tmp = i;
788                 ++tmp;
789                 delete *i;
790                 i = tmp;
791         }
792 }
793
794 int
795 Locations::set_current (Location *loc, bool want_lock)
796 {
797         int ret;
798
799         if (want_lock) {
800                 Glib::Threads::Mutex::Lock lm (lock);
801                 ret = set_current_unlocked (loc);
802         } else {
803                 ret = set_current_unlocked (loc);
804         }
805
806         if (ret == 0) {
807                 current_changed (current_location); /* EMIT SIGNAL */
808         }
809         return ret;
810 }
811
812 void
813 Locations::set_clock_origin (Location* loc, void *src)
814 {
815         LocationList::iterator i;
816         for (i = locations.begin(); i != locations.end(); ++i) {
817                 if ((*i)->is_clock_origin ()) {
818                         (*i)->set_is_clock_origin (false, src);
819                 }
820                 if (*i == loc) {
821                         (*i)->set_is_clock_origin (true, src);
822                 }
823         }
824 }
825
826 int
827 Locations::next_available_name(string& result,string base)
828 {
829         LocationList::iterator i;
830         string::size_type l;
831         int suffix;
832         char buf[32];
833         std::map<uint32_t,bool> taken;
834         uint32_t n;
835
836         result = base;
837         l = base.length();
838
839         if (!base.empty()) {
840
841                 /* find all existing names that match "base", and store
842                    the numeric part of them (if any) in the map "taken"
843                 */
844
845                 for (i = locations.begin(); i != locations.end(); ++i) {
846
847                         const string& temp ((*i)->name());
848
849                         if (!temp.find (base,0)) {
850                                 /* grab what comes after the "base" as if it was
851                                    a number, and assuming that works OK,
852                                    store it in "taken" so that we know it
853                                    has been used.
854                                 */
855                                 if ((suffix = atoi (temp.substr(l))) != 0) {
856                                         taken.insert (make_pair (suffix,true));
857                                 }
858                         }
859                 }
860         }
861
862         /* Now search for an un-used suffix to add to "base". This
863            will find "holes" in the numbering sequence when a location
864            was deleted.
865
866            This must start at 1, both for human-numbering reasons
867            and also because the call to atoi() above would return
868            zero if there is no recognizable numeric suffix, causing
869            "base 0" not to be inserted into the "taken" map.
870         */
871
872         n = 1;
873
874         while (n < UINT32_MAX) {
875                 if (taken.find (n) == taken.end()) {
876                         snprintf (buf, sizeof(buf), "%d", n);
877                         result += buf;
878                         return 1;
879                 }
880                 ++n;
881         }
882
883         return 0;
884 }
885
886 int
887 Locations::set_current_unlocked (Location *loc)
888 {
889         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
890                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
891                 return -1;
892         }
893
894         current_location = loc;
895         return 0;
896 }
897
898 void
899 Locations::clear ()
900 {
901         {
902                 Glib::Threads::Mutex::Lock lm (lock);
903
904                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
905
906                         LocationList::iterator tmp = i;
907                         ++tmp;
908
909                         if (!(*i)->is_session_range()) {
910                                 delete *i;
911                                 locations.erase (i);
912                         }
913
914                         i = tmp;
915                 }
916
917                 current_location = 0;
918         }
919
920         changed (); /* EMIT SIGNAL */
921         current_changed (0); /* EMIT SIGNAL */
922 }
923
924 void
925 Locations::clear_markers ()
926 {
927         {
928                 Glib::Threads::Mutex::Lock lm (lock);
929                 LocationList::iterator tmp;
930
931                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
932                         tmp = i;
933                         ++tmp;
934
935                         if ((*i)->is_mark() && !(*i)->is_session_range()) {
936                                 delete *i;
937                                 locations.erase (i);
938                         }
939
940                         i = tmp;
941                 }
942         }
943
944         changed (); /* EMIT SIGNAL */
945 }
946
947 void
948 Locations::clear_ranges ()
949 {
950         {
951                 Glib::Threads::Mutex::Lock lm (lock);
952                 LocationList::iterator tmp;
953
954                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
955
956                         tmp = i;
957                         ++tmp;
958
959                         /* We do not remove these ranges as part of this
960                          * operation
961                          */
962
963                         if ((*i)->is_auto_punch() ||
964                             (*i)->is_auto_loop() ||
965                             (*i)->is_session_range()) {
966                                 i = tmp;
967                                 continue;
968                         }
969
970                         if (!(*i)->is_mark()) {
971                                 delete *i;
972                                 locations.erase (i);
973
974                         }
975
976                         i = tmp;
977                 }
978
979                 current_location = 0;
980         }
981
982         changed ();
983         current_changed (0); /* EMIT SIGNAL */
984 }
985
986 void
987 Locations::add (Location *loc, bool make_current)
988 {
989         assert (loc);
990
991         {
992                 Glib::Threads::Mutex::Lock lm (lock);
993                 locations.push_back (loc);
994
995                 if (make_current) {
996                         current_location = loc;
997                 }
998         }
999
1000         added (loc); /* EMIT SIGNAL */
1001
1002         if (make_current) {
1003                 current_changed (current_location); /* EMIT SIGNAL */
1004         }
1005
1006         if (loc->is_session_range()) {
1007                 Session::StartTimeChanged (0);
1008                 Session::EndTimeChanged (1);
1009         }
1010 }
1011
1012 void
1013 Locations::remove (Location *loc)
1014 {
1015         bool was_removed = false;
1016         bool was_current = false;
1017         LocationList::iterator i;
1018
1019         if (!loc) {
1020                 return;
1021         }
1022
1023         if (loc->is_session_range()) {
1024                 return;
1025         }
1026
1027         {
1028                 Glib::Threads::Mutex::Lock lm (lock);
1029
1030                 for (i = locations.begin(); i != locations.end(); ++i) {
1031                         if ((*i) == loc) {
1032                                 bool was_loop = (*i)->is_auto_loop();
1033                                 delete *i;
1034                                 locations.erase (i);
1035                                 was_removed = true;
1036                                 if (current_location == loc) {
1037                                         current_location = 0;
1038                                         was_current = true;
1039                                 }
1040                                 if (was_loop) {
1041                                         if (_session.get_play_loop()) {
1042                                                 _session.request_play_loop (false, false);
1043                                         }
1044                                         _session.auto_loop_location_changed (0);
1045                                 }
1046                                 break;
1047                         }
1048                 }
1049         }
1050
1051         if (was_removed) {
1052
1053                 removed (loc); /* EMIT SIGNAL */
1054
1055                 if (was_current) {
1056                         current_changed (0); /* EMIT SIGNAL */
1057                 }
1058         }
1059 }
1060
1061 XMLNode&
1062 Locations::get_state ()
1063 {
1064         XMLNode *node = new XMLNode ("Locations");
1065         LocationList::iterator iter;
1066         Glib::Threads::Mutex::Lock lm (lock);
1067
1068         for (iter = locations.begin(); iter != locations.end(); ++iter) {
1069                 node->add_child_nocopy ((*iter)->get_state ());
1070         }
1071
1072         return *node;
1073 }
1074
1075 int
1076 Locations::set_state (const XMLNode& node, int version)
1077 {
1078         if (node.name() != "Locations") {
1079                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1080                 return -1;
1081         }
1082
1083         XMLNodeList nlist = node.children();
1084
1085         /* build up a new locations list in here */
1086         LocationList new_locations;
1087
1088         current_location = 0;
1089
1090         Location* session_range_location = 0;
1091         if (version < 3000) {
1092                 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1093                 new_locations.push_back (session_range_location);
1094         }
1095
1096         {
1097                 Glib::Threads::Mutex::Lock lm (lock);
1098
1099                 XMLNodeConstIterator niter;
1100                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1101
1102                         try {
1103
1104                                 XMLProperty const * prop_id = (*niter)->property ("id");
1105                                 assert (prop_id);
1106                                 PBD::ID id (prop_id->value ());
1107
1108                                 LocationList::const_iterator i = locations.begin();
1109                                 while (i != locations.end () && (*i)->id() != id) {
1110                                         ++i;
1111                                 }
1112
1113                                 Location* loc;
1114                                 if (i != locations.end()) {
1115                                         /* we can re-use an old Location object */
1116                                         loc = *i;
1117
1118                                         // changed locations will be updated by Locations::changed signal
1119                                         loc->set_state (**niter, version);
1120                                 } else {
1121                                         loc = new Location (_session, **niter);
1122                                 }
1123
1124                                 bool add = true;
1125
1126                                 if (version < 3000) {
1127                                         /* look for old-style IsStart / IsEnd properties in this location;
1128                                            if they are present, update the session_range_location accordingly
1129                                         */
1130                                         XMLProperty const * prop = (*niter)->property ("flags");
1131                                         if (prop) {
1132                                                 string v = prop->value ();
1133                                                 while (1) {
1134                                                         string::size_type const c = v.find_first_of (',');
1135                                                         string const s = v.substr (0, c);
1136                                                         if (s == X_("IsStart")) {
1137                                                                 session_range_location->set_start (loc->start(), true);
1138                                                                 add = false;
1139                                                         } else if (s == X_("IsEnd")) {
1140                                                                 session_range_location->set_end (loc->start(), true);
1141                                                                 add = false;
1142                                                         }
1143
1144                                                         if (c == string::npos) {
1145                                                                 break;
1146                                                         }
1147
1148                                                         v = v.substr (c + 1);
1149                                                 }
1150                                         }
1151                                 }
1152
1153                                 if (add) {
1154                                         new_locations.push_back (loc);
1155                                 }
1156                         }
1157
1158                         catch (failed_constructor& err) {
1159                                 error << _("could not load location from session file - ignored") << endmsg;
1160                         }
1161                 }
1162
1163                 /* We may have some unused locations in the old list. */
1164                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1165                         LocationList::iterator tmp = i;
1166                         ++tmp;
1167
1168                         LocationList::iterator n = new_locations.begin();
1169                         bool found = false;
1170
1171                         while (n != new_locations.end ()) {
1172                                 if ((*i)->id() == (*n)->id()) {
1173                                         found = true;
1174                                         break;
1175                                 }
1176                                 ++n;
1177                         }
1178
1179                         if (!found) {
1180                                 delete *i;
1181                                 locations.erase (i);
1182                         }
1183
1184                         i = tmp;
1185                 }
1186
1187                 locations = new_locations;
1188
1189                 if (locations.size()) {
1190                         current_location = locations.front();
1191                 } else {
1192                         current_location = 0;
1193                 }
1194         }
1195
1196         changed (); /* EMIT SIGNAL */
1197
1198         return 0;
1199 }
1200
1201
1202 typedef std::pair<samplepos_t,Location*> LocationPair;
1203
1204 struct LocationStartEarlierComparison
1205 {
1206         bool operator() (LocationPair a, LocationPair b) {
1207                 return a.first < b.first;
1208         }
1209 };
1210
1211 struct LocationStartLaterComparison
1212 {
1213         bool operator() (LocationPair a, LocationPair b) {
1214                 return a.first > b.first;
1215         }
1216 };
1217
1218 samplepos_t
1219 Locations::first_mark_before (samplepos_t sample, bool include_special_ranges)
1220 {
1221         Glib::Threads::Mutex::Lock lm (lock);
1222         vector<LocationPair> locs;
1223
1224         for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1225                 locs.push_back (make_pair ((*i)->start(), (*i)));
1226                 if (!(*i)->is_mark()) {
1227                         locs.push_back (make_pair ((*i)->end(), (*i)));
1228                 }
1229         }
1230
1231         LocationStartLaterComparison cmp;
1232         sort (locs.begin(), locs.end(), cmp);
1233
1234         /* locs is sorted in ascending order */
1235
1236         for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1237                 if ((*i).second->is_hidden()) {
1238                         continue;
1239                 }
1240                 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1241                         continue;
1242                 }
1243                 if ((*i).first < sample) {
1244                         return (*i).first;
1245                 }
1246         }
1247
1248         return -1;
1249 }
1250
1251 Location*
1252 Locations::mark_at (samplepos_t pos, samplecnt_t slop) const
1253 {
1254         Glib::Threads::Mutex::Lock lm (lock);
1255         Location* closest = 0;
1256         sampleoffset_t mindelta = max_samplepos;
1257         sampleoffset_t delta;
1258
1259         /* locations are not necessarily stored in linear time order so we have
1260          * to iterate across all of them to find the one closest to a give point.
1261          */
1262
1263         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1264
1265                 if ((*i)->is_mark()) {
1266                         if (pos > (*i)->start()) {
1267                                 delta = pos - (*i)->start();
1268                         } else {
1269                                 delta = (*i)->start() - pos;
1270                         }
1271
1272                         if (slop == 0 && delta == 0) {
1273                                 /* special case: no slop, and direct hit for position */
1274                                 return *i;
1275                         }
1276
1277                         if (delta <= slop) {
1278                                 if (delta < mindelta) {
1279                                         closest = *i;
1280                                         mindelta = delta;
1281                                 }
1282                         }
1283                 }
1284         }
1285
1286         return closest;
1287 }
1288
1289 samplepos_t
1290 Locations::first_mark_after (samplepos_t sample, bool include_special_ranges)
1291 {
1292         Glib::Threads::Mutex::Lock lm (lock);
1293         vector<LocationPair> locs;
1294
1295         for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1296                 locs.push_back (make_pair ((*i)->start(), (*i)));
1297                 if (!(*i)->is_mark()) {
1298                         locs.push_back (make_pair ((*i)->end(), (*i)));
1299                 }
1300         }
1301
1302         LocationStartEarlierComparison cmp;
1303         sort (locs.begin(), locs.end(), cmp);
1304
1305         /* locs is sorted in reverse order */
1306
1307         for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1308                 if ((*i).second->is_hidden()) {
1309                         continue;
1310                 }
1311                 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1312                         continue;
1313                 }
1314                 if ((*i).first > sample) {
1315                         return (*i).first;
1316                 }
1317         }
1318
1319         return -1;
1320 }
1321
1322 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1323  *  side of a sample.  Note that if sample is exactly on a `mark', that mark will not be considered for returning
1324  *  as before/after.
1325  *  @param sample Frame to look for.
1326  *  @param before Filled in with the position of the last `mark' before `sample' (or max_samplepos if none exists)
1327  *  @param after Filled in with the position of the next `mark' after `sample' (or max_samplepos if none exists)
1328  */
1329 void
1330 Locations::marks_either_side (samplepos_t const sample, samplepos_t& before, samplepos_t& after) const
1331 {
1332         before = after = max_samplepos;
1333
1334         LocationList locs;
1335
1336         {
1337                 Glib::Threads::Mutex::Lock lm (lock);
1338                 locs = locations;
1339         }
1340
1341         /* Get a list of positions; don't store any that are exactly on our requested position */
1342
1343         std::list<samplepos_t> positions;
1344
1345         for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1346                 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1347                         continue;
1348                 }
1349
1350                 if (!(*i)->is_hidden()) {
1351                         if ((*i)->is_mark ()) {
1352                                 if ((*i)->start() != sample) {
1353                                         positions.push_back ((*i)->start ());
1354                                 }
1355                         } else {
1356                                 if ((*i)->start() != sample) {
1357                                         positions.push_back ((*i)->start ());
1358                                 }
1359                                 if ((*i)->end() != sample) {
1360                                         positions.push_back ((*i)->end ());
1361                                 }
1362                         }
1363                 }
1364         }
1365
1366         if (positions.empty ()) {
1367                 return;
1368         }
1369
1370         positions.sort ();
1371
1372         std::list<samplepos_t>::iterator i = positions.begin ();
1373         while (i != positions.end () && *i < sample) {
1374                 ++i;
1375         }
1376
1377         if (i == positions.end ()) {
1378                 /* run out of marks */
1379                 before = positions.back ();
1380                 return;
1381         }
1382
1383         after = *i;
1384
1385         if (i == positions.begin ()) {
1386                 /* none before */
1387                 return;
1388         }
1389
1390         --i;
1391         before = *i;
1392 }
1393
1394 Location*
1395 Locations::session_range_location () const
1396 {
1397         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1398                 if ((*i)->is_session_range()) {
1399                         return const_cast<Location*> (*i);
1400                 }
1401         }
1402         return 0;
1403 }
1404
1405 Location*
1406 Locations::auto_loop_location () const
1407 {
1408         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1409                 if ((*i)->is_auto_loop()) {
1410                         return const_cast<Location*> (*i);
1411                 }
1412         }
1413         return 0;
1414 }
1415
1416 Location*
1417 Locations::auto_punch_location () const
1418 {
1419         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1420                 if ((*i)->is_auto_punch()) {
1421                         return const_cast<Location*> (*i);
1422                 }
1423         }
1424         return 0;
1425 }
1426
1427 Location*
1428 Locations::clock_origin_location () const
1429 {
1430         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1431                 if ((*i)->is_clock_origin()) {
1432                         return const_cast<Location*> (*i);
1433                 }
1434         }
1435         return session_range_location ();
1436 }
1437
1438 uint32_t
1439 Locations::num_range_markers () const
1440 {
1441         uint32_t cnt = 0;
1442         Glib::Threads::Mutex::Lock lm (lock);
1443         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1444                 if ((*i)->is_range_marker()) {
1445                         ++cnt;
1446                 }
1447         }
1448         return cnt;
1449 }
1450
1451 Location *
1452 Locations::get_location_by_id(PBD::ID id)
1453 {
1454         LocationList::iterator it;
1455         for (it  = locations.begin(); it != locations.end(); ++it)
1456                 if (id == (*it)->id())
1457                         return *it;
1458
1459         return 0;
1460 }
1461
1462 void
1463 Locations::find_all_between (samplepos_t start, samplepos_t end, LocationList& ll, Location::Flags flags)
1464 {
1465         Glib::Threads::Mutex::Lock lm (lock);
1466
1467         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1468                 if ((flags == 0 || (*i)->matches (flags)) &&
1469                     ((*i)->start() >= start && (*i)->end() < end)) {
1470                         ll.push_back (*i);
1471                 }
1472         }
1473 }