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