2 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2006 Hans Fugal <hans@fugal.net>
4 * Copyright (C) 2007-2011 David Robillard <d@drobilla.net>
5 * Copyright (C) 2008-2009 Sakari Bergen <sakari.bergen@beatwaves.net>
6 * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
7 * Copyright (C) 2015-2016 Nick Mainsbridge <mainsbridge@gmail.com>
8 * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
9 * Copyright (C) 2015 GZharun <grygoriiz@wavesglobal.com>
10 * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <cstdio> /* for sprintf */
35 #include "pbd/types_convert.h"
36 #include "pbd/stl_delete.h"
37 #include "pbd/xml++.h"
38 #include "pbd/enumwriter.h"
40 #include "ardour/location.h"
41 #include "ardour/midi_scene_change.h"
42 #include "ardour/session.h"
43 #include "ardour/audiofilesource.h"
44 #include "ardour/tempo.h"
45 #include "ardour/types_convert.h"
50 DEFINE_ENUM_CONVERT(ARDOUR::Location::Flags);
54 using namespace ARDOUR;
57 PBD::Signal0<void> Location::scene_changed;
58 PBD::Signal1<void,Location*> Location::name_changed;
59 PBD::Signal1<void,Location*> Location::end_changed;
60 PBD::Signal1<void,Location*> Location::start_changed;
61 PBD::Signal1<void,Location*> Location::flags_changed;
62 PBD::Signal1<void,Location*> Location::lock_changed;
63 PBD::Signal1<void,Location*> Location::position_lock_style_changed;
64 PBD::Signal1<void,Location*> Location::changed;
66 Location::Location (Session& s)
67 : SessionHandleRef (s)
74 , _position_lock_style (AudioTime)
75 , _timestamp (time (0))
81 /** Construct a new Location, giving it the position lock style determined by glue-new-markers-to-bars-and-beats */
82 Location::Location (Session& s, samplepos_t sample_start, samplepos_t sample_end, const std::string &name, Flags bits, const uint32_t sub_num)
83 : SessionHandleRef (s)
85 , _start (sample_start)
89 , _position_lock_style (s.config.get_glue_new_markers_to_bars_and_beats() ? MusicTime : AudioTime)
90 , _timestamp (time (0))
92 recompute_beat_from_samples (sub_num);
98 Location::Location (const Location& other)
99 : SessionHandleRef (other._session)
100 , StatefulDestructible()
101 , _name (other._name)
102 , _start (other._start)
103 , _start_beat (other._start_beat)
105 , _end_beat (other._end_beat)
106 , _flags (other._flags)
107 , _position_lock_style (other._position_lock_style)
108 , _timestamp (time (0))
110 /* copy is not locked even if original was */
114 assert (_start >= 0);
117 /* scene change is NOT COPIED */
120 Location::Location (Session& s, const XMLNode& node)
121 : SessionHandleRef (s)
123 , _position_lock_style (AudioTime)
124 , _timestamp (time (0))
126 /* Note: _position_lock_style is initialised above in case set_state doesn't set it
127 (for 2.X session file compatibility).
130 if (set_state (node, Stateful::loading_state_version)) {
131 throw failed_constructor ();
134 assert (_start >= 0);
139 Location::operator== (const Location& other)
141 if (_name != other._name ||
142 _start != other._start ||
143 _end != other._end ||
144 _start_beat != other._start_beat ||
145 _end_beat != other._end_beat ||
146 _flags != other._flags ||
147 _position_lock_style != other._position_lock_style) {
154 Location::operator= (const Location& other)
156 if (this == &other) {
161 _start = other._start;
162 _start_beat = other._start_beat;
164 _end_beat = other._end_beat;
165 _flags = other._flags;
166 _position_lock_style = other._position_lock_style;
168 /* XXX need to copy scene change */
170 /* copy is not locked even if original was */
174 /* "changed" not emitted on purpose */
176 assert (_start >= 0);
182 /** Set location name
186 Location::set_name (const std::string& str)
190 name_changed (this); /* EMIT SIGNAL */
191 NameChanged (); /* EMIT SIGNAL */
194 /** Set start position.
195 * @param s New start.
196 * @param force true to force setting, even if the given new start is after the current end.
197 * @param allow_beat_recompute True to recompute BEAT start time from the new given start time.
200 Location::set_start (samplepos_t s, bool force, bool allow_beat_recompute, const uint32_t sub_num)
211 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || (!is_mark() && s > _end)) {
220 if (allow_beat_recompute) {
221 recompute_beat_from_samples (sub_num);
224 start_changed (this); /* EMIT SIGNAL */
225 StartChanged (); /* EMIT SIGNAL */
226 //end_changed (this); /* EMIT SIGNAL */
227 //EndChanged (); /* EMIT SIGNAL */
230 /* moving the start (position) of a marker with a scene change
231 requires an update in the Scene Changer.
235 scene_changed (); /* EMIT SIGNAL */
238 assert (_start >= 0);
243 /* range locations must exceed a minimum duration */
244 if (_end - s < Config->get_range_location_minimum()) {
251 samplepos_t const old = _start;
254 if (allow_beat_recompute) {
255 recompute_beat_from_samples (sub_num);
257 start_changed (this); /* EMIT SIGNAL */
258 StartChanged (); /* EMIT SIGNAL */
260 if (is_session_range ()) {
261 Session::StartTimeChanged (old); /* EMIT SIGNAL */
262 AudioFileSource::set_header_position_offset (s);
266 assert (_start >= 0);
271 /** Set end position.
273 * @param force true to force setting, even if the given new end is before the current start.
274 * @param allow_beat_recompute True to recompute BEAT end time from the new given end time.
277 Location::set_end (samplepos_t e, bool force, bool allow_beat_recompute, const uint32_t sub_num)
288 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
297 if (allow_beat_recompute) {
298 recompute_beat_from_samples (sub_num);
300 //start_changed (this); /* EMIT SIGNAL */
301 //StartChanged (); /* EMIT SIGNAL */
302 end_changed (this); /* EMIT SIGNAL */
303 EndChanged (); /* EMIT SIGNAL */
306 assert (_start >= 0);
311 /* range locations must exceed a minimum duration */
312 if (e - _start < Config->get_range_location_minimum()) {
319 samplepos_t const old = _end;
322 if (allow_beat_recompute) {
323 recompute_beat_from_samples (sub_num);
326 end_changed(this); /* EMIT SIGNAL */
327 EndChanged(); /* EMIT SIGNAL */
329 if (is_session_range()) {
330 Session::EndTimeChanged (old); /* EMIT SIGNAL */
340 Location::set (samplepos_t s, samplepos_t e, bool allow_beat_recompute, const uint32_t sub_num)
342 if (s < 0 || e < 0) {
347 if (((is_auto_punch() || is_auto_loop()) && s >= e) || (!is_mark() && s > e)) {
351 bool start_change = false;
352 bool end_change = false;
360 if (allow_beat_recompute) {
361 recompute_beat_from_samples (sub_num);
368 assert (_start >= 0);
373 /* range locations must exceed a minimum duration */
374 if (e - s < Config->get_range_location_minimum()) {
380 samplepos_t const old = _start;
383 if (allow_beat_recompute) {
384 recompute_beat_from_samples (sub_num);
389 if (is_session_range ()) {
390 Session::StartTimeChanged (old); /* EMIT SIGNAL */
391 AudioFileSource::set_header_position_offset (s);
398 samplepos_t const old = _end;
401 if (allow_beat_recompute) {
402 recompute_beat_from_samples (sub_num);
407 if (is_session_range()) {
408 Session::EndTimeChanged (old); /* EMIT SIGNAL */
415 if (start_change && end_change) {
418 } else if (start_change) {
419 start_changed(this); /* EMIT SIGNAL */
420 StartChanged(); /* EMIT SIGNAL */
421 } else if (end_change) {
422 end_changed(this); /* EMIT SIGNAL */
423 EndChanged(); /* EMIT SIGNAL */
430 Location::move_to (samplepos_t pos, const uint32_t sub_num)
442 _end = _start + length();
443 recompute_beat_from_samples (sub_num);
445 changed (this); /* EMIT SIGNAL */
446 Changed (); /* EMIT SIGNAL */
449 assert (_start >= 0);
456 Location::set_hidden (bool yn, void*)
458 if (set_flag_internal (yn, IsHidden)) {
459 flags_changed (this); /* EMIT SIGNAL */
465 Location::set_cd (bool yn, void*)
467 // XXX this really needs to be session start
468 // but its not available here - leave to GUI
470 if (yn && _start == 0) {
471 error << _("You cannot put a CD marker at this position") << endmsg;
475 if (set_flag_internal (yn, IsCDMarker)) {
476 flags_changed (this); /* EMIT SIGNAL */
482 Location::set_is_range_marker (bool yn, void*)
484 if (set_flag_internal (yn, IsRangeMarker)) {
485 flags_changed (this);
486 FlagsChanged (); /* EMIT SIGNAL */
491 Location::set_is_clock_origin (bool yn, void*)
493 if (set_flag_internal (yn, IsClockOrigin)) {
494 flags_changed (this);
495 FlagsChanged (); /* EMIT SIGNAL */
500 Location::set_skip (bool yn)
502 if (is_range_marker() && length() > 0) {
503 if (set_flag_internal (yn, IsSkip)) {
504 flags_changed (this);
511 Location::set_skipping (bool yn)
513 if (is_range_marker() && is_skip() && length() > 0) {
514 if (set_flag_internal (yn, IsSkipping)) {
515 flags_changed (this);
522 Location::set_auto_punch (bool yn, void*)
524 if (is_mark() || _start == _end) {
528 if (set_flag_internal (yn, IsAutoPunch)) {
529 flags_changed (this); /* EMIT SIGNAL */
530 FlagsChanged (); /* EMIT SIGNAL */
535 Location::set_auto_loop (bool yn, void*)
537 if (is_mark() || _start == _end) {
541 if (set_flag_internal (yn, IsAutoLoop)) {
542 flags_changed (this); /* EMIT SIGNAL */
543 FlagsChanged (); /* EMIT SIGNAL */
548 Location::set_flag_internal (bool yn, Flags flag)
551 if (!(_flags & flag)) {
552 _flags = Flags (_flags | flag);
557 _flags = Flags (_flags & ~flag);
565 Location::set_mark (bool yn)
567 /* This function is private, and so does not emit signals */
569 if (_start != _end) {
573 set_flag_internal (yn, IsMark);
578 Location::cd_info_node(const string & name, const string & value)
580 XMLNode* root = new XMLNode("CD-Info");
582 root->set_property("name", name);
583 root->set_property("value", value);
590 Location::get_state ()
592 XMLNode *node = new XMLNode ("Location");
594 typedef map<string, string>::const_iterator CI;
596 for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
597 node->add_child_nocopy(cd_info_node(m->first, m->second));
600 node->set_property ("id", id ());
601 node->set_property ("name", name());
602 node->set_property ("start", start());
603 node->set_property ("end", end());
604 if (position_lock_style() == MusicTime) {
605 node->set_property ("start-beat", _start_beat);
606 node->set_property ("end-beat", _end_beat);
608 node->set_property ("flags", _flags);
609 node->set_property ("locked", _locked);
610 node->set_property ("position-lock-style", _position_lock_style);
611 node->set_property ("timestamp", _timestamp);
613 node->add_child_nocopy (_scene_change->get_state());
620 Location::set_state (const XMLNode& node, int version)
622 XMLNodeList cd_list = node.children();
623 XMLNodeConstIterator cd_iter;
629 if (node.name() != "Location") {
630 error << _("incorrect XML node passed to Location::set_state") << endmsg;
634 if (!set_id (node)) {
635 warning << _("XML node for Location has no ID information") << endmsg;
639 if (!node.get_property ("name", str)) {
640 error << _("XML node for Location has no name information") << endmsg;
646 /* can't use set_start() here, because _end
647 may make the value of _start illegal.
650 if (!node.get_property ("start", _start)) {
651 error << _("XML node for Location has no start information") << endmsg;
655 if (!node.get_property ("end", _end)) {
656 error << _("XML node for Location has no end information") << endmsg;
660 node.get_property ("timestamp", _timestamp);
662 Flags old_flags (_flags);
664 if (!node.get_property ("flags", _flags)) {
665 error << _("XML node for Location has no flags information") << endmsg;
669 if (old_flags != _flags) {
673 if (!node.get_property ("locked", _locked)) {
677 for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
681 if (cd_node->name() != "CD-Info") {
685 if (!cd_node->get_property ("name", cd_name)) {
686 throw failed_constructor ();
689 if (!cd_node->get_property ("value", cd_value)) {
690 throw failed_constructor ();
693 cd_info[cd_name] = cd_value;
696 node.get_property ("position-lock-style", _position_lock_style);
698 XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
701 _scene_change = SceneChange::factory (*scene_child, version);
704 if (position_lock_style() == AudioTime) {
705 recompute_beat_from_samples (0);
708 if (!node.get_property ("start-beat", _start_beat) ||
709 !node.get_property ("end-beat", _end_beat)) {
710 recompute_beat_from_samples (0);
715 changed (this); /* EMIT SIGNAL */
716 Changed (); /* EMIT SIGNAL */
718 assert (_start >= 0);
725 Location::set_position_lock_style (PositionLockStyle ps)
727 if (_position_lock_style == ps) {
731 _position_lock_style = ps;
733 if (ps == MusicTime) {
734 recompute_beat_from_samples (0);
737 position_lock_style_changed (this); /* EMIT SIGNAL */
738 PositionLockStyleChanged (); /* EMIT SIGNAL */
742 Location::recompute_beat_from_samples (const uint32_t sub_num)
744 _start_beat = _session.tempo_map().exact_beat_at_sample (_start, sub_num);
745 _end_beat = _session.tempo_map().exact_beat_at_sample (_end, sub_num);
749 Location::recompute_samples_from_beat ()
751 if (_position_lock_style != MusicTime) {
755 TempoMap& map (_session.tempo_map());
756 set (map.sample_at_beat (_start_beat), map.sample_at_beat (_end_beat), false);
776 Location::set_scene_change (boost::shared_ptr<SceneChange> sc)
778 if (_scene_change != sc) {
780 _session.set_dirty ();
782 scene_changed (); /* EMIT SIGNAL */
783 SceneChangeChanged (); /* EMIT SIGNAL */
787 /*---------------------------------------------------------------------- */
789 Locations::Locations (Session& s)
790 : SessionHandleRef (s)
792 current_location = 0;
795 Locations::~Locations ()
797 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
798 LocationList::iterator tmp = i;
806 Locations::set_current (Location *loc, bool want_lock)
811 Glib::Threads::Mutex::Lock lm (lock);
812 ret = set_current_unlocked (loc);
814 ret = set_current_unlocked (loc);
818 current_changed (current_location); /* EMIT SIGNAL */
824 Locations::set_clock_origin (Location* loc, void *src)
826 LocationList::iterator i;
827 for (i = locations.begin(); i != locations.end(); ++i) {
828 if ((*i)->is_clock_origin ()) {
829 (*i)->set_is_clock_origin (false, src);
832 (*i)->set_is_clock_origin (true, src);
838 Locations::next_available_name(string& result,string base)
840 LocationList::iterator i;
844 std::map<uint32_t,bool> taken;
852 /* find all existing names that match "base", and store
853 the numeric part of them (if any) in the map "taken"
856 for (i = locations.begin(); i != locations.end(); ++i) {
858 const string& temp ((*i)->name());
860 if (!temp.find (base,0)) {
861 /* grab what comes after the "base" as if it was
862 a number, and assuming that works OK,
863 store it in "taken" so that we know it
866 if ((suffix = atoi (temp.substr(l))) != 0) {
867 taken.insert (make_pair (suffix,true));
873 /* Now search for an un-used suffix to add to "base". This
874 will find "holes" in the numbering sequence when a location
877 This must start at 1, both for human-numbering reasons
878 and also because the call to atoi() above would return
879 zero if there is no recognizable numeric suffix, causing
880 "base 0" not to be inserted into the "taken" map.
885 while (n < UINT32_MAX) {
886 if (taken.find (n) == taken.end()) {
887 snprintf (buf, sizeof(buf), "%d", n);
898 Locations::set_current_unlocked (Location *loc)
900 if (find (locations.begin(), locations.end(), loc) == locations.end()) {
901 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
905 current_location = loc;
913 Glib::Threads::Mutex::Lock lm (lock);
915 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
917 LocationList::iterator tmp = i;
920 if (!(*i)->is_session_range()) {
928 current_location = 0;
931 changed (); /* EMIT SIGNAL */
932 current_changed (0); /* EMIT SIGNAL */
936 Locations::clear_markers ()
939 Glib::Threads::Mutex::Lock lm (lock);
940 LocationList::iterator tmp;
942 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
946 if ((*i)->is_mark() && !(*i)->is_session_range()) {
955 changed (); /* EMIT SIGNAL */
959 Locations::clear_ranges ()
962 Glib::Threads::Mutex::Lock lm (lock);
963 LocationList::iterator tmp;
965 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
970 /* We do not remove these ranges as part of this
974 if ((*i)->is_auto_punch() ||
975 (*i)->is_auto_loop() ||
976 (*i)->is_session_range()) {
981 if (!(*i)->is_mark()) {
990 current_location = 0;
994 current_changed (0); /* EMIT SIGNAL */
998 Locations::add (Location *loc, bool make_current)
1003 Glib::Threads::Mutex::Lock lm (lock);
1004 locations.push_back (loc);
1007 current_location = loc;
1011 added (loc); /* EMIT SIGNAL */
1014 current_changed (current_location); /* EMIT SIGNAL */
1017 if (loc->is_session_range()) {
1018 Session::StartTimeChanged (0);
1019 Session::EndTimeChanged (1);
1024 Locations::remove (Location *loc)
1026 bool was_removed = false;
1027 bool was_current = false;
1028 LocationList::iterator i;
1034 if (loc->is_session_range()) {
1039 Glib::Threads::Mutex::Lock lm (lock);
1041 for (i = locations.begin(); i != locations.end(); ++i) {
1043 bool was_loop = (*i)->is_auto_loop();
1045 locations.erase (i);
1047 if (current_location == loc) {
1048 current_location = 0;
1052 if (_session.get_play_loop()) {
1053 _session.request_play_loop (false, false);
1055 _session.auto_loop_location_changed (0);
1064 removed (loc); /* EMIT SIGNAL */
1067 current_changed (0); /* EMIT SIGNAL */
1073 Locations::get_state ()
1075 XMLNode *node = new XMLNode ("Locations");
1076 LocationList::iterator iter;
1077 Glib::Threads::Mutex::Lock lm (lock);
1079 for (iter = locations.begin(); iter != locations.end(); ++iter) {
1080 node->add_child_nocopy ((*iter)->get_state ());
1087 Locations::set_state (const XMLNode& node, int version)
1089 if (node.name() != "Locations") {
1090 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1094 XMLNodeList nlist = node.children();
1096 /* build up a new locations list in here */
1097 LocationList new_locations;
1099 current_location = 0;
1101 Location* session_range_location = 0;
1102 if (version < 3000) {
1103 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1104 new_locations.push_back (session_range_location);
1108 Glib::Threads::Mutex::Lock lm (lock);
1110 XMLNodeConstIterator niter;
1111 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1115 XMLProperty const * prop_id = (*niter)->property ("id");
1117 PBD::ID id (prop_id->value ());
1119 LocationList::const_iterator i = locations.begin();
1120 while (i != locations.end () && (*i)->id() != id) {
1125 if (i != locations.end()) {
1126 /* we can re-use an old Location object */
1129 // changed locations will be updated by Locations::changed signal
1130 loc->set_state (**niter, version);
1132 loc = new Location (_session, **niter);
1137 if (version < 3000) {
1138 /* look for old-style IsStart / IsEnd properties in this location;
1139 if they are present, update the session_range_location accordingly
1141 XMLProperty const * prop = (*niter)->property ("flags");
1143 string v = prop->value ();
1145 string::size_type const c = v.find_first_of (',');
1146 string const s = v.substr (0, c);
1147 if (s == X_("IsStart")) {
1148 session_range_location->set_start (loc->start(), true);
1150 } else if (s == X_("IsEnd")) {
1151 session_range_location->set_end (loc->start(), true);
1155 if (c == string::npos) {
1159 v = v.substr (c + 1);
1165 new_locations.push_back (loc);
1169 catch (failed_constructor& err) {
1170 error << _("could not load location from session file - ignored") << endmsg;
1174 /* We may have some unused locations in the old list. */
1175 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1176 LocationList::iterator tmp = i;
1179 LocationList::iterator n = new_locations.begin();
1182 while (n != new_locations.end ()) {
1183 if ((*i)->id() == (*n)->id()) {
1192 locations.erase (i);
1198 locations = new_locations;
1200 if (locations.size()) {
1201 current_location = locations.front();
1203 current_location = 0;
1207 changed (); /* EMIT SIGNAL */
1213 typedef std::pair<samplepos_t,Location*> LocationPair;
1215 struct LocationStartEarlierComparison
1217 bool operator() (LocationPair a, LocationPair b) {
1218 return a.first < b.first;
1222 struct LocationStartLaterComparison
1224 bool operator() (LocationPair a, LocationPair b) {
1225 return a.first > b.first;
1230 Locations::first_mark_before (samplepos_t sample, bool include_special_ranges)
1232 Glib::Threads::Mutex::Lock lm (lock);
1233 vector<LocationPair> locs;
1235 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1236 locs.push_back (make_pair ((*i)->start(), (*i)));
1237 if (!(*i)->is_mark()) {
1238 locs.push_back (make_pair ((*i)->end(), (*i)));
1242 LocationStartLaterComparison cmp;
1243 sort (locs.begin(), locs.end(), cmp);
1245 /* locs is sorted in ascending order */
1247 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1248 if ((*i).second->is_hidden()) {
1251 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1254 if ((*i).first < sample) {
1263 Locations::mark_at (samplepos_t pos, samplecnt_t slop) const
1265 Glib::Threads::Mutex::Lock lm (lock);
1266 Location* closest = 0;
1267 sampleoffset_t mindelta = max_samplepos;
1268 sampleoffset_t delta;
1270 /* locations are not necessarily stored in linear time order so we have
1271 * to iterate across all of them to find the one closest to a give point.
1274 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1276 if ((*i)->is_mark()) {
1277 if (pos > (*i)->start()) {
1278 delta = pos - (*i)->start();
1280 delta = (*i)->start() - pos;
1283 if (slop == 0 && delta == 0) {
1284 /* special case: no slop, and direct hit for position */
1288 if (delta <= slop) {
1289 if (delta < mindelta) {
1301 Locations::first_mark_after (samplepos_t sample, bool include_special_ranges)
1303 Glib::Threads::Mutex::Lock lm (lock);
1304 vector<LocationPair> locs;
1306 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1307 locs.push_back (make_pair ((*i)->start(), (*i)));
1308 if (!(*i)->is_mark()) {
1309 locs.push_back (make_pair ((*i)->end(), (*i)));
1313 LocationStartEarlierComparison cmp;
1314 sort (locs.begin(), locs.end(), cmp);
1316 /* locs is sorted in reverse order */
1318 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1319 if ((*i).second->is_hidden()) {
1322 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1325 if ((*i).first > sample) {
1333 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1334 * side of a sample. Note that if sample is exactly on a `mark', that mark will not be considered for returning
1336 * @param sample Frame to look for.
1337 * @param before Filled in with the position of the last `mark' before `sample' (or max_samplepos if none exists)
1338 * @param after Filled in with the position of the next `mark' after `sample' (or max_samplepos if none exists)
1341 Locations::marks_either_side (samplepos_t const sample, samplepos_t& before, samplepos_t& after) const
1343 before = after = max_samplepos;
1348 Glib::Threads::Mutex::Lock lm (lock);
1352 /* Get a list of positions; don't store any that are exactly on our requested position */
1354 std::list<samplepos_t> positions;
1356 for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1357 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1361 if (!(*i)->is_hidden()) {
1362 if ((*i)->is_mark ()) {
1363 if ((*i)->start() != sample) {
1364 positions.push_back ((*i)->start ());
1367 if ((*i)->start() != sample) {
1368 positions.push_back ((*i)->start ());
1370 if ((*i)->end() != sample) {
1371 positions.push_back ((*i)->end ());
1377 if (positions.empty ()) {
1383 std::list<samplepos_t>::iterator i = positions.begin ();
1384 while (i != positions.end () && *i < sample) {
1388 if (i == positions.end ()) {
1389 /* run out of marks */
1390 before = positions.back ();
1396 if (i == positions.begin ()) {
1406 Locations::session_range_location () const
1408 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1409 if ((*i)->is_session_range()) {
1410 return const_cast<Location*> (*i);
1417 Locations::auto_loop_location () const
1419 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1420 if ((*i)->is_auto_loop()) {
1421 return const_cast<Location*> (*i);
1428 Locations::auto_punch_location () const
1430 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1431 if ((*i)->is_auto_punch()) {
1432 return const_cast<Location*> (*i);
1439 Locations::clock_origin_location () const
1441 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1442 if ((*i)->is_clock_origin()) {
1443 return const_cast<Location*> (*i);
1446 return session_range_location ();
1450 Locations::num_range_markers () const
1453 Glib::Threads::Mutex::Lock lm (lock);
1454 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1455 if ((*i)->is_range_marker()) {
1463 Locations::get_location_by_id(PBD::ID id)
1465 LocationList::iterator it;
1466 for (it = locations.begin(); it != locations.end(); ++it)
1467 if (id == (*it)->id())
1474 Locations::find_all_between (samplepos_t start, samplepos_t end, LocationList& ll, Location::Flags flags)
1476 Glib::Threads::Mutex::Lock lm (lock);
1478 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1479 if ((flags == 0 || (*i)->matches (flags)) &&
1480 ((*i)->start() >= start && (*i)->end() < end)) {