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/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"
42 using namespace ARDOUR;
45 PBD::Signal0<void> Location::scene_changed;
46 PBD::Signal1<void,Location*> Location::name_changed;
47 PBD::Signal1<void,Location*> Location::end_changed;
48 PBD::Signal1<void,Location*> Location::start_changed;
49 PBD::Signal1<void,Location*> Location::flags_changed;
50 PBD::Signal1<void,Location*> Location::lock_changed;
51 PBD::Signal1<void,Location*> Location::position_lock_style_changed;
52 PBD::Signal1<void,Location*> Location::changed;
54 Location::Location (Session& s)
55 : SessionHandleRef (s)
62 , _position_lock_style (AudioTime)
68 /** Construct a new Location, giving it the position lock style determined by glue-new-markers-to-bars-and-beats */
69 Location::Location (Session& s, framepos_t sample_start, framepos_t sample_end, const std::string &name, Flags bits, const uint32_t sub_num)
70 : SessionHandleRef (s)
72 , _start (sample_start)
76 , _position_lock_style (s.config.get_glue_new_markers_to_bars_and_beats() ? MusicTime : AudioTime)
79 recompute_beat_from_frames (sub_num);
85 Location::Location (const Location& other)
86 : SessionHandleRef (other._session)
87 , StatefulDestructible()
89 , _start (other._start)
90 , _start_beat (other._start_beat)
92 , _end_beat (other._end_beat)
93 , _flags (other._flags)
94 , _position_lock_style (other._position_lock_style)
97 /* copy is not locked even if original was */
101 assert (_start >= 0);
104 /* scene change is NOT COPIED */
107 Location::Location (Session& s, const XMLNode& node)
108 : SessionHandleRef (s)
110 , _position_lock_style (AudioTime)
112 /* Note: _position_lock_style is initialised above in case set_state doesn't set it
113 (for 2.X session file compatibility).
116 if (set_state (node, Stateful::loading_state_version)) {
117 throw failed_constructor ();
120 assert (_start >= 0);
125 Location::operator== (const Location& other)
127 if (_name != other._name ||
128 _start != other._start ||
129 _end != other._end ||
130 _start_beat != other._start_beat ||
131 _end_beat != other._end_beat ||
132 _flags != other._flags ||
133 _position_lock_style != other._position_lock_style) {
140 Location::operator= (const Location& other)
142 if (this == &other) {
147 _start = other._start;
148 _start_beat = other._start_beat;
150 _end_beat = other._end_beat;
151 _flags = other._flags;
152 _position_lock_style = other._position_lock_style;
154 /* XXX need to copy scene change */
156 /* copy is not locked even if original was */
160 /* "changed" not emitted on purpose */
162 assert (_start >= 0);
168 /** Set location name
172 Location::set_name (const std::string& str)
176 name_changed (this); /* EMIT SIGNAL */
177 NameChanged (); /* EMIT SIGNAL */
180 /** Set start position.
181 * @param s New start.
182 * @param force true to force setting, even if the given new start is after the current end.
183 * @param allow_beat_recompute True to recompute BEAT start time from the new given start time.
186 Location::set_start (framepos_t s, bool force, bool allow_beat_recompute, const uint32_t sub_num)
197 if (((is_auto_punch() || is_auto_loop()) && s >= _end) || (!is_mark() && s > _end)) {
206 if (allow_beat_recompute) {
207 recompute_beat_from_frames (sub_num);
210 start_changed (this); /* EMIT SIGNAL */
211 StartChanged (); /* EMIT SIGNAL */
212 //end_changed (this); /* EMIT SIGNAL */
213 //EndChanged (); /* EMIT SIGNAL */
216 /* moving the start (position) of a marker with a scene change
217 requires an update in the Scene Changer.
221 scene_changed (); /* EMIT SIGNAL */
224 assert (_start >= 0);
229 /* range locations must exceed a minimum duration */
230 if (_end - s < Config->get_range_location_minimum()) {
237 framepos_t const old = _start;
240 if (allow_beat_recompute) {
241 recompute_beat_from_frames (sub_num);
243 start_changed (this); /* EMIT SIGNAL */
244 StartChanged (); /* EMIT SIGNAL */
246 if (is_session_range ()) {
247 Session::StartTimeChanged (old); /* EMIT SIGNAL */
248 AudioFileSource::set_header_position_offset (s);
252 assert (_start >= 0);
257 /** Set end position.
259 * @param force true to force setting, even if the given new end is before the current start.
260 * @param allow_beat_recompute True to recompute BEAT end time from the new given end time.
263 Location::set_end (framepos_t e, bool force, bool allow_beat_recompute, const uint32_t sub_num)
274 if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
283 if (allow_beat_recompute) {
284 recompute_beat_from_frames (sub_num);
286 //start_changed (this); /* EMIT SIGNAL */
287 //StartChanged (); /* EMIT SIGNAL */
288 end_changed (this); /* EMIT SIGNAL */
289 EndChanged (); /* EMIT SIGNAL */
292 assert (_start >= 0);
297 /* range locations must exceed a minimum duration */
298 if (e - _start < Config->get_range_location_minimum()) {
305 framepos_t const old = _end;
308 if (allow_beat_recompute) {
309 recompute_beat_from_frames (sub_num);
312 end_changed(this); /* EMIT SIGNAL */
313 EndChanged(); /* EMIT SIGNAL */
315 if (is_session_range()) {
316 Session::EndTimeChanged (old); /* EMIT SIGNAL */
326 Location::set (framepos_t s, framepos_t e, bool allow_beat_recompute, const uint32_t sub_num)
328 if (s < 0 || e < 0) {
333 if (((is_auto_punch() || is_auto_loop()) && s >= e) || (!is_mark() && s > e)) {
337 bool start_change = false;
338 bool end_change = false;
346 if (allow_beat_recompute) {
347 recompute_beat_from_frames (sub_num);
354 assert (_start >= 0);
359 /* range locations must exceed a minimum duration */
360 if (e - s < Config->get_range_location_minimum()) {
366 framepos_t const old = _start;
369 if (allow_beat_recompute) {
370 recompute_beat_from_frames (sub_num);
375 if (is_session_range ()) {
376 Session::StartTimeChanged (old); /* EMIT SIGNAL */
377 AudioFileSource::set_header_position_offset (s);
384 framepos_t const old = _end;
387 if (allow_beat_recompute) {
388 recompute_beat_from_frames (sub_num);
393 if (is_session_range()) {
394 Session::EndTimeChanged (old); /* EMIT SIGNAL */
401 if (start_change && end_change) {
404 } else if (start_change) {
405 start_changed(this); /* EMIT SIGNAL */
406 StartChanged(); /* EMIT SIGNAL */
407 } else if (end_change) {
408 end_changed(this); /* EMIT SIGNAL */
409 EndChanged(); /* EMIT SIGNAL */
416 Location::move_to (framepos_t pos, const uint32_t sub_num)
428 _end = _start + length();
429 recompute_beat_from_frames (sub_num);
431 changed (this); /* EMIT SIGNAL */
432 Changed (); /* EMIT SIGNAL */
435 assert (_start >= 0);
442 Location::set_hidden (bool yn, void*)
444 if (set_flag_internal (yn, IsHidden)) {
445 flags_changed (this); /* EMIT SIGNAL */
451 Location::set_cd (bool yn, void*)
453 // XXX this really needs to be session start
454 // but its not available here - leave to GUI
456 if (yn && _start == 0) {
457 error << _("You cannot put a CD marker at this position") << endmsg;
461 if (set_flag_internal (yn, IsCDMarker)) {
462 flags_changed (this); /* EMIT SIGNAL */
468 Location::set_is_range_marker (bool yn, void*)
470 if (set_flag_internal (yn, IsRangeMarker)) {
471 flags_changed (this);
472 FlagsChanged (); /* EMIT SIGNAL */
477 Location::set_skip (bool yn)
479 if (is_range_marker() && length() > 0) {
480 if (set_flag_internal (yn, IsSkip)) {
481 flags_changed (this);
488 Location::set_skipping (bool yn)
490 if (is_range_marker() && is_skip() && length() > 0) {
491 if (set_flag_internal (yn, IsSkipping)) {
492 flags_changed (this);
499 Location::set_auto_punch (bool yn, void*)
501 if (is_mark() || _start == _end) {
505 if (set_flag_internal (yn, IsAutoPunch)) {
506 flags_changed (this); /* EMIT SIGNAL */
507 FlagsChanged (); /* EMIT SIGNAL */
512 Location::set_auto_loop (bool yn, void*)
514 if (is_mark() || _start == _end) {
518 if (set_flag_internal (yn, IsAutoLoop)) {
519 flags_changed (this); /* EMIT SIGNAL */
520 FlagsChanged (); /* EMIT SIGNAL */
525 Location::set_flag_internal (bool yn, Flags flag)
528 if (!(_flags & flag)) {
529 _flags = Flags (_flags | flag);
534 _flags = Flags (_flags & ~flag);
542 Location::set_mark (bool yn)
544 /* This function is private, and so does not emit signals */
546 if (_start != _end) {
550 set_flag_internal (yn, IsMark);
555 Location::cd_info_node(const string & name, const string & value)
557 XMLNode* root = new XMLNode("CD-Info");
559 root->add_property("name", name);
560 root->add_property("value", value);
567 Location::get_state ()
569 XMLNode *node = new XMLNode ("Location");
572 typedef map<string, string>::const_iterator CI;
574 for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
575 node->add_child_nocopy(cd_info_node(m->first, m->second));
578 id().print (buf, sizeof (buf));
579 node->add_property("id", buf);
580 node->add_property ("name", name());
581 snprintf (buf, sizeof (buf), "%" PRId64, start());
582 node->add_property ("start", buf);
583 snprintf (buf, sizeof (buf), "%" PRId64, end());
584 node->add_property ("end", buf);
586 if (position_lock_style() == MusicTime) {
587 snprintf (buf, sizeof (buf), "%lf", _start_beat);
588 node->add_property ("start-beat", buf);
589 snprintf (buf, sizeof (buf), "%lf", _end_beat);
590 node->add_property ("end-beat", buf);
593 node->add_property ("flags", enum_2_string (_flags));
594 node->add_property ("locked", (_locked ? "yes" : "no"));
595 node->add_property ("position-lock-style", enum_2_string (_position_lock_style));
598 node->add_child_nocopy (_scene_change->get_state());
605 Location::set_state (const XMLNode& node, int version)
607 XMLProperty const * prop;
609 XMLNodeList cd_list = node.children();
610 XMLNodeConstIterator cd_iter;
616 if (node.name() != "Location") {
617 error << _("incorrect XML node passed to Location::set_state") << endmsg;
621 if (!set_id (node)) {
622 warning << _("XML node for Location has no ID information") << endmsg;
625 if ((prop = node.property ("name")) == 0) {
626 error << _("XML node for Location has no name information") << endmsg;
630 set_name (prop->value());
632 if ((prop = node.property ("start")) == 0) {
633 error << _("XML node for Location has no start information") << endmsg;
637 /* can't use set_start() here, because _end
638 may make the value of _start illegal.
641 sscanf (prop->value().c_str(), "%" PRId64, &_start);
643 if ((prop = node.property ("end")) == 0) {
644 error << _("XML node for Location has no end information") << endmsg;
648 sscanf (prop->value().c_str(), "%" PRId64, &_end);
650 if ((prop = node.property ("flags")) == 0) {
651 error << _("XML node for Location has no flags information") << endmsg;
655 Flags old_flags (_flags);
656 _flags = Flags (string_2_enum (prop->value(), _flags));
658 if (old_flags != _flags) {
662 if ((prop = node.property ("locked")) != 0) {
663 _locked = string_is_affirmative (prop->value());
668 for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
672 if (cd_node->name() != "CD-Info") {
676 if ((prop = cd_node->property ("name")) != 0) {
677 cd_name = prop->value();
679 throw failed_constructor ();
682 if ((prop = cd_node->property ("value")) != 0) {
683 cd_value = prop->value();
685 throw failed_constructor ();
689 cd_info[cd_name] = cd_value;
692 if ((prop = node.property ("position-lock-style")) != 0) {
693 _position_lock_style = PositionLockStyle (string_2_enum (prop->value(), _position_lock_style));
696 XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
699 _scene_change = SceneChange::factory (*scene_child, version);
702 if (position_lock_style() == AudioTime) {
703 recompute_beat_from_frames (0);
706 bool has_beat = false;
708 if ((prop = node.property ("start-beat")) != 0) {
709 sscanf (prop->value().c_str(), "%lf", &_start_beat);
713 if ((prop = node.property ("end-beat")) != 0) {
714 sscanf (prop->value().c_str(), "%lf", &_end_beat);
719 recompute_beat_from_frames (0);
724 changed (this); /* EMIT SIGNAL */
725 Changed (); /* EMIT SIGNAL */
727 assert (_start >= 0);
734 Location::set_position_lock_style (PositionLockStyle ps)
736 if (_position_lock_style == ps) {
740 _position_lock_style = ps;
742 if (ps == MusicTime) {
743 recompute_beat_from_frames (0);
746 position_lock_style_changed (this); /* EMIT SIGNAL */
747 PositionLockStyleChanged (); /* EMIT SIGNAL */
751 Location::recompute_beat_from_frames (const uint32_t sub_num)
753 _start_beat = _session.tempo_map().exact_beat_at_frame (_start, sub_num);
754 _end_beat = _session.tempo_map().exact_beat_at_frame (_end, sub_num);
758 Location::recompute_frames_from_beat ()
760 if (_position_lock_style != MusicTime) {
764 TempoMap& map (_session.tempo_map());
765 set (map.frame_at_beat (_start_beat), map.frame_at_beat (_end_beat), false);
785 Location::set_scene_change (boost::shared_ptr<SceneChange> sc)
787 if (_scene_change != sc) {
789 _session.set_dirty ();
791 scene_changed (); /* EMIT SIGNAL */
792 SceneChangeChanged (); /* EMIT SIGNAL */
796 /*---------------------------------------------------------------------- */
798 Locations::Locations (Session& s)
799 : SessionHandleRef (s)
801 current_location = 0;
804 Locations::~Locations ()
806 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
807 LocationList::iterator tmp = i;
815 Locations::set_current (Location *loc, bool want_lock)
820 Glib::Threads::Mutex::Lock lm (lock);
821 ret = set_current_unlocked (loc);
823 ret = set_current_unlocked (loc);
827 current_changed (current_location); /* EMIT SIGNAL */
833 Locations::next_available_name(string& result,string base)
835 LocationList::iterator i;
839 std::map<uint32_t,bool> taken;
847 /* find all existing names that match "base", and store
848 the numeric part of them (if any) in the map "taken"
851 for (i = locations.begin(); i != locations.end(); ++i) {
853 const string& temp ((*i)->name());
855 if (!temp.find (base,0)) {
856 /* grab what comes after the "base" as if it was
857 a number, and assuming that works OK,
858 store it in "taken" so that we know it
861 if ((suffix = atoi (temp.substr(l))) != 0) {
862 taken.insert (make_pair (suffix,true));
868 /* Now search for an un-used suffix to add to "base". This
869 will find "holes" in the numbering sequence when a location
872 This must start at 1, both for human-numbering reasons
873 and also because the call to atoi() above would return
874 zero if there is no recognizable numeric suffix, causing
875 "base 0" not to be inserted into the "taken" map.
880 while (n < UINT32_MAX) {
881 if (taken.find (n) == taken.end()) {
882 snprintf (buf, sizeof(buf), "%d", n);
893 Locations::set_current_unlocked (Location *loc)
895 if (find (locations.begin(), locations.end(), loc) == locations.end()) {
896 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
900 current_location = loc;
908 Glib::Threads::Mutex::Lock lm (lock);
910 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
912 LocationList::iterator tmp = i;
915 if (!(*i)->is_session_range()) {
923 current_location = 0;
926 changed (); /* EMIT SIGNAL */
927 current_changed (0); /* EMIT SIGNAL */
931 Locations::clear_markers ()
934 Glib::Threads::Mutex::Lock lm (lock);
935 LocationList::iterator tmp;
937 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
941 if ((*i)->is_mark() && !(*i)->is_session_range()) {
950 changed (); /* EMIT SIGNAL */
954 Locations::clear_ranges ()
957 Glib::Threads::Mutex::Lock lm (lock);
958 LocationList::iterator tmp;
960 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
965 /* We do not remove these ranges as part of this
969 if ((*i)->is_auto_punch() ||
970 (*i)->is_auto_loop() ||
971 (*i)->is_session_range()) {
976 if (!(*i)->is_mark()) {
985 current_location = 0;
989 current_changed (0); /* EMIT SIGNAL */
993 Locations::add (Location *loc, bool make_current)
998 Glib::Threads::Mutex::Lock lm (lock);
999 locations.push_back (loc);
1002 current_location = loc;
1006 added (loc); /* EMIT SIGNAL */
1009 current_changed (current_location); /* EMIT SIGNAL */
1012 if (loc->is_session_range()) {
1013 Session::StartTimeChanged (0);
1014 Session::EndTimeChanged (1);
1019 Locations::remove (Location *loc)
1021 bool was_removed = false;
1022 bool was_current = false;
1023 LocationList::iterator i;
1029 if (loc->is_session_range()) {
1034 Glib::Threads::Mutex::Lock lm (lock);
1036 for (i = locations.begin(); i != locations.end(); ++i) {
1039 locations.erase (i);
1041 if (current_location == loc) {
1042 current_location = 0;
1052 removed (loc); /* EMIT SIGNAL */
1055 current_changed (0); /* EMIT SIGNAL */
1061 Locations::get_state ()
1063 XMLNode *node = new XMLNode ("Locations");
1064 LocationList::iterator iter;
1065 Glib::Threads::Mutex::Lock lm (lock);
1067 for (iter = locations.begin(); iter != locations.end(); ++iter) {
1068 node->add_child_nocopy ((*iter)->get_state ());
1075 Locations::set_state (const XMLNode& node, int version)
1077 if (node.name() != "Locations") {
1078 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1082 XMLNodeList nlist = node.children();
1084 /* build up a new locations list in here */
1085 LocationList new_locations;
1087 current_location = 0;
1089 Location* session_range_location = 0;
1090 if (version < 3000) {
1091 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1092 new_locations.push_back (session_range_location);
1096 Glib::Threads::Mutex::Lock lm (lock);
1098 XMLNodeConstIterator niter;
1099 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1103 XMLProperty const * prop_id = (*niter)->property ("id");
1105 PBD::ID id (prop_id->value ());
1107 LocationList::const_iterator i = locations.begin();
1108 while (i != locations.end () && (*i)->id() != id) {
1113 if (i != locations.end()) {
1114 /* we can re-use an old Location object */
1117 // changed locations will be updated by Locations::changed signal
1118 loc->set_state (**niter, version);
1120 loc = new Location (_session, **niter);
1125 if (version < 3000) {
1126 /* look for old-style IsStart / IsEnd properties in this location;
1127 if they are present, update the session_range_location accordingly
1129 XMLProperty const * prop = (*niter)->property ("flags");
1131 string v = prop->value ();
1133 string::size_type const c = v.find_first_of (',');
1134 string const s = v.substr (0, c);
1135 if (s == X_("IsStart")) {
1136 session_range_location->set_start (loc->start(), true);
1138 } else if (s == X_("IsEnd")) {
1139 session_range_location->set_end (loc->start(), true);
1143 if (c == string::npos) {
1147 v = v.substr (c + 1);
1153 new_locations.push_back (loc);
1157 catch (failed_constructor& err) {
1158 error << _("could not load location from session file - ignored") << endmsg;
1162 /* We may have some unused locations in the old list. */
1163 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1164 LocationList::iterator tmp = i;
1167 LocationList::iterator n = new_locations.begin();
1170 while (n != new_locations.end ()) {
1171 if ((*i)->id() == (*n)->id()) {
1180 locations.erase (i);
1186 locations = new_locations;
1188 if (locations.size()) {
1189 current_location = locations.front();
1191 current_location = 0;
1195 changed (); /* EMIT SIGNAL */
1201 typedef std::pair<framepos_t,Location*> LocationPair;
1203 struct LocationStartEarlierComparison
1205 bool operator() (LocationPair a, LocationPair b) {
1206 return a.first < b.first;
1210 struct LocationStartLaterComparison
1212 bool operator() (LocationPair a, LocationPair b) {
1213 return a.first > b.first;
1218 Locations::first_mark_before (framepos_t frame, bool include_special_ranges)
1220 Glib::Threads::Mutex::Lock lm (lock);
1221 vector<LocationPair> locs;
1223 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1224 locs.push_back (make_pair ((*i)->start(), (*i)));
1225 if (!(*i)->is_mark()) {
1226 locs.push_back (make_pair ((*i)->end(), (*i)));
1230 LocationStartLaterComparison cmp;
1231 sort (locs.begin(), locs.end(), cmp);
1233 /* locs is sorted in ascending order */
1235 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1236 if ((*i).second->is_hidden()) {
1239 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1242 if ((*i).first < frame) {
1251 Locations::mark_at (framepos_t pos, framecnt_t slop) const
1253 Glib::Threads::Mutex::Lock lm (lock);
1254 Location* closest = 0;
1255 frameoffset_t mindelta = max_framepos;
1256 frameoffset_t delta;
1258 /* locations are not necessarily stored in linear time order so we have
1259 * to iterate across all of them to find the one closest to a give point.
1262 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1264 if ((*i)->is_mark()) {
1265 if (pos > (*i)->start()) {
1266 delta = pos - (*i)->start();
1268 delta = (*i)->start() - pos;
1271 if (slop == 0 && delta == 0) {
1272 /* special case: no slop, and direct hit for position */
1276 if (delta <= slop) {
1277 if (delta < mindelta) {
1289 Locations::first_mark_after (framepos_t frame, bool include_special_ranges)
1291 Glib::Threads::Mutex::Lock lm (lock);
1292 vector<LocationPair> locs;
1294 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1295 locs.push_back (make_pair ((*i)->start(), (*i)));
1296 if (!(*i)->is_mark()) {
1297 locs.push_back (make_pair ((*i)->end(), (*i)));
1301 LocationStartEarlierComparison cmp;
1302 sort (locs.begin(), locs.end(), cmp);
1304 /* locs is sorted in reverse order */
1306 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1307 if ((*i).second->is_hidden()) {
1310 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1313 if ((*i).first > frame) {
1321 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1322 * side of a frame. Note that if frame is exactly on a `mark', that mark will not be considered for returning
1324 * @param frame Frame to look for.
1325 * @param before Filled in with the position of the last `mark' before `frame' (or max_framepos if none exists)
1326 * @param after Filled in with the position of the next `mark' after `frame' (or max_framepos if none exists)
1329 Locations::marks_either_side (framepos_t const frame, framepos_t& before, framepos_t& after) const
1331 before = after = max_framepos;
1336 Glib::Threads::Mutex::Lock lm (lock);
1340 /* Get a list of positions; don't store any that are exactly on our requested position */
1342 std::list<framepos_t> positions;
1344 for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1345 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1349 if (!(*i)->is_hidden()) {
1350 if ((*i)->is_mark ()) {
1351 if ((*i)->start() != frame) {
1352 positions.push_back ((*i)->start ());
1355 if ((*i)->start() != frame) {
1356 positions.push_back ((*i)->start ());
1358 if ((*i)->end() != frame) {
1359 positions.push_back ((*i)->end ());
1365 if (positions.empty ()) {
1371 std::list<framepos_t>::iterator i = positions.begin ();
1372 while (i != positions.end () && *i < frame) {
1376 if (i == positions.end ()) {
1377 /* run out of marks */
1378 before = positions.back ();
1384 if (i == positions.begin ()) {
1394 Locations::session_range_location () const
1396 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1397 if ((*i)->is_session_range()) {
1398 return const_cast<Location*> (*i);
1405 Locations::auto_loop_location () const
1407 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1408 if ((*i)->is_auto_loop()) {
1409 return const_cast<Location*> (*i);
1416 Locations::auto_punch_location () const
1418 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1419 if ((*i)->is_auto_punch()) {
1420 return const_cast<Location*> (*i);
1427 Locations::num_range_markers () const
1430 Glib::Threads::Mutex::Lock lm (lock);
1431 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1432 if ((*i)->is_range_marker()) {
1440 Locations::get_location_by_id(PBD::ID id)
1442 LocationList::iterator it;
1443 for (it = locations.begin(); it != locations.end(); ++it)
1444 if (id == (*it)->id())
1451 Locations::find_all_between (framepos_t start, framepos_t end, LocationList& ll, Location::Flags flags)
1453 Glib::Threads::Mutex::Lock lm (lock);
1455 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1456 if ((flags == 0 || (*i)->matches (flags)) &&
1457 ((*i)->start() >= start && (*i)->end() < end)) {