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