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