1 /*
2  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2006 Hans Fugal <hans@fugal.net>
4  * Copyright (C) 2007-2011 David Robillard <d@drobilla.net>
5  * Copyright (C) 2008-2009 Sakari Bergen <sakari.bergen@beatwaves.net>
6  * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
7  * Copyright (C) 2015-2016 Nick Mainsbridge <mainsbridge@gmail.com>
8  * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
9  * Copyright (C) 2015 GZharun <grygoriiz@wavesglobal.com>
10  * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26 
27 #include <algorithm>
28 #include <set>
29 #include <cstdio> /* for sprintf */
30 #include <unistd.h>
31 #include <cerrno>
32 #include <ctime>
33 #include <list>
34 
35 #include "pbd/types_convert.h"
36 #include "pbd/stl_delete.h"
37 #include "pbd/xml++.h"
38 #include "pbd/enumwriter.h"
39 
40 #include "ardour/location.h"
41 #include "ardour/midi_scene_change.h"
42 #include "ardour/session.h"
43 #include "ardour/audiofilesource.h"
44 #include "ardour/tempo.h"
45 #include "ardour/types_convert.h"
46 
47 #include "pbd/i18n.h"
48 
49 namespace PBD {
50 	DEFINE_ENUM_CONVERT(ARDOUR::Location::Flags);
51 }
52 
53 using namespace std;
54 using namespace ARDOUR;
55 using namespace PBD;
56 
57 PBD::Signal0<void> Location::scene_changed;
58 PBD::Signal1<void,Location*> Location::name_changed;
59 PBD::Signal1<void,Location*> Location::end_changed;
60 PBD::Signal1<void,Location*> Location::start_changed;
61 PBD::Signal1<void,Location*> Location::flags_changed;
62 PBD::Signal1<void,Location*> Location::lock_changed;
63 PBD::Signal1<void,Location*> Location::position_lock_style_changed;
64 PBD::Signal1<void,Location*> Location::changed;
65 
Location(Session & s)66 Location::Location (Session& s)
67 	: SessionHandleRef (s)
68 	, _start (0)
69 	, _start_beat (0.0)
70 	, _end (0)
71 	, _end_beat (0.0)
72 	, _flags (Flags (0))
73 	, _locked (false)
74 	, _position_lock_style (AudioTime)
75 	, _timestamp (time (0))
76 {
77 	assert (_start >= 0);
78 	assert (_end >= 0);
79 }
80 
81 /** Construct a new Location, giving it the position lock style determined by glue-new-markers-to-bars-and-beats */
Location(Session & s,samplepos_t sample_start,samplepos_t sample_end,const std::string & name,Flags bits,const uint32_t sub_num)82 Location::Location (Session& s, samplepos_t sample_start, samplepos_t sample_end, const std::string &name, Flags bits, const uint32_t sub_num)
83 	: SessionHandleRef (s)
84 	, _name (name)
85 	, _start (sample_start)
86 	, _end (sample_end)
87 	, _flags (bits)
88 	, _locked (false)
89 	, _position_lock_style (s.config.get_glue_new_markers_to_bars_and_beats() ? MusicTime : AudioTime)
90 	, _timestamp (time (0))
91 {
92 	recompute_beat_from_samples (sub_num);
93 
94 	assert (_start >= 0);
95 	assert (_end >= 0);
96 }
97 
Location(const Location & other)98 Location::Location (const Location& other)
99 	: SessionHandleRef (other._session)
100 	, StatefulDestructible()
101 	, _name (other._name)
102 	, _start (other._start)
103 	, _start_beat (other._start_beat)
104 	, _end (other._end)
105 	, _end_beat (other._end_beat)
106 	, _flags (other._flags)
107 	, _position_lock_style (other._position_lock_style)
108 	, _timestamp (time (0))
109 {
110 	/* copy is not locked even if original was */
111 
112 	_locked = false;
113 
114 	assert (_start >= 0);
115 	assert (_end >= 0);
116 
117 	/* scene change is NOT COPIED */
118 }
119 
Location(Session & s,const XMLNode & node)120 Location::Location (Session& s, const XMLNode& node)
121 	: SessionHandleRef (s)
122 	, _flags (Flags (0))
123 	, _position_lock_style (AudioTime)
124 	, _timestamp (time (0))
125 {
126 	/* Note: _position_lock_style is initialised above in case set_state doesn't set it
127 	   (for 2.X session file compatibility).
128 	*/
129 
130 	if (set_state (node, Stateful::loading_state_version)) {
131 		throw failed_constructor ();
132 	}
133 
134 	assert (_start >= 0);
135 	assert (_end >= 0);
136 }
137 
138 bool
operator ==(const Location & other)139 Location::operator== (const Location& other)
140 {
141 	if (_name != other._name ||
142 	    _start != other._start ||
143 	    _end != other._end ||
144 	    _start_beat != other._start_beat ||
145 	    _end_beat != other._end_beat ||
146 	    _flags != other._flags ||
147 	    _position_lock_style != other._position_lock_style) {
148 		return false;
149 	}
150 	return true;
151 }
152 
153 Location*
operator =(const Location & other)154 Location::operator= (const Location& other)
155 {
156 	if (this == &other) {
157 		return this;
158 	}
159 
160 	_name = other._name;
161 	_start = other._start;
162 	_start_beat = other._start_beat;
163 	_end = other._end;
164 	_end_beat = other._end_beat;
165 	_flags = other._flags;
166 	_position_lock_style = other._position_lock_style;
167 
168 	/* XXX need to copy scene change */
169 
170 	/* copy is not locked even if original was */
171 
172 	_locked = false;
173 
174 	/* "changed" not emitted on purpose */
175 
176 	assert (_start >= 0);
177 	assert (_end >= 0);
178 
179 	return this;
180 }
181 
182 /** Set location name
183  */
184 
185 void
set_name(const std::string & str)186 Location::set_name (const std::string& str)
187 {
188 	_name = str;
189 
190 	name_changed (this); /* EMIT SIGNAL */
191 	NameChanged  (); /* EMIT SIGNAL */
192 }
193 
194 /** Set start position.
195  *  @param s New start.
196  *  @param force true to force setting, even if the given new start is after the current end.
197  *  @param allow_beat_recompute True to recompute BEAT start time from the new given start time.
198  */
199 int
set_start(samplepos_t s,bool force,bool allow_beat_recompute,const uint32_t sub_num)200 Location::set_start (samplepos_t s, bool force, bool allow_beat_recompute, const uint32_t sub_num)
201 {
202 	if (s < 0) {
203 		return -1;
204 	}
205 
206 	if (_locked) {
207 		return -1;
208 	}
209 
210 	if (!force) {
211 		if (((is_auto_punch() || is_auto_loop()) && s >= _end) || (!is_mark() && s > _end)) {
212 			return -1;
213 		}
214 	}
215 
216 	if (is_mark()) {
217 		if (_start != s) {
218 			_start = s;
219 			_end = s;
220 			if (allow_beat_recompute) {
221 				recompute_beat_from_samples (sub_num);
222 			}
223 
224 			start_changed (this); /* EMIT SIGNAL */
225 			StartChanged (); /* EMIT SIGNAL */
226 			//end_changed (this); /* EMIT SIGNAL */
227 			//EndChanged (); /* EMIT SIGNAL */
228 		}
229 
230 		/* moving the start (position) of a marker with a scene change
231 		   requires an update in the Scene Changer.
232 		*/
233 
234 		if (_scene_change) {
235 			scene_changed (); /* EMIT SIGNAL */
236 		}
237 
238 		assert (_start >= 0);
239 		assert (_end >= 0);
240 
241 		return 0;
242 	} else if (!force) {
243 		/* range locations must exceed a minimum duration */
244 		if (_end - s < Config->get_range_location_minimum()) {
245 			return -1;
246 		}
247 	}
248 
249 	if (s != _start) {
250 
251 		samplepos_t const old = _start;
252 
253 		_start = s;
254 		if (allow_beat_recompute) {
255 			recompute_beat_from_samples (sub_num);
256 		}
257 		start_changed (this); /* EMIT SIGNAL */
258 		StartChanged (); /* EMIT SIGNAL */
259 
260 		if (is_session_range ()) {
261 			Session::StartTimeChanged (old); /* EMIT SIGNAL */
262 			AudioFileSource::set_header_position_offset (s);
263 		}
264 	}
265 
266 	assert (_start >= 0);
267 
268 	return 0;
269 }
270 
271 /** Set end position.
272  *  @param s New end.
273  *  @param force true to force setting, even if the given new end is before the current start.
274  *  @param allow_beat_recompute True to recompute BEAT end time from the new given end time.
275  */
276 int
set_end(samplepos_t e,bool force,bool allow_beat_recompute,const uint32_t sub_num)277 Location::set_end (samplepos_t e, bool force, bool allow_beat_recompute, const uint32_t sub_num)
278 {
279 	if (e < 0) {
280 		return -1;
281 	}
282 
283 	if (_locked) {
284 		return -1;
285 	}
286 
287 	if (!force) {
288 		if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
289 			return -1;
290 		}
291 	}
292 
293 	if (is_mark()) {
294 		if (_start != e) {
295 			_start = e;
296 			_end = e;
297 			if (allow_beat_recompute) {
298 				recompute_beat_from_samples (sub_num);
299 			}
300 			//start_changed (this); /* EMIT SIGNAL */
301 			//StartChanged (); /* EMIT SIGNAL */
302 			end_changed (this); /* EMIT SIGNAL */
303 			EndChanged (); /* EMIT SIGNAL */
304 		}
305 
306 		assert (_start >= 0);
307 		assert (_end >= 0);
308 
309 		return 0;
310 	} else if (!force) {
311 		/* range locations must exceed a minimum duration */
312 		if (e - _start < Config->get_range_location_minimum()) {
313 			return -1;
314 		}
315 	}
316 
317 	if (e != _end) {
318 
319 		samplepos_t const old = _end;
320 
321 		_end = e;
322 		if (allow_beat_recompute) {
323 			recompute_beat_from_samples (sub_num);
324 		}
325 
326 		end_changed(this); /* EMIT SIGNAL */
327 		EndChanged(); /* EMIT SIGNAL */
328 
329 		if (is_session_range()) {
330 			Session::EndTimeChanged (old); /* EMIT SIGNAL */
331 		}
332 	}
333 
334 	assert (_end >= 0);
335 
336 	return 0;
337 }
338 
339 int
set(samplepos_t s,samplepos_t e,bool allow_beat_recompute,const uint32_t sub_num)340 Location::set (samplepos_t s, samplepos_t e, bool allow_beat_recompute, const uint32_t sub_num)
341 {
342 	if (s < 0 || e < 0) {
343 		return -1;
344 	}
345 
346 	/* check validity */
347 	if (((is_auto_punch() || is_auto_loop()) && s >= e) || (!is_mark() && s > e)) {
348 		return -1;
349 	}
350 
351 	bool start_change = false;
352 	bool end_change = false;
353 
354 	if (is_mark()) {
355 
356 		if (_start != s) {
357 			_start = s;
358 			_end = s;
359 
360 			if (allow_beat_recompute) {
361 				recompute_beat_from_samples (sub_num);
362 			}
363 
364 			start_change = true;
365 			end_change = true;
366 		}
367 
368 		assert (_start >= 0);
369 		assert (_end >= 0);
370 
371 	} else {
372 
373 		/* range locations must exceed a minimum duration */
374 		if (e - s < Config->get_range_location_minimum()) {
375 			return -1;
376 		}
377 
378 		if (s != _start) {
379 
380 			samplepos_t const old = _start;
381 			_start = s;
382 
383 			if (allow_beat_recompute) {
384 				recompute_beat_from_samples (sub_num);
385 			}
386 
387 			start_change = true;
388 
389 			if (is_session_range ()) {
390 				Session::StartTimeChanged (old); /* EMIT SIGNAL */
391 				AudioFileSource::set_header_position_offset (s);
392 			}
393 		}
394 
395 
396 		if (e != _end) {
397 
398 			samplepos_t const old = _end;
399 			_end = e;
400 
401 			if (allow_beat_recompute) {
402 				recompute_beat_from_samples (sub_num);
403 			}
404 
405 			end_change = true;
406 
407 			if (is_session_range()) {
408 				Session::EndTimeChanged (old); /* EMIT SIGNAL */
409 			}
410 		}
411 
412 		assert (_end >= 0);
413 	}
414 
415 	if (start_change && end_change) {
416 		changed (this);
417 		Changed ();
418 	} else if (start_change) {
419 		start_changed(this); /* EMIT SIGNAL */
420 		StartChanged(); /* EMIT SIGNAL */
421 	} else if (end_change) {
422 		end_changed(this); /* EMIT SIGNAL */
423 		EndChanged(); /* EMIT SIGNAL */
424 	}
425 
426 	return 0;
427 }
428 
429 int
move_to(samplepos_t pos,const uint32_t sub_num)430 Location::move_to (samplepos_t pos, const uint32_t sub_num)
431 {
432 	if (pos < 0) {
433 		return -1;
434 	}
435 
436 	if (_locked) {
437 		return -1;
438 	}
439 
440 	if (_start != pos) {
441 		_start = pos;
442 		_end = _start + length();
443 		recompute_beat_from_samples (sub_num);
444 
445 		changed (this); /* EMIT SIGNAL */
446 		Changed (); /* EMIT SIGNAL */
447 	}
448 
449 	assert (_start >= 0);
450 	assert (_end >= 0);
451 
452 	return 0;
453 }
454 
455 void
set_hidden(bool yn,void *)456 Location::set_hidden (bool yn, void*)
457 {
458 	/* do not allow session range markers to be hidden */
459 	if (is_session_range()) {
460 		return;
461 	}
462 
463 	if (set_flag_internal (yn, IsHidden)) {
464 		flags_changed (this); /* EMIT SIGNAL */
465 		FlagsChanged ();
466 	}
467 }
468 
469 void
set_cd(bool yn,void *)470 Location::set_cd (bool yn, void*)
471 {
472 	if (set_flag_internal (yn, IsCDMarker)) {
473 		flags_changed (this); /* EMIT SIGNAL */
474 		FlagsChanged ();
475 	}
476 }
477 
478 void
set_is_range_marker(bool yn,void *)479 Location::set_is_range_marker (bool yn, void*)
480 {
481 	if (set_flag_internal (yn, IsRangeMarker)) {
482 		flags_changed (this);
483 		FlagsChanged (); /* EMIT SIGNAL */
484 	}
485 }
486 
487 void
set_is_clock_origin(bool yn,void *)488 Location::set_is_clock_origin (bool yn, void*)
489 {
490 	if (set_flag_internal (yn, IsClockOrigin)) {
491 		flags_changed (this);
492 		FlagsChanged (); /* EMIT SIGNAL */
493 	}
494 }
495 
496 void
set_skip(bool yn)497 Location::set_skip (bool yn)
498 {
499 	if (is_range_marker() && length() > 0) {
500 		if (set_flag_internal (yn, IsSkip)) {
501 			flags_changed (this);
502 			FlagsChanged ();
503 		}
504 	}
505 }
506 
507 void
set_skipping(bool yn)508 Location::set_skipping (bool yn)
509 {
510 	if (is_range_marker() && is_skip() && length() > 0) {
511 		if (set_flag_internal (yn, IsSkipping)) {
512 			flags_changed (this);
513 			FlagsChanged ();
514 		}
515 	}
516 }
517 
518 void
set_auto_punch(bool yn,void *)519 Location::set_auto_punch (bool yn, void*)
520 {
521 	if (is_mark() || _start == _end) {
522 		return;
523 	}
524 
525 	if (set_flag_internal (yn, IsAutoPunch)) {
526 		flags_changed (this); /* EMIT SIGNAL */
527 		FlagsChanged (); /* EMIT SIGNAL */
528 	}
529 }
530 
531 void
set_auto_loop(bool yn,void *)532 Location::set_auto_loop (bool yn, void*)
533 {
534 	if (is_mark() || _start == _end) {
535 		return;
536 	}
537 
538 	if (set_flag_internal (yn, IsAutoLoop)) {
539 		flags_changed (this); /* EMIT SIGNAL */
540 		FlagsChanged (); /* EMIT SIGNAL */
541 	}
542 }
543 
544 bool
set_flag_internal(bool yn,Flags flag)545 Location::set_flag_internal (bool yn, Flags flag)
546 {
547 	if (yn) {
548 		if (!(_flags & flag)) {
549 			_flags = Flags (_flags | flag);
550 			return true;
551 		}
552 	} else {
553 		if (_flags & flag) {
554 			_flags = Flags (_flags & ~flag);
555 			return true;
556 		}
557 	}
558 	return false;
559 }
560 
561 void
set_mark(bool yn)562 Location::set_mark (bool yn)
563 {
564 	/* This function is private, and so does not emit signals */
565 
566 	if (_start != _end) {
567 		return;
568 	}
569 
570 	set_flag_internal (yn, IsMark);
571 }
572 
573 
574 XMLNode&
cd_info_node(const string & name,const string & value)575 Location::cd_info_node(const string & name, const string & value)
576 {
577 	XMLNode* root = new XMLNode("CD-Info");
578 
579 	root->set_property("name", name);
580 	root->set_property("value", value);
581 
582 	return *root;
583 }
584 
585 
586 XMLNode&
get_state()587 Location::get_state ()
588 {
589 	XMLNode *node = new XMLNode ("Location");
590 
591 	typedef map<string, string>::const_iterator CI;
592 
593 	for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
594 		node->add_child_nocopy(cd_info_node(m->first, m->second));
595 	}
596 
597 	node->set_property ("id", id ());
598 	node->set_property ("name", name());
599 	node->set_property ("start", start());
600 	node->set_property ("end", end());
601 	if (position_lock_style() == MusicTime) {
602 		node->set_property ("start-beat", _start_beat);
603 		node->set_property ("end-beat", _end_beat);
604 	}
605 	node->set_property ("flags", _flags);
606 	node->set_property ("locked", _locked);
607 	node->set_property ("position-lock-style", _position_lock_style);
608 	node->set_property ("timestamp", _timestamp);
609 	if (_scene_change) {
610 		node->add_child_nocopy (_scene_change->get_state());
611 	}
612 
613 	return *node;
614 }
615 
616 int
set_state(const XMLNode & node,int version)617 Location::set_state (const XMLNode& node, int version)
618 {
619 	XMLNodeList cd_list = node.children();
620 	XMLNodeConstIterator cd_iter;
621 	XMLNode *cd_node;
622 
623 	string cd_name;
624 	string cd_value;
625 
626 	if (node.name() != "Location") {
627 		error << _("incorrect XML node passed to Location::set_state") << endmsg;
628 		return -1;
629 	}
630 
631 	if (!set_id (node)) {
632 		warning << _("XML node for Location has no ID information") << endmsg;
633 	}
634 
635 	std::string str;
636 	if (!node.get_property ("name", str)) {
637 		error << _("XML node for Location has no name information") << endmsg;
638 		return -1;
639 	}
640 
641 	set_name (str);
642 
643 	/* can't use set_start() here, because _end
644 	   may make the value of _start illegal.
645 	*/
646 
647 	if (!node.get_property ("start", _start)) {
648 		error << _("XML node for Location has no start information") << endmsg;
649 		return -1;
650 	}
651 
652 	if (!node.get_property ("end", _end)) {
653 		error << _("XML node for Location has no end information") << endmsg;
654 		return -1;
655 	}
656 
657 	node.get_property ("timestamp", _timestamp);
658 
659 	Flags old_flags (_flags);
660 
661 	if (!node.get_property ("flags", _flags)) {
662 		error << _("XML node for Location has no flags information") << endmsg;
663 		return -1;
664 	}
665 
666 	if (old_flags != _flags) {
667 		FlagsChanged ();
668 	}
669 
670 	if (!node.get_property ("locked", _locked)) {
671 		_locked = false;
672 	}
673 
674 	for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
675 
676 		cd_node = *cd_iter;
677 
678 		if (cd_node->name() != "CD-Info") {
679 			continue;
680 		}
681 
682 		if (!cd_node->get_property ("name", cd_name)) {
683 			throw failed_constructor ();
684 		}
685 
686 		if (!cd_node->get_property ("value", cd_value)) {
687 			throw failed_constructor ();
688 		}
689 
690 		cd_info[cd_name] = cd_value;
691 	}
692 
693 	node.get_property ("position-lock-style", _position_lock_style);
694 
695 	XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
696 
697 	if (scene_child) {
698 		_scene_change = SceneChange::factory (*scene_child, version);
699 	}
700 
701 	if (position_lock_style() == AudioTime) {
702 		recompute_beat_from_samples (0);
703 	} else{
704 		/* music */
705 		if (!node.get_property ("start-beat", _start_beat) ||
706 		    !node.get_property ("end-beat", _end_beat)) {
707 			recompute_beat_from_samples (0);
708 		}
709 	}
710 
711 
712 	changed (this); /* EMIT SIGNAL */
713 	Changed (); /* EMIT SIGNAL */
714 
715 	assert (_start >= 0);
716 	assert (_end >= 0);
717 
718 	return 0;
719 }
720 
721 void
set_position_lock_style(PositionLockStyle ps)722 Location::set_position_lock_style (PositionLockStyle ps)
723 {
724 	if (_position_lock_style == ps) {
725 		return;
726 	}
727 
728 	_position_lock_style = ps;
729 
730 	if (ps == MusicTime) {
731 		recompute_beat_from_samples (0);
732 	}
733 
734 	position_lock_style_changed (this); /* EMIT SIGNAL */
735 	PositionLockStyleChanged (); /* EMIT SIGNAL */
736 }
737 
738 void
recompute_beat_from_samples(const uint32_t sub_num)739 Location::recompute_beat_from_samples (const uint32_t sub_num)
740 {
741 	_start_beat = _session.tempo_map().exact_beat_at_sample (_start, sub_num);
742 	_end_beat = _session.tempo_map().exact_beat_at_sample (_end, sub_num);
743 }
744 
745 void
recompute_samples_from_beat()746 Location::recompute_samples_from_beat ()
747 {
748 	if (_position_lock_style != MusicTime) {
749 		return;
750 	}
751 
752 	TempoMap& map (_session.tempo_map());
753 	set (map.sample_at_beat (_start_beat), map.sample_at_beat (_end_beat), false);
754 }
755 
756 void
lock()757 Location::lock ()
758 {
759 	_locked = true;
760 	lock_changed (this);
761 	LockChanged ();
762 }
763 
764 void
unlock()765 Location::unlock ()
766 {
767 	_locked = false;
768 	lock_changed (this);
769 	LockChanged ();
770 }
771 
772 void
set_scene_change(boost::shared_ptr<SceneChange> sc)773 Location::set_scene_change (boost::shared_ptr<SceneChange>  sc)
774 {
775         if (_scene_change != sc) {
776                 _scene_change = sc;
777                 _session.set_dirty ();
778 
779                 scene_changed (); /* EMIT SIGNAL */
780                 SceneChangeChanged (); /* EMIT SIGNAL */
781         }
782 }
783 
784 /*---------------------------------------------------------------------- */
785 
Locations(Session & s)786 Locations::Locations (Session& s)
787 	: SessionHandleRef (s)
788 {
789 	current_location = 0;
790 }
791 
~Locations()792 Locations::~Locations ()
793 {
794 	Glib::Threads::RWLock::WriterLock lm (_lock);
795 	for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
796 		LocationList::iterator tmp = i;
797 		++tmp;
798 		delete *i;
799 		i = tmp;
800 	}
801 }
802 
803 int
set_current(Location * loc,bool want_lock)804 Locations::set_current (Location *loc, bool want_lock)
805 {
806 	int ret;
807 
808 	if (want_lock) {
809 		Glib::Threads::RWLock::ReaderLock lm (_lock);
810 		ret = set_current_unlocked (loc);
811 	} else {
812 		ret = set_current_unlocked (loc);
813 	}
814 
815 	if (ret == 0) {
816 		current_changed (current_location); /* EMIT SIGNAL */
817 	}
818 	return ret;
819 }
820 
821 void
set_clock_origin(Location * loc,void * src)822 Locations::set_clock_origin (Location* loc, void *src)
823 {
824 	LocationList::iterator i;
825 	for (i = locations.begin(); i != locations.end(); ++i) {
826 		if ((*i)->is_clock_origin ()) {
827 			(*i)->set_is_clock_origin (false, src);
828 		}
829 		if (*i == loc) {
830 			(*i)->set_is_clock_origin (true, src);
831 		}
832 	}
833 }
834 
835 int
next_available_name(string & result,string base)836 Locations::next_available_name(string& result,string base)
837 {
838 	LocationList::iterator i;
839 	string::size_type l;
840 	int suffix;
841 	char buf[32];
842 	std::map<uint32_t,bool> taken;
843 	uint32_t n;
844 
845 	result = base;
846 	l = base.length();
847 
848 	if (!base.empty()) {
849 
850 		/* find all existing names that match "base", and store
851 		   the numeric part of them (if any) in the map "taken"
852 		*/
853 
854 		for (i = locations.begin(); i != locations.end(); ++i) {
855 
856 			const string& temp ((*i)->name());
857 
858 			if (!temp.find (base,0)) {
859 				/* grab what comes after the "base" as if it was
860 				   a number, and assuming that works OK,
861 				   store it in "taken" so that we know it
862 				   has been used.
863 				*/
864                                 if ((suffix = atoi (temp.substr(l))) != 0) {
865 					taken.insert (make_pair (suffix,true));
866 				}
867 			}
868 		}
869 	}
870 
871 	/* Now search for an un-used suffix to add to "base". This
872 	   will find "holes" in the numbering sequence when a location
873 	   was deleted.
874 
875 	   This must start at 1, both for human-numbering reasons
876 	   and also because the call to atoi() above would return
877 	   zero if there is no recognizable numeric suffix, causing
878 	   "base 0" not to be inserted into the "taken" map.
879 	*/
880 
881 	n = 1;
882 
883 	while (n < UINT32_MAX) {
884 		if (taken.find (n) == taken.end()) {
885 			snprintf (buf, sizeof(buf), "%d", n);
886 			result += buf;
887 			return 1;
888 		}
889 		++n;
890 	}
891 
892 	return 0;
893 }
894 
895 int
set_current_unlocked(Location * loc)896 Locations::set_current_unlocked (Location *loc)
897 {
898 	if (find (locations.begin(), locations.end(), loc) == locations.end()) {
899 		error << _("Locations: attempt to use unknown location as selected location") << endmsg;
900 		return -1;
901 	}
902 
903 	current_location = loc;
904 	return 0;
905 }
906 
907 bool
clear()908 Locations::clear ()
909 {
910 	bool deleted = false;
911 
912 	{
913 		Glib::Threads::RWLock::WriterLock lm (_lock);
914 
915 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
916 
917 			LocationList::iterator tmp = i;
918 			++tmp;
919 
920 			if (!(*i)->is_session_range()) {
921 				delete *i;
922 				locations.erase (i);
923 				deleted = true;
924 			}
925 
926 			i = tmp;
927 		}
928 
929 		current_location = 0;
930 	}
931 	if (deleted) {
932 		changed (); /* EMIT SIGNAL */
933 		current_changed (0); /* EMIT SIGNAL */
934 	}
935 
936 	return deleted;
937 }
938 
939 bool
clear_markers()940 Locations::clear_markers ()
941 {
942 	bool deleted = false;
943 
944 	{
945 		Glib::Threads::RWLock::WriterLock lm (_lock);
946 		LocationList::iterator tmp;
947 
948 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
949 			tmp = i;
950 			++tmp;
951 
952 			if ((*i)->is_mark() && !(*i)->is_session_range()) {
953 				delete *i;
954 				locations.erase (i);
955 				deleted = true;
956 			}
957 
958 			i = tmp;
959 		}
960 	}
961 
962 	if (deleted) {
963 		changed (); /* EMIT SIGNAL */
964 	}
965 
966 	return deleted;
967 }
968 
969 bool
clear_xrun_markers()970 Locations::clear_xrun_markers ()
971 {
972 	bool deleted = false;
973 
974 	{
975 		Glib::Threads::RWLock::WriterLock lm (_lock);
976 		LocationList::iterator tmp;
977 
978 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
979 			tmp = i;
980 			++tmp;
981 
982 			if ((*i)->is_xrun()) {
983 				delete *i;
984 				locations.erase (i);
985 				deleted = true;
986 			}
987 
988 			i = tmp;
989 		}
990 	}
991 
992 	if (deleted) {
993 		changed (); /* EMIT SIGNAL */
994 	}
995 
996 	return deleted;
997 }
998 
999 bool
clear_ranges()1000 Locations::clear_ranges ()
1001 {
1002 	bool deleted = false;
1003 
1004 	{
1005 		Glib::Threads::RWLock::WriterLock lm (_lock);
1006 		LocationList::iterator tmp;
1007 
1008 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1009 
1010 			tmp = i;
1011 			++tmp;
1012 
1013 			/* We do not remove these ranges as part of this
1014 			 * operation
1015 			 */
1016 
1017 			if ((*i)->is_auto_punch() ||
1018 			    (*i)->is_auto_loop() ||
1019 			    (*i)->is_session_range()) {
1020 				i = tmp;
1021 				continue;
1022 			}
1023 
1024 			if (!(*i)->is_mark()) {
1025 				delete *i;
1026 				locations.erase (i);
1027 				deleted = true;
1028 			}
1029 
1030 			i = tmp;
1031 		}
1032 
1033 		current_location = 0;
1034 	}
1035 
1036 	if (deleted) {
1037 		changed (); /* EMIT SIGNAL */
1038 		current_changed (0); /* EMIT SIGNAL */
1039 	}
1040 
1041 	return deleted;
1042 }
1043 
1044 void
add(Location * loc,bool make_current)1045 Locations::add (Location *loc, bool make_current)
1046 {
1047 	assert (loc);
1048 
1049 	{
1050 		Glib::Threads::RWLock::WriterLock lm (_lock);
1051 		locations.push_back (loc);
1052 
1053 		if (make_current) {
1054 			current_location = loc;
1055 		}
1056 	}
1057 
1058 	added (loc); /* EMIT SIGNAL */
1059 
1060 	if (loc->name().empty()) {
1061 		string new_name;
1062 
1063 		if (loc->is_mark()) {
1064 			next_available_name (new_name, _("mark"));
1065 		} else {
1066 			next_available_name (new_name, _("range"));
1067 		}
1068 
1069 		loc->set_name (new_name);
1070 	}
1071 
1072 	if (make_current) {
1073 		current_changed (current_location); /* EMIT SIGNAL */
1074 	}
1075 
1076 	if (loc->is_session_range()) {
1077 		Session::StartTimeChanged (0);
1078 		Session::EndTimeChanged (1);
1079 	}
1080 }
1081 
1082 Location*
add_range(samplepos_t start,samplepos_t end)1083 Locations::add_range(samplepos_t start, samplepos_t end)
1084 {
1085 	string name;
1086 	next_available_name(name, _("range"));
1087 
1088 	Location* loc = new Location(_session, start, end, name, Location::IsRangeMarker);
1089 	add(loc, false);
1090 
1091 	return loc;
1092 }
1093 
1094 void
remove(Location * loc)1095 Locations::remove (Location *loc)
1096 {
1097 	bool was_removed = false;
1098 	bool was_current = false;
1099 	bool was_loop    = false;
1100 	LocationList::iterator i;
1101 
1102 	if (!loc) {
1103 		return;
1104 	}
1105 
1106 	if (loc->is_session_range()) {
1107 		return;
1108 	}
1109 
1110 	{
1111 		Glib::Threads::RWLock::WriterLock lm (_lock);
1112 
1113 		for (i = locations.begin(); i != locations.end(); ++i) {
1114 			if ((*i) != loc) {
1115 				continue;
1116 			}
1117 			was_loop = (*i)->is_auto_loop();
1118 			if ((*i)->is_auto_punch()) {
1119 				/* needs to happen before deleting:
1120 				 * disconnect signals, clear events */
1121 				lm.release ();
1122 				_session.set_auto_punch_location (0);
1123 				lm.acquire ();
1124 			}
1125 			delete *i;
1126 			locations.erase (i);
1127 			was_removed = true;
1128 			if (current_location == loc) {
1129 				current_location = 0;
1130 				was_current = true;
1131 			}
1132 			break;
1133 		}
1134 	}
1135 
1136 	if (was_removed) {
1137 
1138 		if (was_loop) {
1139 			if (_session.get_play_loop()) {
1140 				_session.request_play_loop (false, false);
1141 			}
1142 			_session.auto_loop_location_changed (0);
1143 		}
1144 
1145 		removed (loc); /* EMIT SIGNAL */
1146 
1147 		if (was_current) {
1148 			current_changed (0); /* EMIT SIGNAL */
1149 		}
1150 	}
1151 }
1152 
1153 XMLNode&
get_state()1154 Locations::get_state ()
1155 {
1156 	XMLNode *node = new XMLNode ("Locations");
1157 	LocationList::iterator iter;
1158 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1159 
1160 	for (iter = locations.begin(); iter != locations.end(); ++iter) {
1161 		node->add_child_nocopy ((*iter)->get_state ());
1162 	}
1163 
1164 	return *node;
1165 }
1166 
1167 int
set_state(const XMLNode & node,int version)1168 Locations::set_state (const XMLNode& node, int version)
1169 {
1170 	if (node.name() != "Locations") {
1171 		error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1172 		return -1;
1173 	}
1174 
1175 	XMLNodeList nlist = node.children();
1176 
1177 	/* build up a new locations list in here */
1178 	LocationList new_locations;
1179 
1180 	{
1181 		Glib::Threads::RWLock::WriterLock lm (_lock);
1182 
1183 		current_location = 0;
1184 
1185 		Location* session_range_location = 0;
1186 		if (version < 3000) {
1187 			session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1188 			new_locations.push_back (session_range_location);
1189 		}
1190 
1191 
1192 		XMLNodeConstIterator niter;
1193 		for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1194 
1195 			try {
1196 
1197 				XMLProperty const * prop_id = (*niter)->property ("id");
1198 				assert (prop_id);
1199 				PBD::ID id (prop_id->value ());
1200 
1201 				LocationList::const_iterator i = locations.begin();
1202 				while (i != locations.end () && (*i)->id() != id) {
1203 					++i;
1204 				}
1205 
1206 				Location* loc;
1207 				if (i != locations.end()) {
1208 					/* we can re-use an old Location object */
1209 					loc = *i;
1210 
1211 					// changed locations will be updated by Locations::changed signal
1212 					loc->set_state (**niter, version);
1213 				} else {
1214 					loc = new Location (_session, **niter);
1215 				}
1216 
1217 				bool add = true;
1218 
1219 				if (version < 3000) {
1220 					/* look for old-style IsStart / IsEnd properties in this location;
1221 					   if they are present, update the session_range_location accordingly
1222 					*/
1223 					XMLProperty const * prop = (*niter)->property ("flags");
1224 					if (prop) {
1225 						string v = prop->value ();
1226 						while (1) {
1227 							string::size_type const c = v.find_first_of (',');
1228 							string const s = v.substr (0, c);
1229 							if (s == X_("IsStart")) {
1230 								session_range_location->set_start (loc->start(), true);
1231 								add = false;
1232 							} else if (s == X_("IsEnd")) {
1233 								session_range_location->set_end (loc->start(), true);
1234 								add = false;
1235 							}
1236 
1237 							if (c == string::npos) {
1238 								break;
1239 							}
1240 
1241 							v = v.substr (c + 1);
1242 						}
1243 					}
1244 				}
1245 
1246 				if (add) {
1247 					new_locations.push_back (loc);
1248 				}
1249 			}
1250 
1251 			catch (failed_constructor& err) {
1252 				error << _("could not load location from session file - ignored") << endmsg;
1253 			}
1254 		}
1255 
1256 		/* We may have some unused locations in the old list. */
1257 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1258 			LocationList::iterator tmp = i;
1259 			++tmp;
1260 
1261 			LocationList::iterator n = new_locations.begin();
1262 			bool found = false;
1263 
1264 			while (n != new_locations.end ()) {
1265 				if ((*i)->id() == (*n)->id()) {
1266 					found = true;
1267 					break;
1268 				}
1269 				++n;
1270 			}
1271 
1272 			if (!found) {
1273 				delete *i;
1274 				locations.erase (i);
1275 			}
1276 
1277 			i = tmp;
1278 		}
1279 
1280 		locations = new_locations;
1281 
1282 		if (locations.size()) {
1283 			current_location = locations.front();
1284 		} else {
1285 			current_location = 0;
1286 		}
1287 	}
1288 
1289 	changed (); /* EMIT SIGNAL */
1290 
1291 	return 0;
1292 }
1293 
1294 
1295 typedef std::pair<samplepos_t,Location*> LocationPair;
1296 
1297 struct LocationStartEarlierComparison
1298 {
operator ()LocationStartEarlierComparison1299 	bool operator() (LocationPair a, LocationPair b) {
1300 		return a.first < b.first;
1301 	}
1302 };
1303 
1304 struct LocationStartLaterComparison
1305 {
operator ()LocationStartLaterComparison1306 	bool operator() (LocationPair a, LocationPair b) {
1307 		return a.first > b.first;
1308 	}
1309 };
1310 
1311 samplepos_t
first_mark_before(samplepos_t sample,bool include_special_ranges)1312 Locations::first_mark_before (samplepos_t sample, bool include_special_ranges)
1313 {
1314 	vector<LocationPair> locs;
1315 	{
1316 		Glib::Threads::RWLock::ReaderLock lm (_lock);
1317 
1318 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1319 			locs.push_back (make_pair ((*i)->start(), (*i)));
1320 			if (!(*i)->is_mark()) {
1321 				locs.push_back (make_pair ((*i)->end(), (*i)));
1322 			}
1323 		}
1324 	}
1325 
1326 	LocationStartLaterComparison cmp;
1327 	sort (locs.begin(), locs.end(), cmp);
1328 
1329 	/* locs is sorted in ascending order */
1330 
1331 	for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1332 		if ((*i).second->is_hidden()) {
1333 			continue;
1334 		}
1335 		if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1336 			continue;
1337 		}
1338 		if ((*i).first < sample) {
1339 			return (*i).first;
1340 		}
1341 	}
1342 
1343 	return -1;
1344 }
1345 
1346 Location*
mark_at(samplepos_t pos,samplecnt_t slop) const1347 Locations::mark_at (samplepos_t pos, samplecnt_t slop) const
1348 {
1349 	Location* closest = 0;
1350 	sampleoffset_t mindelta = max_samplepos;
1351 	sampleoffset_t delta;
1352 
1353 	/* locations are not necessarily stored in linear time order so we have
1354 	 * to iterate across all of them to find the one closest to a give point.
1355 	 */
1356 
1357 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1358 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1359 
1360 		if ((*i)->is_mark()) {
1361 			if (pos > (*i)->start()) {
1362 				delta = pos - (*i)->start();
1363 			} else {
1364 				delta = (*i)->start() - pos;
1365 			}
1366 
1367 			if (slop == 0 && delta == 0) {
1368 				/* special case: no slop, and direct hit for position */
1369 				return *i;
1370 			}
1371 
1372 			if (delta <= slop) {
1373 				if (delta < mindelta) {
1374 					closest = *i;
1375 					mindelta = delta;
1376 				}
1377 			}
1378 		}
1379 	}
1380 
1381 	return closest;
1382 }
1383 
1384 samplepos_t
first_mark_after(samplepos_t sample,bool include_special_ranges)1385 Locations::first_mark_after (samplepos_t sample, bool include_special_ranges)
1386 {
1387 	vector<LocationPair> locs;
1388 
1389 	{
1390 		Glib::Threads::RWLock::ReaderLock lm (_lock);
1391 		for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1392 			locs.push_back (make_pair ((*i)->start(), (*i)));
1393 			if (!(*i)->is_mark()) {
1394 				locs.push_back (make_pair ((*i)->end(), (*i)));
1395 			}
1396 		}
1397 	}
1398 
1399 	LocationStartEarlierComparison cmp;
1400 	sort (locs.begin(), locs.end(), cmp);
1401 
1402 	/* locs is sorted in reverse order */
1403 
1404 	for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1405 		if ((*i).second->is_hidden()) {
1406 			continue;
1407 		}
1408 		if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1409 			continue;
1410 		}
1411 		if ((*i).first > sample) {
1412 			return (*i).first;
1413 		}
1414 	}
1415 
1416 	return -1;
1417 }
1418 
1419 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1420  *  side of a sample.  Note that if sample is exactly on a `mark', that mark will not be considered for returning
1421  *  as before/after.
1422  *  @param sample Frame to look for.
1423  *  @param before Filled in with the position of the last `mark' before `sample' (or max_samplepos if none exists)
1424  *  @param after Filled in with the position of the next `mark' after `sample' (or max_samplepos if none exists)
1425  */
1426 void
marks_either_side(samplepos_t const sample,samplepos_t & before,samplepos_t & after) const1427 Locations::marks_either_side (samplepos_t const sample, samplepos_t& before, samplepos_t& after) const
1428 {
1429 	before = after = max_samplepos;
1430 
1431 	LocationList locs;
1432 
1433 	{
1434 		Glib::Threads::RWLock::ReaderLock lm (_lock);
1435 		locs = locations;
1436 	}
1437 
1438 	/* Get a list of positions; don't store any that are exactly on our requested position */
1439 
1440 	std::list<samplepos_t> positions;
1441 
1442 	for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1443 		if (((*i)->is_auto_loop() || (*i)->is_auto_punch()) || (*i)->is_xrun()) {
1444 			continue;
1445 		}
1446 
1447 		if (!(*i)->is_hidden()) {
1448 			if ((*i)->is_mark ()) {
1449 				if ((*i)->start() != sample) {
1450 					positions.push_back ((*i)->start ());
1451 				}
1452 			} else {
1453 				if ((*i)->start() != sample) {
1454 					positions.push_back ((*i)->start ());
1455 				}
1456 				if ((*i)->end() != sample) {
1457 					positions.push_back ((*i)->end ());
1458 				}
1459 			}
1460 		}
1461 	}
1462 
1463 	if (positions.empty ()) {
1464 		return;
1465 	}
1466 
1467 	positions.sort ();
1468 
1469 	std::list<samplepos_t>::iterator i = positions.begin ();
1470 	while (i != positions.end () && *i < sample) {
1471 		++i;
1472 	}
1473 
1474 	if (i == positions.end ()) {
1475 		/* run out of marks */
1476 		before = positions.back ();
1477 		return;
1478 	}
1479 
1480 	after = *i;
1481 
1482 	if (i == positions.begin ()) {
1483 		/* none before */
1484 		return;
1485 	}
1486 
1487 	--i;
1488 	before = *i;
1489 }
1490 
1491 Location*
session_range_location() const1492 Locations::session_range_location () const
1493 {
1494 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1495 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1496 		if ((*i)->is_session_range()) {
1497 			return const_cast<Location*> (*i);
1498 		}
1499 	}
1500 	return 0;
1501 }
1502 
1503 Location*
auto_loop_location() const1504 Locations::auto_loop_location () const
1505 {
1506 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1507 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1508 		if ((*i)->is_auto_loop()) {
1509 			return const_cast<Location*> (*i);
1510 		}
1511 	}
1512 	return 0;
1513 }
1514 
1515 Location*
auto_punch_location() const1516 Locations::auto_punch_location () const
1517 {
1518 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1519 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1520 		if ((*i)->is_auto_punch()) {
1521 			return const_cast<Location*> (*i);
1522 		}
1523 	}
1524 	return 0;
1525 }
1526 
1527 Location*
clock_origin_location() const1528 Locations::clock_origin_location () const
1529 {
1530 	Location* sr = 0;
1531 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1532 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1533 		if ((*i)->is_clock_origin()) {
1534 			return const_cast<Location*> (*i);
1535 		}
1536 		if ((*i)->is_session_range()) {
1537 			sr = const_cast<Location*> (*i);
1538 		}
1539 	}
1540 	/* fall back to session_range_location () */
1541 	return sr;
1542 }
1543 
1544 uint32_t
num_range_markers() const1545 Locations::num_range_markers () const
1546 {
1547 	uint32_t cnt = 0;
1548 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1549 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1550 		if ((*i)->is_range_marker()) {
1551 			++cnt;
1552 		}
1553 	}
1554 	return cnt;
1555 }
1556 
1557 Location *
get_location_by_id(PBD::ID id)1558 Locations::get_location_by_id(PBD::ID id)
1559 {
1560 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1561 	for (LocationList::const_iterator i  = locations.begin(); i != locations.end(); ++i) {
1562 		if (id == (*i)->id()) {
1563 			return const_cast<Location*> (*i);
1564 		}
1565 	}
1566 	return 0;
1567 }
1568 
1569 void
find_all_between(samplepos_t start,samplepos_t end,LocationList & ll,Location::Flags flags)1570 Locations::find_all_between (samplepos_t start, samplepos_t end, LocationList& ll, Location::Flags flags)
1571 {
1572 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1573 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1574 		if ((flags == 0 || (*i)->matches (flags)) &&
1575 		    ((*i)->start() >= start && (*i)->end() < end)) {
1576 			ll.push_back (*i);
1577 		}
1578 	}
1579 }
1580 
1581 Location *
range_starts_at(samplepos_t pos,samplecnt_t slop,bool incl) const1582 Locations::range_starts_at(samplepos_t pos, samplecnt_t slop, bool incl) const
1583 {
1584 	Location *closest = 0;
1585 	sampleoffset_t mindelta = max_samplepos;
1586 
1587 	Glib::Threads::RWLock::ReaderLock lm (_lock);
1588 	for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1589 		if (!(*i)->is_range_marker()) {
1590 			continue;
1591 		}
1592 
1593 		if (incl && (pos < (*i)->start() || pos > (*i)->end())) {
1594 			continue;
1595 		}
1596 
1597 		sampleoffset_t delta = std::abs((double)(pos - (*i)->start()));
1598 
1599 		if (delta == 0) {
1600 			return *i;
1601 		}
1602 
1603 		if (delta > slop) {
1604 			continue;
1605 		}
1606 
1607 		if (delta < mindelta) {
1608 			closest = *i;
1609 			mindelta = delta;
1610 		}
1611 	}
1612 
1613 	return closest;
1614 }
1615 
1616 void
ripple(samplepos_t at,samplecnt_t distance,bool include_locked,bool notify)1617 Locations::ripple (samplepos_t at, samplecnt_t distance, bool include_locked, bool notify)
1618 {
1619 	Glib::Threads::RWLock::WriterLock lm (_lock);
1620 
1621 	for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1622 
1623 		if (!include_locked && (*i)->locked()) {
1624 			continue;
1625 		}
1626 
1627 		bool locked = (*i)->locked();
1628 
1629 		if (locked) {
1630 			(*i)->unlock ();
1631 		}
1632 
1633 		if ((*i)->start() >= at) {
1634 			(*i)->set_start ((*i)->start() - distance);
1635 
1636 			if (!(*i)->is_mark()) {
1637 				(*i)->set_end ((*i)->end() - distance);
1638 			}
1639 		}
1640 
1641 		if (locked) {
1642 			(*i)->locked();
1643 		}
1644 	}
1645 
1646 	if (notify) {
1647 		changed(); /* EMIT SIGNAL */
1648 	}
1649 }
1650