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 node->add_property ("id", id ().to_s ());
579 node->add_property ("name", name());
580 snprintf (buf, sizeof (buf), "%" PRId64, start());
581 node->add_property ("start", buf);
582 snprintf (buf, sizeof (buf), "%" PRId64, end());
583 node->add_property ("end", buf);
585 if (position_lock_style() == MusicTime) {
586 snprintf (buf, sizeof (buf), "%lf", _start_beat);
587 node->add_property ("start-beat", buf);
588 snprintf (buf, sizeof (buf), "%lf", _end_beat);
589 node->add_property ("end-beat", buf);
592 node->add_property ("flags", enum_2_string (_flags));
593 node->add_property ("locked", (_locked ? "yes" : "no"));
594 node->add_property ("position-lock-style", enum_2_string (_position_lock_style));
597 node->add_child_nocopy (_scene_change->get_state());
604 Location::set_state (const XMLNode& node, int version)
606 XMLProperty const * prop;
608 XMLNodeList cd_list = node.children();
609 XMLNodeConstIterator cd_iter;
615 if (node.name() != "Location") {
616 error << _("incorrect XML node passed to Location::set_state") << endmsg;
620 if (!set_id (node)) {
621 warning << _("XML node for Location has no ID information") << endmsg;
624 if ((prop = node.property ("name")) == 0) {
625 error << _("XML node for Location has no name information") << endmsg;
629 set_name (prop->value());
631 if ((prop = node.property ("start")) == 0) {
632 error << _("XML node for Location has no start information") << endmsg;
636 /* can't use set_start() here, because _end
637 may make the value of _start illegal.
640 sscanf (prop->value().c_str(), "%" PRId64, &_start);
642 if ((prop = node.property ("end")) == 0) {
643 error << _("XML node for Location has no end information") << endmsg;
647 sscanf (prop->value().c_str(), "%" PRId64, &_end);
649 if ((prop = node.property ("flags")) == 0) {
650 error << _("XML node for Location has no flags information") << endmsg;
654 Flags old_flags (_flags);
655 _flags = Flags (string_2_enum (prop->value(), _flags));
657 if (old_flags != _flags) {
661 if ((prop = node.property ("locked")) != 0) {
662 _locked = string_is_affirmative (prop->value());
667 for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
671 if (cd_node->name() != "CD-Info") {
675 if ((prop = cd_node->property ("name")) != 0) {
676 cd_name = prop->value();
678 throw failed_constructor ();
681 if ((prop = cd_node->property ("value")) != 0) {
682 cd_value = prop->value();
684 throw failed_constructor ();
688 cd_info[cd_name] = cd_value;
691 if ((prop = node.property ("position-lock-style")) != 0) {
692 _position_lock_style = PositionLockStyle (string_2_enum (prop->value(), _position_lock_style));
695 XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
698 _scene_change = SceneChange::factory (*scene_child, version);
701 if (position_lock_style() == AudioTime) {
702 recompute_beat_from_frames (0);
705 bool has_beat = false;
707 if ((prop = node.property ("start-beat")) != 0) {
708 sscanf (prop->value().c_str(), "%lf", &_start_beat);
712 if ((prop = node.property ("end-beat")) != 0) {
713 sscanf (prop->value().c_str(), "%lf", &_end_beat);
718 recompute_beat_from_frames (0);
723 changed (this); /* EMIT SIGNAL */
724 Changed (); /* EMIT SIGNAL */
726 assert (_start >= 0);
733 Location::set_position_lock_style (PositionLockStyle ps)
735 if (_position_lock_style == ps) {
739 _position_lock_style = ps;
741 if (ps == MusicTime) {
742 recompute_beat_from_frames (0);
745 position_lock_style_changed (this); /* EMIT SIGNAL */
746 PositionLockStyleChanged (); /* EMIT SIGNAL */
750 Location::recompute_beat_from_frames (const uint32_t sub_num)
752 _start_beat = _session.tempo_map().exact_beat_at_frame (_start, sub_num);
753 _end_beat = _session.tempo_map().exact_beat_at_frame (_end, sub_num);
757 Location::recompute_frames_from_beat ()
759 if (_position_lock_style != MusicTime) {
763 TempoMap& map (_session.tempo_map());
764 set (map.frame_at_beat (_start_beat), map.frame_at_beat (_end_beat), false);
784 Location::set_scene_change (boost::shared_ptr<SceneChange> sc)
786 if (_scene_change != sc) {
788 _session.set_dirty ();
790 scene_changed (); /* EMIT SIGNAL */
791 SceneChangeChanged (); /* EMIT SIGNAL */
795 /*---------------------------------------------------------------------- */
797 Locations::Locations (Session& s)
798 : SessionHandleRef (s)
800 current_location = 0;
803 Locations::~Locations ()
805 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
806 LocationList::iterator tmp = i;
814 Locations::set_current (Location *loc, bool want_lock)
819 Glib::Threads::Mutex::Lock lm (lock);
820 ret = set_current_unlocked (loc);
822 ret = set_current_unlocked (loc);
826 current_changed (current_location); /* EMIT SIGNAL */
832 Locations::next_available_name(string& result,string base)
834 LocationList::iterator i;
838 std::map<uint32_t,bool> taken;
846 /* find all existing names that match "base", and store
847 the numeric part of them (if any) in the map "taken"
850 for (i = locations.begin(); i != locations.end(); ++i) {
852 const string& temp ((*i)->name());
854 if (!temp.find (base,0)) {
855 /* grab what comes after the "base" as if it was
856 a number, and assuming that works OK,
857 store it in "taken" so that we know it
860 if ((suffix = atoi (temp.substr(l))) != 0) {
861 taken.insert (make_pair (suffix,true));
867 /* Now search for an un-used suffix to add to "base". This
868 will find "holes" in the numbering sequence when a location
871 This must start at 1, both for human-numbering reasons
872 and also because the call to atoi() above would return
873 zero if there is no recognizable numeric suffix, causing
874 "base 0" not to be inserted into the "taken" map.
879 while (n < UINT32_MAX) {
880 if (taken.find (n) == taken.end()) {
881 snprintf (buf, sizeof(buf), "%d", n);
892 Locations::set_current_unlocked (Location *loc)
894 if (find (locations.begin(), locations.end(), loc) == locations.end()) {
895 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
899 current_location = loc;
907 Glib::Threads::Mutex::Lock lm (lock);
909 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
911 LocationList::iterator tmp = i;
914 if (!(*i)->is_session_range()) {
922 current_location = 0;
925 changed (); /* EMIT SIGNAL */
926 current_changed (0); /* EMIT SIGNAL */
930 Locations::clear_markers ()
933 Glib::Threads::Mutex::Lock lm (lock);
934 LocationList::iterator tmp;
936 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
940 if ((*i)->is_mark() && !(*i)->is_session_range()) {
949 changed (); /* EMIT SIGNAL */
953 Locations::clear_ranges ()
956 Glib::Threads::Mutex::Lock lm (lock);
957 LocationList::iterator tmp;
959 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
964 /* We do not remove these ranges as part of this
968 if ((*i)->is_auto_punch() ||
969 (*i)->is_auto_loop() ||
970 (*i)->is_session_range()) {
975 if (!(*i)->is_mark()) {
984 current_location = 0;
988 current_changed (0); /* EMIT SIGNAL */
992 Locations::add (Location *loc, bool make_current)
997 Glib::Threads::Mutex::Lock lm (lock);
998 locations.push_back (loc);
1001 current_location = loc;
1005 added (loc); /* EMIT SIGNAL */
1008 current_changed (current_location); /* EMIT SIGNAL */
1011 if (loc->is_session_range()) {
1012 Session::StartTimeChanged (0);
1013 Session::EndTimeChanged (1);
1018 Locations::remove (Location *loc)
1020 bool was_removed = false;
1021 bool was_current = false;
1022 LocationList::iterator i;
1028 if (loc->is_session_range()) {
1033 Glib::Threads::Mutex::Lock lm (lock);
1035 for (i = locations.begin(); i != locations.end(); ++i) {
1037 bool was_loop = (*i)->is_auto_loop();
1039 locations.erase (i);
1041 if (current_location == loc) {
1042 current_location = 0;
1046 if (_session.get_play_loop()) {
1047 _session.request_play_loop (false, false);
1049 _session.auto_loop_location_changed (0);
1058 removed (loc); /* EMIT SIGNAL */
1061 current_changed (0); /* EMIT SIGNAL */
1067 Locations::get_state ()
1069 XMLNode *node = new XMLNode ("Locations");
1070 LocationList::iterator iter;
1071 Glib::Threads::Mutex::Lock lm (lock);
1073 for (iter = locations.begin(); iter != locations.end(); ++iter) {
1074 node->add_child_nocopy ((*iter)->get_state ());
1081 Locations::set_state (const XMLNode& node, int version)
1083 if (node.name() != "Locations") {
1084 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1088 XMLNodeList nlist = node.children();
1090 /* build up a new locations list in here */
1091 LocationList new_locations;
1093 current_location = 0;
1095 Location* session_range_location = 0;
1096 if (version < 3000) {
1097 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1098 new_locations.push_back (session_range_location);
1102 Glib::Threads::Mutex::Lock lm (lock);
1104 XMLNodeConstIterator niter;
1105 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1109 XMLProperty const * prop_id = (*niter)->property ("id");
1111 PBD::ID id (prop_id->value ());
1113 LocationList::const_iterator i = locations.begin();
1114 while (i != locations.end () && (*i)->id() != id) {
1119 if (i != locations.end()) {
1120 /* we can re-use an old Location object */
1123 // changed locations will be updated by Locations::changed signal
1124 loc->set_state (**niter, version);
1126 loc = new Location (_session, **niter);
1131 if (version < 3000) {
1132 /* look for old-style IsStart / IsEnd properties in this location;
1133 if they are present, update the session_range_location accordingly
1135 XMLProperty const * prop = (*niter)->property ("flags");
1137 string v = prop->value ();
1139 string::size_type const c = v.find_first_of (',');
1140 string const s = v.substr (0, c);
1141 if (s == X_("IsStart")) {
1142 session_range_location->set_start (loc->start(), true);
1144 } else if (s == X_("IsEnd")) {
1145 session_range_location->set_end (loc->start(), true);
1149 if (c == string::npos) {
1153 v = v.substr (c + 1);
1159 new_locations.push_back (loc);
1163 catch (failed_constructor& err) {
1164 error << _("could not load location from session file - ignored") << endmsg;
1168 /* We may have some unused locations in the old list. */
1169 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1170 LocationList::iterator tmp = i;
1173 LocationList::iterator n = new_locations.begin();
1176 while (n != new_locations.end ()) {
1177 if ((*i)->id() == (*n)->id()) {
1186 locations.erase (i);
1192 locations = new_locations;
1194 if (locations.size()) {
1195 current_location = locations.front();
1197 current_location = 0;
1201 changed (); /* EMIT SIGNAL */
1207 typedef std::pair<framepos_t,Location*> LocationPair;
1209 struct LocationStartEarlierComparison
1211 bool operator() (LocationPair a, LocationPair b) {
1212 return a.first < b.first;
1216 struct LocationStartLaterComparison
1218 bool operator() (LocationPair a, LocationPair b) {
1219 return a.first > b.first;
1224 Locations::first_mark_before (framepos_t frame, bool include_special_ranges)
1226 Glib::Threads::Mutex::Lock lm (lock);
1227 vector<LocationPair> locs;
1229 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1230 locs.push_back (make_pair ((*i)->start(), (*i)));
1231 if (!(*i)->is_mark()) {
1232 locs.push_back (make_pair ((*i)->end(), (*i)));
1236 LocationStartLaterComparison cmp;
1237 sort (locs.begin(), locs.end(), cmp);
1239 /* locs is sorted in ascending order */
1241 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1242 if ((*i).second->is_hidden()) {
1245 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1248 if ((*i).first < frame) {
1257 Locations::mark_at (framepos_t pos, framecnt_t slop) const
1259 Glib::Threads::Mutex::Lock lm (lock);
1260 Location* closest = 0;
1261 frameoffset_t mindelta = max_framepos;
1262 frameoffset_t delta;
1264 /* locations are not necessarily stored in linear time order so we have
1265 * to iterate across all of them to find the one closest to a give point.
1268 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1270 if ((*i)->is_mark()) {
1271 if (pos > (*i)->start()) {
1272 delta = pos - (*i)->start();
1274 delta = (*i)->start() - pos;
1277 if (slop == 0 && delta == 0) {
1278 /* special case: no slop, and direct hit for position */
1282 if (delta <= slop) {
1283 if (delta < mindelta) {
1295 Locations::first_mark_after (framepos_t frame, bool include_special_ranges)
1297 Glib::Threads::Mutex::Lock lm (lock);
1298 vector<LocationPair> locs;
1300 for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1301 locs.push_back (make_pair ((*i)->start(), (*i)));
1302 if (!(*i)->is_mark()) {
1303 locs.push_back (make_pair ((*i)->end(), (*i)));
1307 LocationStartEarlierComparison cmp;
1308 sort (locs.begin(), locs.end(), cmp);
1310 /* locs is sorted in reverse order */
1312 for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1313 if ((*i).second->is_hidden()) {
1316 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1319 if ((*i).first > frame) {
1327 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1328 * side of a frame. Note that if frame is exactly on a `mark', that mark will not be considered for returning
1330 * @param frame Frame to look for.
1331 * @param before Filled in with the position of the last `mark' before `frame' (or max_framepos if none exists)
1332 * @param after Filled in with the position of the next `mark' after `frame' (or max_framepos if none exists)
1335 Locations::marks_either_side (framepos_t const frame, framepos_t& before, framepos_t& after) const
1337 before = after = max_framepos;
1342 Glib::Threads::Mutex::Lock lm (lock);
1346 /* Get a list of positions; don't store any that are exactly on our requested position */
1348 std::list<framepos_t> positions;
1350 for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1351 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1355 if (!(*i)->is_hidden()) {
1356 if ((*i)->is_mark ()) {
1357 if ((*i)->start() != frame) {
1358 positions.push_back ((*i)->start ());
1361 if ((*i)->start() != frame) {
1362 positions.push_back ((*i)->start ());
1364 if ((*i)->end() != frame) {
1365 positions.push_back ((*i)->end ());
1371 if (positions.empty ()) {
1377 std::list<framepos_t>::iterator i = positions.begin ();
1378 while (i != positions.end () && *i < frame) {
1382 if (i == positions.end ()) {
1383 /* run out of marks */
1384 before = positions.back ();
1390 if (i == positions.begin ()) {
1400 Locations::session_range_location () const
1402 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1403 if ((*i)->is_session_range()) {
1404 return const_cast<Location*> (*i);
1411 Locations::auto_loop_location () const
1413 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1414 if ((*i)->is_auto_loop()) {
1415 return const_cast<Location*> (*i);
1422 Locations::auto_punch_location () const
1424 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1425 if ((*i)->is_auto_punch()) {
1426 return const_cast<Location*> (*i);
1433 Locations::num_range_markers () const
1436 Glib::Threads::Mutex::Lock lm (lock);
1437 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1438 if ((*i)->is_range_marker()) {
1446 Locations::get_location_by_id(PBD::ID id)
1448 LocationList::iterator it;
1449 for (it = locations.begin(); it != locations.end(); ++it)
1450 if (id == (*it)->id())
1457 Locations::find_all_between (framepos_t start, framepos_t end, LocationList& ll, Location::Flags flags)
1459 Glib::Threads::Mutex::Lock lm (lock);
1461 for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1462 if ((flags == 0 || (*i)->matches (flags)) &&
1463 ((*i)->start() >= start && (*i)->end() < end)) {