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) {
1038 bool was_loop = (*i)->is_auto_loop();
1040 locations.erase (i);
1042 if (current_location == loc) {
1043 current_location = 0;
1047 if (_session.get_play_loop()) {
1048 _session.request_play_loop (false, false);
1050 _session.auto_loop_location_changed (0);
1059 removed (loc); /* EMIT SIGNAL */
1062 current_changed (0); /* EMIT SIGNAL */
1068 Locations::get_state ()
1070 XMLNode *node = new XMLNode ("Locations");
1071 LocationList::iterator iter;
1072 Glib::Threads::Mutex::Lock lm (lock);
1074 for (iter = locations.begin(); iter != locations.end(); ++iter) {
1075 node->add_child_nocopy ((*iter)->get_state ());
1082 Locations::set_state (const XMLNode& node, int version)
1084 if (node.name() != "Locations") {
1085 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1089 XMLNodeList nlist = node.children();
1091 /* build up a new locations list in here */
1092 LocationList new_locations;
1094 current_location = 0;
1096 Location* session_range_location = 0;
1097 if (version < 3000) {
1098 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1099 new_locations.push_back (session_range_location);
1103 Glib::Threads::Mutex::Lock lm (lock);
1105 XMLNodeConstIterator niter;
1106 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1110 XMLProperty const * prop_id = (*niter)->property ("id");
1112 PBD::ID id (prop_id->value ());
1114 LocationList::const_iterator i = locations.begin();
1115 while (i != locations.end () && (*i)->id() != id) {
1120 if (i != locations.end()) {
1121 /* we can re-use an old Location object */
1124 // changed locations will be updated by Locations::changed signal
1125 loc->set_state (**niter, version);
1127 loc = new Location (_session, **niter);
1132 if (version < 3000) {
1133 /* look for old-style IsStart / IsEnd properties in this location;
1134 if they are present, update the session_range_location accordingly
1136 XMLProperty const * prop = (*niter)->property ("flags");
1138 string v = prop->value ();
1140 string::size_type const c = v.find_first_of (',');
1141 string const s = v.substr (0, c);
1142 if (s == X_("IsStart")) {
1143 session_range_location->set_start (loc->start(), true);
1145 } else if (s == X_("IsEnd")) {
1146 session_range_location->set_end (loc->start(), true);
1150 if (c == string::npos) {
1154 v = v.substr (c + 1);
1160 new_locations.push_back (loc);
1164 catch (failed_constructor& err) {
1165 error << _("could not load location from session file - ignored") << endmsg;
1169 /* We may have some unused locations in the old list. */
1170 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1171 LocationList::iterator tmp = i;
1174 LocationList::iterator n = new_locations.begin();
1177 while (n != new_locations.end ()) {
1178 if ((*i)->id() == (*n)->id()) {
1187 locations.erase (i);
1193 locations = new_locations;
1195 if (locations.size()) {
1196 current_location = locations.front();
1198 current_location = 0;
1202 changed (); /* EMIT SIGNAL */
1208 typedef std::pair<framepos_t,Location*> LocationPair;
1210 struct LocationStartEarlierComparison
1212 bool operator() (LocationPair a, LocationPair b) {
1213 return a.first < b.first;
1217 struct LocationStartLaterComparison
1219 bool operator() (LocationPair a, LocationPair b) {
1220 return a.first > b.first;
1225 Locations::first_mark_before (framepos_t frame, bool include_special_ranges)
1227 Glib::Threads::Mutex::Lock lm (lock);
1228 vector<LocationPair> locs;
1230 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1231 locs.push_back (make_pair ((*i)->start(), (*i)));
1232 if (!(*i)->is_mark()) {
1233 locs.push_back (make_pair ((*i)->end(), (*i)));
1237 LocationStartLaterComparison cmp;
1238 sort (locs.begin(), locs.end(), cmp);
1240 /* locs is sorted in ascending order */
1242 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1243 if ((*i).second->is_hidden()) {
1246 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1249 if ((*i).first < frame) {
1258 Locations::mark_at (framepos_t pos, framecnt_t slop) const
1260 Glib::Threads::Mutex::Lock lm (lock);
1261 Location* closest = 0;
1262 frameoffset_t mindelta = max_framepos;
1263 frameoffset_t delta;
1265 /* locations are not necessarily stored in linear time order so we have
1266 * to iterate across all of them to find the one closest to a give point.
1269 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1271 if ((*i)->is_mark()) {
1272 if (pos > (*i)->start()) {
1273 delta = pos - (*i)->start();
1275 delta = (*i)->start() - pos;
1278 if (slop == 0 && delta == 0) {
1279 /* special case: no slop, and direct hit for position */
1283 if (delta <= slop) {
1284 if (delta < mindelta) {
1296 Locations::first_mark_after (framepos_t frame, bool include_special_ranges)
1298 Glib::Threads::Mutex::Lock lm (lock);
1299 vector<LocationPair> locs;
1301 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1302 locs.push_back (make_pair ((*i)->start(), (*i)));
1303 if (!(*i)->is_mark()) {
1304 locs.push_back (make_pair ((*i)->end(), (*i)));
1308 LocationStartEarlierComparison cmp;
1309 sort (locs.begin(), locs.end(), cmp);
1311 /* locs is sorted in reverse order */
1313 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1314 if ((*i).second->is_hidden()) {
1317 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1320 if ((*i).first > frame) {
1328 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1329 * side of a frame. Note that if frame is exactly on a `mark', that mark will not be considered for returning
1331 * @param frame Frame to look for.
1332 * @param before Filled in with the position of the last `mark' before `frame' (or max_framepos if none exists)
1333 * @param after Filled in with the position of the next `mark' after `frame' (or max_framepos if none exists)
1336 Locations::marks_either_side (framepos_t const frame, framepos_t& before, framepos_t& after) const
1338 before = after = max_framepos;
1343 Glib::Threads::Mutex::Lock lm (lock);
1347 /* Get a list of positions; don't store any that are exactly on our requested position */
1349 std::list<framepos_t> positions;
1351 for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1352 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1356 if (!(*i)->is_hidden()) {
1357 if ((*i)->is_mark ()) {
1358 if ((*i)->start() != frame) {
1359 positions.push_back ((*i)->start ());
1362 if ((*i)->start() != frame) {
1363 positions.push_back ((*i)->start ());
1365 if ((*i)->end() != frame) {
1366 positions.push_back ((*i)->end ());
1372 if (positions.empty ()) {
1378 std::list<framepos_t>::iterator i = positions.begin ();
1379 while (i != positions.end () && *i < frame) {
1383 if (i == positions.end ()) {
1384 /* run out of marks */
1385 before = positions.back ();
1391 if (i == positions.begin ()) {
1401 Locations::session_range_location () const
1403 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1404 if ((*i)->is_session_range()) {
1405 return const_cast<Location*> (*i);
1412 Locations::auto_loop_location () const
1414 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1415 if ((*i)->is_auto_loop()) {
1416 return const_cast<Location*> (*i);
1423 Locations::auto_punch_location () const
1425 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1426 if ((*i)->is_auto_punch()) {
1427 return const_cast<Location*> (*i);
1434 Locations::num_range_markers () const
1437 Glib::Threads::Mutex::Lock lm (lock);
1438 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1439 if ((*i)->is_range_marker()) {
1447 Locations::get_location_by_id(PBD::ID id)
1449 LocationList::iterator it;
1450 for (it = locations.begin(); it != locations.end(); ++it)
1451 if (id == (*it)->id())
1458 Locations::find_all_between (framepos_t start, framepos_t end, LocationList& ll, Location::Flags flags)
1460 Glib::Threads::Mutex::Lock lm (lock);
1462 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1463 if ((flags == 0 || (*i)->matches (flags)) &&
1464 ((*i)->start() >= start && (*i)->end() < end)) {