2 Copyright (C) 2000 Paul Davis
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.
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.
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.
22 #include <cstdio> /* for sprintf */
28 #include "pbd/types_convert.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/enumwriter.h"
33 #include "ardour/location.h"
34 #include "ardour/midi_scene_change.h"
35 #include "ardour/session.h"
36 #include "ardour/audiofilesource.h"
37 #include "ardour/tempo.h"
38 #include "ardour/types_convert.h"
43 DEFINE_ENUM_CONVERT(ARDOUR::Location::Flags);
47 using namespace ARDOUR;
50 PBD::Signal0<void> Location::scene_changed;
51 PBD::Signal1<void,Location*> Location::name_changed;
52 PBD::Signal1<void,Location*> Location::end_changed;
53 PBD::Signal1<void,Location*> Location::start_changed;
54 PBD::Signal1<void,Location*> Location::flags_changed;
55 PBD::Signal1<void,Location*> Location::lock_changed;
56 PBD::Signal1<void,Location*> Location::position_lock_style_changed;
57 PBD::Signal1<void,Location*> Location::changed;
59 Location::Location (Session& s)
60 : SessionHandleRef (s)
67 , _position_lock_style (AudioTime)
73 /** Construct a new Location, giving it the position lock style determined by glue-new-markers-to-bars-and-beats */
74 Location::Location (Session& s, samplepos_t sample_start, samplepos_t sample_end, const std::string &name, Flags bits, const uint32_t sub_num)
75 : SessionHandleRef (s)
77 , _start (sample_start)
81 , _position_lock_style (s.config.get_glue_new_markers_to_bars_and_beats() ? MusicTime : AudioTime)
84 recompute_beat_from_samples (sub_num);
90 Location::Location (const Location& other)
91 : SessionHandleRef (other._session)
92 , StatefulDestructible()
94 , _start (other._start)
95 , _start_beat (other._start_beat)
97 , _end_beat (other._end_beat)
98 , _flags (other._flags)
99 , _position_lock_style (other._position_lock_style)
102 /* copy is not locked even if original was */
106 assert (_start >= 0);
109 /* scene change is NOT COPIED */
112 Location::Location (Session& s, const XMLNode& node)
113 : SessionHandleRef (s)
115 , _position_lock_style (AudioTime)
117 /* Note: _position_lock_style is initialised above in case set_state doesn't set it
118 (for 2.X session file compatibility).
121 if (set_state (node, Stateful::loading_state_version)) {
122 throw failed_constructor ();
125 assert (_start >= 0);
130 Location::operator== (const Location& other)
132 if (_name != other._name ||
133 _start != other._start ||
134 _end != other._end ||
135 _start_beat != other._start_beat ||
136 _end_beat != other._end_beat ||
137 _flags != other._flags ||
138 _position_lock_style != other._position_lock_style) {
145 Location::operator= (const Location& other)
147 if (this == &other) {
152 _start = other._start;
153 _start_beat = other._start_beat;
155 _end_beat = other._end_beat;
156 _flags = other._flags;
157 _position_lock_style = other._position_lock_style;
159 /* XXX need to copy scene change */
161 /* copy is not locked even if original was */
165 /* "changed" not emitted on purpose */
167 assert (_start >= 0);
173 /** Set location name
177 Location::set_name (const std::string& str)
181 name_changed (this); /* EMIT SIGNAL */
182 NameChanged (); /* EMIT SIGNAL */
185 /** Set start position.
186 * @param s New start.
187 * @param force true to force setting, even if the given new start is after the current end.
188 * @param allow_beat_recompute True to recompute BEAT start time from the new given start time.
191 Location::set_start (samplepos_t s, bool force, bool allow_beat_recompute, const uint32_t sub_num)
202 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || (!is_mark() && s > _end)) {
211 if (allow_beat_recompute) {
212 recompute_beat_from_samples (sub_num);
215 start_changed (this); /* EMIT SIGNAL */
216 StartChanged (); /* EMIT SIGNAL */
217 //end_changed (this); /* EMIT SIGNAL */
218 //EndChanged (); /* EMIT SIGNAL */
221 /* moving the start (position) of a marker with a scene change
222 requires an update in the Scene Changer.
226 scene_changed (); /* EMIT SIGNAL */
229 assert (_start >= 0);
234 /* range locations must exceed a minimum duration */
235 if (_end - s < Config->get_range_location_minimum()) {
242 samplepos_t const old = _start;
245 if (allow_beat_recompute) {
246 recompute_beat_from_samples (sub_num);
248 start_changed (this); /* EMIT SIGNAL */
249 StartChanged (); /* EMIT SIGNAL */
251 if (is_session_range ()) {
252 Session::StartTimeChanged (old); /* EMIT SIGNAL */
253 AudioFileSource::set_header_position_offset (s);
257 assert (_start >= 0);
262 /** Set end position.
264 * @param force true to force setting, even if the given new end is before the current start.
265 * @param allow_beat_recompute True to recompute BEAT end time from the new given end time.
268 Location::set_end (samplepos_t e, bool force, bool allow_beat_recompute, const uint32_t sub_num)
279 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
288 if (allow_beat_recompute) {
289 recompute_beat_from_samples (sub_num);
291 //start_changed (this); /* EMIT SIGNAL */
292 //StartChanged (); /* EMIT SIGNAL */
293 end_changed (this); /* EMIT SIGNAL */
294 EndChanged (); /* EMIT SIGNAL */
297 assert (_start >= 0);
302 /* range locations must exceed a minimum duration */
303 if (e - _start < Config->get_range_location_minimum()) {
310 samplepos_t const old = _end;
313 if (allow_beat_recompute) {
314 recompute_beat_from_samples (sub_num);
317 end_changed(this); /* EMIT SIGNAL */
318 EndChanged(); /* EMIT SIGNAL */
320 if (is_session_range()) {
321 Session::EndTimeChanged (old); /* EMIT SIGNAL */
331 Location::set (samplepos_t s, samplepos_t e, bool allow_beat_recompute, const uint32_t sub_num)
333 if (s < 0 || e < 0) {
338 if (((is_auto_punch() || is_auto_loop()) && s >= e) || (!is_mark() && s > e)) {
342 bool start_change = false;
343 bool end_change = false;
351 if (allow_beat_recompute) {
352 recompute_beat_from_samples (sub_num);
359 assert (_start >= 0);
364 /* range locations must exceed a minimum duration */
365 if (e - s < Config->get_range_location_minimum()) {
371 samplepos_t const old = _start;
374 if (allow_beat_recompute) {
375 recompute_beat_from_samples (sub_num);
380 if (is_session_range ()) {
381 Session::StartTimeChanged (old); /* EMIT SIGNAL */
382 AudioFileSource::set_header_position_offset (s);
389 samplepos_t const old = _end;
392 if (allow_beat_recompute) {
393 recompute_beat_from_samples (sub_num);
398 if (is_session_range()) {
399 Session::EndTimeChanged (old); /* EMIT SIGNAL */
406 if (start_change && end_change) {
409 } else if (start_change) {
410 start_changed(this); /* EMIT SIGNAL */
411 StartChanged(); /* EMIT SIGNAL */
412 } else if (end_change) {
413 end_changed(this); /* EMIT SIGNAL */
414 EndChanged(); /* EMIT SIGNAL */
421 Location::move_to (samplepos_t pos, const uint32_t sub_num)
433 _end = _start + length();
434 recompute_beat_from_samples (sub_num);
436 changed (this); /* EMIT SIGNAL */
437 Changed (); /* EMIT SIGNAL */
440 assert (_start >= 0);
447 Location::set_hidden (bool yn, void*)
449 if (set_flag_internal (yn, IsHidden)) {
450 flags_changed (this); /* EMIT SIGNAL */
456 Location::set_cd (bool yn, void*)
458 // XXX this really needs to be session start
459 // but its not available here - leave to GUI
461 if (yn && _start == 0) {
462 error << _("You cannot put a CD marker at this position") << endmsg;
466 if (set_flag_internal (yn, IsCDMarker)) {
467 flags_changed (this); /* EMIT SIGNAL */
473 Location::set_is_range_marker (bool yn, void*)
475 if (set_flag_internal (yn, IsRangeMarker)) {
476 flags_changed (this);
477 FlagsChanged (); /* EMIT SIGNAL */
482 Location::set_is_clock_origin (bool yn, void*)
484 if (set_flag_internal (yn, IsClockOrigin)) {
485 flags_changed (this);
486 FlagsChanged (); /* EMIT SIGNAL */
491 Location::set_skip (bool yn)
493 if (is_range_marker() && length() > 0) {
494 if (set_flag_internal (yn, IsSkip)) {
495 flags_changed (this);
502 Location::set_skipping (bool yn)
504 if (is_range_marker() && is_skip() && length() > 0) {
505 if (set_flag_internal (yn, IsSkipping)) {
506 flags_changed (this);
513 Location::set_auto_punch (bool yn, void*)
515 if (is_mark() || _start == _end) {
519 if (set_flag_internal (yn, IsAutoPunch)) {
520 flags_changed (this); /* EMIT SIGNAL */
521 FlagsChanged (); /* EMIT SIGNAL */
526 Location::set_auto_loop (bool yn, void*)
528 if (is_mark() || _start == _end) {
532 if (set_flag_internal (yn, IsAutoLoop)) {
533 flags_changed (this); /* EMIT SIGNAL */
534 FlagsChanged (); /* EMIT SIGNAL */
539 Location::set_flag_internal (bool yn, Flags flag)
542 if (!(_flags & flag)) {
543 _flags = Flags (_flags | flag);
548 _flags = Flags (_flags & ~flag);
556 Location::set_mark (bool yn)
558 /* This function is private, and so does not emit signals */
560 if (_start != _end) {
564 set_flag_internal (yn, IsMark);
569 Location::cd_info_node(const string & name, const string & value)
571 XMLNode* root = new XMLNode("CD-Info");
573 root->set_property("name", name);
574 root->set_property("value", value);
581 Location::get_state ()
583 XMLNode *node = new XMLNode ("Location");
585 typedef map<string, string>::const_iterator CI;
587 for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
588 node->add_child_nocopy(cd_info_node(m->first, m->second));
591 node->set_property ("id", id ());
592 node->set_property ("name", name());
593 node->set_property ("start", start());
594 node->set_property ("end", end());
595 if (position_lock_style() == MusicTime) {
596 node->set_property ("start-beat", _start_beat);
597 node->set_property ("end-beat", _end_beat);
599 node->set_property ("flags", _flags);
600 node->set_property ("locked", _locked);
601 node->set_property ("position-lock-style", _position_lock_style);
604 node->add_child_nocopy (_scene_change->get_state());
611 Location::set_state (const XMLNode& node, int version)
613 XMLNodeList cd_list = node.children();
614 XMLNodeConstIterator cd_iter;
620 if (node.name() != "Location") {
621 error << _("incorrect XML node passed to Location::set_state") << endmsg;
625 if (!set_id (node)) {
626 warning << _("XML node for Location has no ID information") << endmsg;
630 if (!node.get_property ("name", str)) {
631 error << _("XML node for Location has no name information") << endmsg;
637 /* can't use set_start() here, because _end
638 may make the value of _start illegal.
641 if (!node.get_property ("start", _start)) {
642 error << _("XML node for Location has no start information") << endmsg;
646 if (!node.get_property ("end", _end)) {
647 error << _("XML node for Location has no end information") << endmsg;
651 Flags old_flags (_flags);
653 if (!node.get_property ("flags", _flags)) {
654 error << _("XML node for Location has no flags information") << endmsg;
658 if (old_flags != _flags) {
662 if (!node.get_property ("locked", _locked)) {
666 for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
670 if (cd_node->name() != "CD-Info") {
674 if (!cd_node->get_property ("name", cd_name)) {
675 throw failed_constructor ();
678 if (!cd_node->get_property ("value", cd_value)) {
679 throw failed_constructor ();
682 cd_info[cd_name] = cd_value;
685 node.get_property ("position-lock-style", _position_lock_style);
687 XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
690 _scene_change = SceneChange::factory (*scene_child, version);
693 if (position_lock_style() == AudioTime) {
694 recompute_beat_from_samples (0);
697 if (!node.get_property ("start-beat", _start_beat) ||
698 !node.get_property ("end-beat", _end_beat)) {
699 recompute_beat_from_samples (0);
704 changed (this); /* EMIT SIGNAL */
705 Changed (); /* EMIT SIGNAL */
707 assert (_start >= 0);
714 Location::set_position_lock_style (PositionLockStyle ps)
716 if (_position_lock_style == ps) {
720 _position_lock_style = ps;
722 if (ps == MusicTime) {
723 recompute_beat_from_samples (0);
726 position_lock_style_changed (this); /* EMIT SIGNAL */
727 PositionLockStyleChanged (); /* EMIT SIGNAL */
731 Location::recompute_beat_from_samples (const uint32_t sub_num)
733 _start_beat = _session.tempo_map().exact_beat_at_sample (_start, sub_num);
734 _end_beat = _session.tempo_map().exact_beat_at_sample (_end, sub_num);
738 Location::recompute_samples_from_beat ()
740 if (_position_lock_style != MusicTime) {
744 TempoMap& map (_session.tempo_map());
745 set (map.sample_at_beat (_start_beat), map.sample_at_beat (_end_beat), false);
765 Location::set_scene_change (boost::shared_ptr<SceneChange> sc)
767 if (_scene_change != sc) {
769 _session.set_dirty ();
771 scene_changed (); /* EMIT SIGNAL */
772 SceneChangeChanged (); /* EMIT SIGNAL */
776 /*---------------------------------------------------------------------- */
778 Locations::Locations (Session& s)
779 : SessionHandleRef (s)
781 current_location = 0;
784 Locations::~Locations ()
786 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
787 LocationList::iterator tmp = i;
795 Locations::set_current (Location *loc, bool want_lock)
800 Glib::Threads::Mutex::Lock lm (lock);
801 ret = set_current_unlocked (loc);
803 ret = set_current_unlocked (loc);
807 current_changed (current_location); /* EMIT SIGNAL */
813 Locations::set_clock_origin (Location* loc, void *src)
815 LocationList::iterator i;
816 for (i = locations.begin(); i != locations.end(); ++i) {
817 if ((*i)->is_clock_origin ()) {
818 (*i)->set_is_clock_origin (false, src);
821 (*i)->set_is_clock_origin (true, src);
827 Locations::next_available_name(string& result,string base)
829 LocationList::iterator i;
833 std::map<uint32_t,bool> taken;
841 /* find all existing names that match "base", and store
842 the numeric part of them (if any) in the map "taken"
845 for (i = locations.begin(); i != locations.end(); ++i) {
847 const string& temp ((*i)->name());
849 if (!temp.find (base,0)) {
850 /* grab what comes after the "base" as if it was
851 a number, and assuming that works OK,
852 store it in "taken" so that we know it
855 if ((suffix = atoi (temp.substr(l))) != 0) {
856 taken.insert (make_pair (suffix,true));
862 /* Now search for an un-used suffix to add to "base". This
863 will find "holes" in the numbering sequence when a location
866 This must start at 1, both for human-numbering reasons
867 and also because the call to atoi() above would return
868 zero if there is no recognizable numeric suffix, causing
869 "base 0" not to be inserted into the "taken" map.
874 while (n < UINT32_MAX) {
875 if (taken.find (n) == taken.end()) {
876 snprintf (buf, sizeof(buf), "%d", n);
887 Locations::set_current_unlocked (Location *loc)
889 if (find (locations.begin(), locations.end(), loc) == locations.end()) {
890 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
894 current_location = loc;
902 Glib::Threads::Mutex::Lock lm (lock);
904 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
906 LocationList::iterator tmp = i;
909 if (!(*i)->is_session_range()) {
917 current_location = 0;
920 changed (); /* EMIT SIGNAL */
921 current_changed (0); /* EMIT SIGNAL */
925 Locations::clear_markers ()
928 Glib::Threads::Mutex::Lock lm (lock);
929 LocationList::iterator tmp;
931 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
935 if ((*i)->is_mark() && !(*i)->is_session_range()) {
944 changed (); /* EMIT SIGNAL */
948 Locations::clear_ranges ()
951 Glib::Threads::Mutex::Lock lm (lock);
952 LocationList::iterator tmp;
954 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
959 /* We do not remove these ranges as part of this
963 if ((*i)->is_auto_punch() ||
964 (*i)->is_auto_loop() ||
965 (*i)->is_session_range()) {
970 if (!(*i)->is_mark()) {
979 current_location = 0;
983 current_changed (0); /* EMIT SIGNAL */
987 Locations::add (Location *loc, bool make_current)
992 Glib::Threads::Mutex::Lock lm (lock);
993 locations.push_back (loc);
996 current_location = loc;
1000 added (loc); /* EMIT SIGNAL */
1003 current_changed (current_location); /* EMIT SIGNAL */
1006 if (loc->is_session_range()) {
1007 Session::StartTimeChanged (0);
1008 Session::EndTimeChanged (1);
1013 Locations::remove (Location *loc)
1015 bool was_removed = false;
1016 bool was_current = false;
1017 LocationList::iterator i;
1023 if (loc->is_session_range()) {
1028 Glib::Threads::Mutex::Lock lm (lock);
1030 for (i = locations.begin(); i != locations.end(); ++i) {
1032 bool was_loop = (*i)->is_auto_loop();
1034 locations.erase (i);
1036 if (current_location == loc) {
1037 current_location = 0;
1041 if (_session.get_play_loop()) {
1042 _session.request_play_loop (false, false);
1044 _session.auto_loop_location_changed (0);
1053 removed (loc); /* EMIT SIGNAL */
1056 current_changed (0); /* EMIT SIGNAL */
1062 Locations::get_state ()
1064 XMLNode *node = new XMLNode ("Locations");
1065 LocationList::iterator iter;
1066 Glib::Threads::Mutex::Lock lm (lock);
1068 for (iter = locations.begin(); iter != locations.end(); ++iter) {
1069 node->add_child_nocopy ((*iter)->get_state ());
1076 Locations::set_state (const XMLNode& node, int version)
1078 if (node.name() != "Locations") {
1079 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1083 XMLNodeList nlist = node.children();
1085 /* build up a new locations list in here */
1086 LocationList new_locations;
1088 current_location = 0;
1090 Location* session_range_location = 0;
1091 if (version < 3000) {
1092 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1093 new_locations.push_back (session_range_location);
1097 Glib::Threads::Mutex::Lock lm (lock);
1099 XMLNodeConstIterator niter;
1100 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1104 XMLProperty const * prop_id = (*niter)->property ("id");
1106 PBD::ID id (prop_id->value ());
1108 LocationList::const_iterator i = locations.begin();
1109 while (i != locations.end () && (*i)->id() != id) {
1114 if (i != locations.end()) {
1115 /* we can re-use an old Location object */
1118 // changed locations will be updated by Locations::changed signal
1119 loc->set_state (**niter, version);
1121 loc = new Location (_session, **niter);
1126 if (version < 3000) {
1127 /* look for old-style IsStart / IsEnd properties in this location;
1128 if they are present, update the session_range_location accordingly
1130 XMLProperty const * prop = (*niter)->property ("flags");
1132 string v = prop->value ();
1134 string::size_type const c = v.find_first_of (',');
1135 string const s = v.substr (0, c);
1136 if (s == X_("IsStart")) {
1137 session_range_location->set_start (loc->start(), true);
1139 } else if (s == X_("IsEnd")) {
1140 session_range_location->set_end (loc->start(), true);
1144 if (c == string::npos) {
1148 v = v.substr (c + 1);
1154 new_locations.push_back (loc);
1158 catch (failed_constructor& err) {
1159 error << _("could not load location from session file - ignored") << endmsg;
1163 /* We may have some unused locations in the old list. */
1164 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1165 LocationList::iterator tmp = i;
1168 LocationList::iterator n = new_locations.begin();
1171 while (n != new_locations.end ()) {
1172 if ((*i)->id() == (*n)->id()) {
1181 locations.erase (i);
1187 locations = new_locations;
1189 if (locations.size()) {
1190 current_location = locations.front();
1192 current_location = 0;
1196 changed (); /* EMIT SIGNAL */
1202 typedef std::pair<samplepos_t,Location*> LocationPair;
1204 struct LocationStartEarlierComparison
1206 bool operator() (LocationPair a, LocationPair b) {
1207 return a.first < b.first;
1211 struct LocationStartLaterComparison
1213 bool operator() (LocationPair a, LocationPair b) {
1214 return a.first > b.first;
1219 Locations::first_mark_before (samplepos_t sample, bool include_special_ranges)
1221 Glib::Threads::Mutex::Lock lm (lock);
1222 vector<LocationPair> locs;
1224 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1225 locs.push_back (make_pair ((*i)->start(), (*i)));
1226 if (!(*i)->is_mark()) {
1227 locs.push_back (make_pair ((*i)->end(), (*i)));
1231 LocationStartLaterComparison cmp;
1232 sort (locs.begin(), locs.end(), cmp);
1234 /* locs is sorted in ascending order */
1236 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1237 if ((*i).second->is_hidden()) {
1240 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1243 if ((*i).first < sample) {
1252 Locations::mark_at (samplepos_t pos, samplecnt_t slop) const
1254 Glib::Threads::Mutex::Lock lm (lock);
1255 Location* closest = 0;
1256 sampleoffset_t mindelta = max_samplepos;
1257 sampleoffset_t delta;
1259 /* locations are not necessarily stored in linear time order so we have
1260 * to iterate across all of them to find the one closest to a give point.
1263 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1265 if ((*i)->is_mark()) {
1266 if (pos > (*i)->start()) {
1267 delta = pos - (*i)->start();
1269 delta = (*i)->start() - pos;
1272 if (slop == 0 && delta == 0) {
1273 /* special case: no slop, and direct hit for position */
1277 if (delta <= slop) {
1278 if (delta < mindelta) {
1290 Locations::first_mark_after (samplepos_t sample, bool include_special_ranges)
1292 Glib::Threads::Mutex::Lock lm (lock);
1293 vector<LocationPair> locs;
1295 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1296 locs.push_back (make_pair ((*i)->start(), (*i)));
1297 if (!(*i)->is_mark()) {
1298 locs.push_back (make_pair ((*i)->end(), (*i)));
1302 LocationStartEarlierComparison cmp;
1303 sort (locs.begin(), locs.end(), cmp);
1305 /* locs is sorted in reverse order */
1307 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1308 if ((*i).second->is_hidden()) {
1311 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1314 if ((*i).first > sample) {
1322 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1323 * side of a sample. Note that if sample is exactly on a `mark', that mark will not be considered for returning
1325 * @param sample Frame to look for.
1326 * @param before Filled in with the position of the last `mark' before `sample' (or max_samplepos if none exists)
1327 * @param after Filled in with the position of the next `mark' after `sample' (or max_samplepos if none exists)
1330 Locations::marks_either_side (samplepos_t const sample, samplepos_t& before, samplepos_t& after) const
1332 before = after = max_samplepos;
1337 Glib::Threads::Mutex::Lock lm (lock);
1341 /* Get a list of positions; don't store any that are exactly on our requested position */
1343 std::list<samplepos_t> positions;
1345 for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1346 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1350 if (!(*i)->is_hidden()) {
1351 if ((*i)->is_mark ()) {
1352 if ((*i)->start() != sample) {
1353 positions.push_back ((*i)->start ());
1356 if ((*i)->start() != sample) {
1357 positions.push_back ((*i)->start ());
1359 if ((*i)->end() != sample) {
1360 positions.push_back ((*i)->end ());
1366 if (positions.empty ()) {
1372 std::list<samplepos_t>::iterator i = positions.begin ();
1373 while (i != positions.end () && *i < sample) {
1377 if (i == positions.end ()) {
1378 /* run out of marks */
1379 before = positions.back ();
1385 if (i == positions.begin ()) {
1395 Locations::session_range_location () const
1397 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1398 if ((*i)->is_session_range()) {
1399 return const_cast<Location*> (*i);
1406 Locations::auto_loop_location () const
1408 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1409 if ((*i)->is_auto_loop()) {
1410 return const_cast<Location*> (*i);
1417 Locations::auto_punch_location () const
1419 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1420 if ((*i)->is_auto_punch()) {
1421 return const_cast<Location*> (*i);
1428 Locations::clock_origin_location () const
1430 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1431 if ((*i)->is_clock_origin()) {
1432 return const_cast<Location*> (*i);
1435 return session_range_location ();
1439 Locations::num_range_markers () const
1442 Glib::Threads::Mutex::Lock lm (lock);
1443 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1444 if ((*i)->is_range_marker()) {
1452 Locations::get_location_by_id(PBD::ID id)
1454 LocationList::iterator it;
1455 for (it = locations.begin(); it != locations.end(); ++it)
1456 if (id == (*it)->id())
1463 Locations::find_all_between (samplepos_t start, samplepos_t end, LocationList& ll, Location::Flags flags)
1465 Glib::Threads::Mutex::Lock lm (lock);
1467 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1468 if ((flags == 0 || (*i)->matches (flags)) &&
1469 ((*i)->start() >= start && (*i)->end() < end)) {