Expose virtual-keyboard port as async-port
[ardour.git] / libs / ardour / location.cc
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
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 */
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
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
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
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*
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
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
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
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
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
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
456 Location::set_hidden (bool yn, void*)
457 {
458         if (set_flag_internal (yn, IsHidden)) {
459                 flags_changed (this); /* EMIT SIGNAL */
460                 FlagsChanged ();
461         }
462 }
463
464 void
465 Location::set_cd (bool yn, void*)
466 {
467         // XXX this really needs to be session start
468         // but its not available here - leave to GUI
469
470         if (yn && _start == 0) {
471                 error << _("You cannot put a CD marker at this position") << endmsg;
472                 return;
473         }
474
475         if (set_flag_internal (yn, IsCDMarker)) {
476                 flags_changed (this); /* EMIT SIGNAL */
477                 FlagsChanged ();
478         }
479 }
480
481 void
482 Location::set_is_range_marker (bool yn, void*)
483 {
484         if (set_flag_internal (yn, IsRangeMarker)) {
485                 flags_changed (this);
486                 FlagsChanged (); /* EMIT SIGNAL */
487         }
488 }
489
490 void
491 Location::set_is_clock_origin (bool yn, void*)
492 {
493         if (set_flag_internal (yn, IsClockOrigin)) {
494                 flags_changed (this);
495                 FlagsChanged (); /* EMIT SIGNAL */
496         }
497 }
498
499 void
500 Location::set_skip (bool yn)
501 {
502         if (is_range_marker() && length() > 0) {
503                 if (set_flag_internal (yn, IsSkip)) {
504                         flags_changed (this);
505                         FlagsChanged ();
506                 }
507         }
508 }
509
510 void
511 Location::set_skipping (bool yn)
512 {
513         if (is_range_marker() && is_skip() && length() > 0) {
514                 if (set_flag_internal (yn, IsSkipping)) {
515                         flags_changed (this);
516                         FlagsChanged ();
517                 }
518         }
519 }
520
521 void
522 Location::set_auto_punch (bool yn, void*)
523 {
524         if (is_mark() || _start == _end) {
525                 return;
526         }
527
528         if (set_flag_internal (yn, IsAutoPunch)) {
529                 flags_changed (this); /* EMIT SIGNAL */
530                 FlagsChanged (); /* EMIT SIGNAL */
531         }
532 }
533
534 void
535 Location::set_auto_loop (bool yn, void*)
536 {
537         if (is_mark() || _start == _end) {
538                 return;
539         }
540
541         if (set_flag_internal (yn, IsAutoLoop)) {
542                 flags_changed (this); /* EMIT SIGNAL */
543                 FlagsChanged (); /* EMIT SIGNAL */
544         }
545 }
546
547 bool
548 Location::set_flag_internal (bool yn, Flags flag)
549 {
550         if (yn) {
551                 if (!(_flags & flag)) {
552                         _flags = Flags (_flags | flag);
553                         return true;
554                 }
555         } else {
556                 if (_flags & flag) {
557                         _flags = Flags (_flags & ~flag);
558                         return true;
559                 }
560         }
561         return false;
562 }
563
564 void
565 Location::set_mark (bool yn)
566 {
567         /* This function is private, and so does not emit signals */
568
569         if (_start != _end) {
570                 return;
571         }
572
573         set_flag_internal (yn, IsMark);
574 }
575
576
577 XMLNode&
578 Location::cd_info_node(const string & name, const string & value)
579 {
580         XMLNode* root = new XMLNode("CD-Info");
581
582         root->set_property("name", name);
583         root->set_property("value", value);
584
585         return *root;
586 }
587
588
589 XMLNode&
590 Location::get_state ()
591 {
592         XMLNode *node = new XMLNode ("Location");
593
594         typedef map<string, string>::const_iterator CI;
595
596         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
597                 node->add_child_nocopy(cd_info_node(m->first, m->second));
598         }
599
600         node->set_property ("id", id ());
601         node->set_property ("name", name());
602         node->set_property ("start", start());
603         node->set_property ("end", end());
604         if (position_lock_style() == MusicTime) {
605                 node->set_property ("start-beat", _start_beat);
606                 node->set_property ("end-beat", _end_beat);
607         }
608         node->set_property ("flags", _flags);
609         node->set_property ("locked", _locked);
610         node->set_property ("position-lock-style", _position_lock_style);
611         node->set_property ("timestamp", _timestamp);
612         if (_scene_change) {
613                 node->add_child_nocopy (_scene_change->get_state());
614         }
615
616         return *node;
617 }
618
619 int
620 Location::set_state (const XMLNode& node, int version)
621 {
622         XMLNodeList cd_list = node.children();
623         XMLNodeConstIterator cd_iter;
624         XMLNode *cd_node;
625
626         string cd_name;
627         string cd_value;
628
629         if (node.name() != "Location") {
630                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
631                 return -1;
632         }
633
634         if (!set_id (node)) {
635                 warning << _("XML node for Location has no ID information") << endmsg;
636         }
637
638         std::string str;
639         if (!node.get_property ("name", str)) {
640                 error << _("XML node for Location has no name information") << endmsg;
641                 return -1;
642         }
643
644         set_name (str);
645
646         /* can't use set_start() here, because _end
647            may make the value of _start illegal.
648         */
649
650         if (!node.get_property ("start", _start)) {
651                 error << _("XML node for Location has no start information") << endmsg;
652                 return -1;
653         }
654
655         if (!node.get_property ("end", _end)) {
656                 error << _("XML node for Location has no end information") << endmsg;
657                 return -1;
658         }
659
660         node.get_property ("timestamp", _timestamp);
661
662         Flags old_flags (_flags);
663
664         if (!node.get_property ("flags", _flags)) {
665                 error << _("XML node for Location has no flags information") << endmsg;
666                 return -1;
667         }
668
669         if (old_flags != _flags) {
670                 FlagsChanged ();
671         }
672
673         if (!node.get_property ("locked", _locked)) {
674                 _locked = false;
675         }
676
677         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
678
679                 cd_node = *cd_iter;
680
681                 if (cd_node->name() != "CD-Info") {
682                         continue;
683                 }
684
685                 if (!cd_node->get_property ("name", cd_name)) {
686                         throw failed_constructor ();
687                 }
688
689                 if (!cd_node->get_property ("value", cd_value)) {
690                         throw failed_constructor ();
691                 }
692
693                 cd_info[cd_name] = cd_value;
694         }
695
696         node.get_property ("position-lock-style", _position_lock_style);
697
698         XMLNode* scene_child = find_named_node (node, SceneChange::xml_node_name);
699
700         if (scene_child) {
701                 _scene_change = SceneChange::factory (*scene_child, version);
702         }
703
704         if (position_lock_style() == AudioTime) {
705                 recompute_beat_from_samples (0);
706         } else{
707                 /* music */
708                 if (!node.get_property ("start-beat", _start_beat) ||
709                     !node.get_property ("end-beat", _end_beat)) {
710                         recompute_beat_from_samples (0);
711                 }
712         }
713
714
715         changed (this); /* EMIT SIGNAL */
716         Changed (); /* EMIT SIGNAL */
717
718         assert (_start >= 0);
719         assert (_end >= 0);
720
721         return 0;
722 }
723
724 void
725 Location::set_position_lock_style (PositionLockStyle ps)
726 {
727         if (_position_lock_style == ps) {
728                 return;
729         }
730
731         _position_lock_style = ps;
732
733         if (ps == MusicTime) {
734                 recompute_beat_from_samples (0);
735         }
736
737         position_lock_style_changed (this); /* EMIT SIGNAL */
738         PositionLockStyleChanged (); /* EMIT SIGNAL */
739 }
740
741 void
742 Location::recompute_beat_from_samples (const uint32_t sub_num)
743 {
744         _start_beat = _session.tempo_map().exact_beat_at_sample (_start, sub_num);
745         _end_beat = _session.tempo_map().exact_beat_at_sample (_end, sub_num);
746 }
747
748 void
749 Location::recompute_samples_from_beat ()
750 {
751         if (_position_lock_style != MusicTime) {
752                 return;
753         }
754
755         TempoMap& map (_session.tempo_map());
756         set (map.sample_at_beat (_start_beat), map.sample_at_beat (_end_beat), false);
757 }
758
759 void
760 Location::lock ()
761 {
762         _locked = true;
763         lock_changed (this);
764         LockChanged ();
765 }
766
767 void
768 Location::unlock ()
769 {
770         _locked = false;
771         lock_changed (this);
772         LockChanged ();
773 }
774
775 void
776 Location::set_scene_change (boost::shared_ptr<SceneChange>  sc)
777 {
778         if (_scene_change != sc) {
779                 _scene_change = sc;
780                 _session.set_dirty ();
781
782                 scene_changed (); /* EMIT SIGNAL */
783                 SceneChangeChanged (); /* EMIT SIGNAL */
784         }
785 }
786
787 /*---------------------------------------------------------------------- */
788
789 Locations::Locations (Session& s)
790         : SessionHandleRef (s)
791 {
792         current_location = 0;
793 }
794
795 Locations::~Locations ()
796 {
797         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
798                 LocationList::iterator tmp = i;
799                 ++tmp;
800                 delete *i;
801                 i = tmp;
802         }
803 }
804
805 int
806 Locations::set_current (Location *loc, bool want_lock)
807 {
808         int ret;
809
810         if (want_lock) {
811                 Glib::Threads::Mutex::Lock lm (lock);
812                 ret = set_current_unlocked (loc);
813         } else {
814                 ret = set_current_unlocked (loc);
815         }
816
817         if (ret == 0) {
818                 current_changed (current_location); /* EMIT SIGNAL */
819         }
820         return ret;
821 }
822
823 void
824 Locations::set_clock_origin (Location* loc, void *src)
825 {
826         LocationList::iterator i;
827         for (i = locations.begin(); i != locations.end(); ++i) {
828                 if ((*i)->is_clock_origin ()) {
829                         (*i)->set_is_clock_origin (false, src);
830                 }
831                 if (*i == loc) {
832                         (*i)->set_is_clock_origin (true, src);
833                 }
834         }
835 }
836
837 int
838 Locations::next_available_name(string& result,string base)
839 {
840         LocationList::iterator i;
841         string::size_type l;
842         int suffix;
843         char buf[32];
844         std::map<uint32_t,bool> taken;
845         uint32_t n;
846
847         result = base;
848         l = base.length();
849
850         if (!base.empty()) {
851
852                 /* find all existing names that match "base", and store
853                    the numeric part of them (if any) in the map "taken"
854                 */
855
856                 for (i = locations.begin(); i != locations.end(); ++i) {
857
858                         const string& temp ((*i)->name());
859
860                         if (!temp.find (base,0)) {
861                                 /* grab what comes after the "base" as if it was
862                                    a number, and assuming that works OK,
863                                    store it in "taken" so that we know it
864                                    has been used.
865                                 */
866                                 if ((suffix = atoi (temp.substr(l))) != 0) {
867                                         taken.insert (make_pair (suffix,true));
868                                 }
869                         }
870                 }
871         }
872
873         /* Now search for an un-used suffix to add to "base". This
874            will find "holes" in the numbering sequence when a location
875            was deleted.
876
877            This must start at 1, both for human-numbering reasons
878            and also because the call to atoi() above would return
879            zero if there is no recognizable numeric suffix, causing
880            "base 0" not to be inserted into the "taken" map.
881         */
882
883         n = 1;
884
885         while (n < UINT32_MAX) {
886                 if (taken.find (n) == taken.end()) {
887                         snprintf (buf, sizeof(buf), "%d", n);
888                         result += buf;
889                         return 1;
890                 }
891                 ++n;
892         }
893
894         return 0;
895 }
896
897 int
898 Locations::set_current_unlocked (Location *loc)
899 {
900         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
901                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
902                 return -1;
903         }
904
905         current_location = loc;
906         return 0;
907 }
908
909 void
910 Locations::clear ()
911 {
912         {
913                 Glib::Threads::Mutex::Lock 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                         }
924
925                         i = tmp;
926                 }
927
928                 current_location = 0;
929         }
930
931         changed (); /* EMIT SIGNAL */
932         current_changed (0); /* EMIT SIGNAL */
933 }
934
935 void
936 Locations::clear_markers ()
937 {
938         {
939                 Glib::Threads::Mutex::Lock lm (lock);
940                 LocationList::iterator tmp;
941
942                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
943                         tmp = i;
944                         ++tmp;
945
946                         if ((*i)->is_mark() && !(*i)->is_session_range()) {
947                                 delete *i;
948                                 locations.erase (i);
949                         }
950
951                         i = tmp;
952                 }
953         }
954
955         changed (); /* EMIT SIGNAL */
956 }
957
958 void
959 Locations::clear_ranges ()
960 {
961         {
962                 Glib::Threads::Mutex::Lock lm (lock);
963                 LocationList::iterator tmp;
964
965                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
966
967                         tmp = i;
968                         ++tmp;
969
970                         /* We do not remove these ranges as part of this
971                          * operation
972                          */
973
974                         if ((*i)->is_auto_punch() ||
975                             (*i)->is_auto_loop() ||
976                             (*i)->is_session_range()) {
977                                 i = tmp;
978                                 continue;
979                         }
980
981                         if (!(*i)->is_mark()) {
982                                 delete *i;
983                                 locations.erase (i);
984
985                         }
986
987                         i = tmp;
988                 }
989
990                 current_location = 0;
991         }
992
993         changed ();
994         current_changed (0); /* EMIT SIGNAL */
995 }
996
997 void
998 Locations::add (Location *loc, bool make_current)
999 {
1000         assert (loc);
1001
1002         {
1003                 Glib::Threads::Mutex::Lock lm (lock);
1004                 locations.push_back (loc);
1005
1006                 if (make_current) {
1007                         current_location = loc;
1008                 }
1009         }
1010
1011         added (loc); /* EMIT SIGNAL */
1012
1013         if (make_current) {
1014                 current_changed (current_location); /* EMIT SIGNAL */
1015         }
1016
1017         if (loc->is_session_range()) {
1018                 Session::StartTimeChanged (0);
1019                 Session::EndTimeChanged (1);
1020         }
1021 }
1022
1023 void
1024 Locations::remove (Location *loc)
1025 {
1026         bool was_removed = false;
1027         bool was_current = false;
1028         LocationList::iterator i;
1029
1030         if (!loc) {
1031                 return;
1032         }
1033
1034         if (loc->is_session_range()) {
1035                 return;
1036         }
1037
1038         {
1039                 Glib::Threads::Mutex::Lock lm (lock);
1040
1041                 for (i = locations.begin(); i != locations.end(); ++i) {
1042                         if ((*i) == loc) {
1043                                 bool was_loop = (*i)->is_auto_loop();
1044                                 delete *i;
1045                                 locations.erase (i);
1046                                 was_removed = true;
1047                                 if (current_location == loc) {
1048                                         current_location = 0;
1049                                         was_current = true;
1050                                 }
1051                                 if (was_loop) {
1052                                         if (_session.get_play_loop()) {
1053                                                 _session.request_play_loop (false, false);
1054                                         }
1055                                         _session.auto_loop_location_changed (0);
1056                                 }
1057                                 break;
1058                         }
1059                 }
1060         }
1061
1062         if (was_removed) {
1063
1064                 removed (loc); /* EMIT SIGNAL */
1065
1066                 if (was_current) {
1067                         current_changed (0); /* EMIT SIGNAL */
1068                 }
1069         }
1070 }
1071
1072 XMLNode&
1073 Locations::get_state ()
1074 {
1075         XMLNode *node = new XMLNode ("Locations");
1076         LocationList::iterator iter;
1077         Glib::Threads::Mutex::Lock lm (lock);
1078
1079         for (iter = locations.begin(); iter != locations.end(); ++iter) {
1080                 node->add_child_nocopy ((*iter)->get_state ());
1081         }
1082
1083         return *node;
1084 }
1085
1086 int
1087 Locations::set_state (const XMLNode& node, int version)
1088 {
1089         if (node.name() != "Locations") {
1090                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
1091                 return -1;
1092         }
1093
1094         XMLNodeList nlist = node.children();
1095
1096         /* build up a new locations list in here */
1097         LocationList new_locations;
1098
1099         current_location = 0;
1100
1101         Location* session_range_location = 0;
1102         if (version < 3000) {
1103                 session_range_location = new Location (_session, 0, 0, _("session"), Location::IsSessionRange, 0);
1104                 new_locations.push_back (session_range_location);
1105         }
1106
1107         {
1108                 Glib::Threads::Mutex::Lock lm (lock);
1109
1110                 XMLNodeConstIterator niter;
1111                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1112
1113                         try {
1114
1115                                 XMLProperty const * prop_id = (*niter)->property ("id");
1116                                 assert (prop_id);
1117                                 PBD::ID id (prop_id->value ());
1118
1119                                 LocationList::const_iterator i = locations.begin();
1120                                 while (i != locations.end () && (*i)->id() != id) {
1121                                         ++i;
1122                                 }
1123
1124                                 Location* loc;
1125                                 if (i != locations.end()) {
1126                                         /* we can re-use an old Location object */
1127                                         loc = *i;
1128
1129                                         // changed locations will be updated by Locations::changed signal
1130                                         loc->set_state (**niter, version);
1131                                 } else {
1132                                         loc = new Location (_session, **niter);
1133                                 }
1134
1135                                 bool add = true;
1136
1137                                 if (version < 3000) {
1138                                         /* look for old-style IsStart / IsEnd properties in this location;
1139                                            if they are present, update the session_range_location accordingly
1140                                         */
1141                                         XMLProperty const * prop = (*niter)->property ("flags");
1142                                         if (prop) {
1143                                                 string v = prop->value ();
1144                                                 while (1) {
1145                                                         string::size_type const c = v.find_first_of (',');
1146                                                         string const s = v.substr (0, c);
1147                                                         if (s == X_("IsStart")) {
1148                                                                 session_range_location->set_start (loc->start(), true);
1149                                                                 add = false;
1150                                                         } else if (s == X_("IsEnd")) {
1151                                                                 session_range_location->set_end (loc->start(), true);
1152                                                                 add = false;
1153                                                         }
1154
1155                                                         if (c == string::npos) {
1156                                                                 break;
1157                                                         }
1158
1159                                                         v = v.substr (c + 1);
1160                                                 }
1161                                         }
1162                                 }
1163
1164                                 if (add) {
1165                                         new_locations.push_back (loc);
1166                                 }
1167                         }
1168
1169                         catch (failed_constructor& err) {
1170                                 error << _("could not load location from session file - ignored") << endmsg;
1171                         }
1172                 }
1173
1174                 /* We may have some unused locations in the old list. */
1175                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
1176                         LocationList::iterator tmp = i;
1177                         ++tmp;
1178
1179                         LocationList::iterator n = new_locations.begin();
1180                         bool found = false;
1181
1182                         while (n != new_locations.end ()) {
1183                                 if ((*i)->id() == (*n)->id()) {
1184                                         found = true;
1185                                         break;
1186                                 }
1187                                 ++n;
1188                         }
1189
1190                         if (!found) {
1191                                 delete *i;
1192                                 locations.erase (i);
1193                         }
1194
1195                         i = tmp;
1196                 }
1197
1198                 locations = new_locations;
1199
1200                 if (locations.size()) {
1201                         current_location = locations.front();
1202                 } else {
1203                         current_location = 0;
1204                 }
1205         }
1206
1207         changed (); /* EMIT SIGNAL */
1208
1209         return 0;
1210 }
1211
1212
1213 typedef std::pair<samplepos_t,Location*> LocationPair;
1214
1215 struct LocationStartEarlierComparison
1216 {
1217         bool operator() (LocationPair a, LocationPair b) {
1218                 return a.first < b.first;
1219         }
1220 };
1221
1222 struct LocationStartLaterComparison
1223 {
1224         bool operator() (LocationPair a, LocationPair b) {
1225                 return a.first > b.first;
1226         }
1227 };
1228
1229 samplepos_t
1230 Locations::first_mark_before (samplepos_t sample, bool include_special_ranges)
1231 {
1232         Glib::Threads::Mutex::Lock lm (lock);
1233         vector<LocationPair> locs;
1234
1235         for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1236                 locs.push_back (make_pair ((*i)->start(), (*i)));
1237                 if (!(*i)->is_mark()) {
1238                         locs.push_back (make_pair ((*i)->end(), (*i)));
1239                 }
1240         }
1241
1242         LocationStartLaterComparison cmp;
1243         sort (locs.begin(), locs.end(), cmp);
1244
1245         /* locs is sorted in ascending order */
1246
1247         for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1248                 if ((*i).second->is_hidden()) {
1249                         continue;
1250                 }
1251                 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1252                         continue;
1253                 }
1254                 if ((*i).first < sample) {
1255                         return (*i).first;
1256                 }
1257         }
1258
1259         return -1;
1260 }
1261
1262 Location*
1263 Locations::mark_at (samplepos_t pos, samplecnt_t slop) const
1264 {
1265         Glib::Threads::Mutex::Lock lm (lock);
1266         Location* closest = 0;
1267         sampleoffset_t mindelta = max_samplepos;
1268         sampleoffset_t delta;
1269
1270         /* locations are not necessarily stored in linear time order so we have
1271          * to iterate across all of them to find the one closest to a give point.
1272          */
1273
1274         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1275
1276                 if ((*i)->is_mark()) {
1277                         if (pos > (*i)->start()) {
1278                                 delta = pos - (*i)->start();
1279                         } else {
1280                                 delta = (*i)->start() - pos;
1281                         }
1282
1283                         if (slop == 0 && delta == 0) {
1284                                 /* special case: no slop, and direct hit for position */
1285                                 return *i;
1286                         }
1287
1288                         if (delta <= slop) {
1289                                 if (delta < mindelta) {
1290                                         closest = *i;
1291                                         mindelta = delta;
1292                                 }
1293                         }
1294                 }
1295         }
1296
1297         return closest;
1298 }
1299
1300 samplepos_t
1301 Locations::first_mark_after (samplepos_t sample, bool include_special_ranges)
1302 {
1303         Glib::Threads::Mutex::Lock lm (lock);
1304         vector<LocationPair> locs;
1305
1306         for (LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
1307                 locs.push_back (make_pair ((*i)->start(), (*i)));
1308                 if (!(*i)->is_mark()) {
1309                         locs.push_back (make_pair ((*i)->end(), (*i)));
1310                 }
1311         }
1312
1313         LocationStartEarlierComparison cmp;
1314         sort (locs.begin(), locs.end(), cmp);
1315
1316         /* locs is sorted in reverse order */
1317
1318         for (vector<LocationPair>::iterator i = locs.begin(); i != locs.end(); ++i) {
1319                 if ((*i).second->is_hidden()) {
1320                         continue;
1321                 }
1322                 if (!include_special_ranges && ((*i).second->is_auto_loop() || (*i).second->is_auto_punch())) {
1323                         continue;
1324                 }
1325                 if ((*i).first > sample) {
1326                         return (*i).first;
1327                 }
1328         }
1329
1330         return -1;
1331 }
1332
1333 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
1334  *  side of a sample.  Note that if sample is exactly on a `mark', that mark will not be considered for returning
1335  *  as before/after.
1336  *  @param sample Frame to look for.
1337  *  @param before Filled in with the position of the last `mark' before `sample' (or max_samplepos if none exists)
1338  *  @param after Filled in with the position of the next `mark' after `sample' (or max_samplepos if none exists)
1339  */
1340 void
1341 Locations::marks_either_side (samplepos_t const sample, samplepos_t& before, samplepos_t& after) const
1342 {
1343         before = after = max_samplepos;
1344
1345         LocationList locs;
1346
1347         {
1348                 Glib::Threads::Mutex::Lock lm (lock);
1349                 locs = locations;
1350         }
1351
1352         /* Get a list of positions; don't store any that are exactly on our requested position */
1353
1354         std::list<samplepos_t> positions;
1355
1356         for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
1357                 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
1358                         continue;
1359                 }
1360
1361                 if (!(*i)->is_hidden()) {
1362                         if ((*i)->is_mark ()) {
1363                                 if ((*i)->start() != sample) {
1364                                         positions.push_back ((*i)->start ());
1365                                 }
1366                         } else {
1367                                 if ((*i)->start() != sample) {
1368                                         positions.push_back ((*i)->start ());
1369                                 }
1370                                 if ((*i)->end() != sample) {
1371                                         positions.push_back ((*i)->end ());
1372                                 }
1373                         }
1374                 }
1375         }
1376
1377         if (positions.empty ()) {
1378                 return;
1379         }
1380
1381         positions.sort ();
1382
1383         std::list<samplepos_t>::iterator i = positions.begin ();
1384         while (i != positions.end () && *i < sample) {
1385                 ++i;
1386         }
1387
1388         if (i == positions.end ()) {
1389                 /* run out of marks */
1390                 before = positions.back ();
1391                 return;
1392         }
1393
1394         after = *i;
1395
1396         if (i == positions.begin ()) {
1397                 /* none before */
1398                 return;
1399         }
1400
1401         --i;
1402         before = *i;
1403 }
1404
1405 Location*
1406 Locations::session_range_location () const
1407 {
1408         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1409                 if ((*i)->is_session_range()) {
1410                         return const_cast<Location*> (*i);
1411                 }
1412         }
1413         return 0;
1414 }
1415
1416 Location*
1417 Locations::auto_loop_location () const
1418 {
1419         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1420                 if ((*i)->is_auto_loop()) {
1421                         return const_cast<Location*> (*i);
1422                 }
1423         }
1424         return 0;
1425 }
1426
1427 Location*
1428 Locations::auto_punch_location () const
1429 {
1430         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1431                 if ((*i)->is_auto_punch()) {
1432                         return const_cast<Location*> (*i);
1433                 }
1434         }
1435         return 0;
1436 }
1437
1438 Location*
1439 Locations::clock_origin_location () const
1440 {
1441         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1442                 if ((*i)->is_clock_origin()) {
1443                         return const_cast<Location*> (*i);
1444                 }
1445         }
1446         return session_range_location ();
1447 }
1448
1449 uint32_t
1450 Locations::num_range_markers () const
1451 {
1452         uint32_t cnt = 0;
1453         Glib::Threads::Mutex::Lock lm (lock);
1454         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1455                 if ((*i)->is_range_marker()) {
1456                         ++cnt;
1457                 }
1458         }
1459         return cnt;
1460 }
1461
1462 Location *
1463 Locations::get_location_by_id(PBD::ID id)
1464 {
1465         LocationList::iterator it;
1466         for (it  = locations.begin(); it != locations.end(); ++it)
1467                 if (id == (*it)->id())
1468                         return *it;
1469
1470         return 0;
1471 }
1472
1473 void
1474 Locations::find_all_between (samplepos_t start, samplepos_t end, LocationList& ll, Location::Flags flags)
1475 {
1476         Glib::Threads::Mutex::Lock lm (lock);
1477
1478         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
1479                 if ((flags == 0 || (*i)->matches (flags)) &&
1480                     ((*i)->start() >= start && (*i)->end() < end)) {
1481                         ll.push_back (*i);
1482                 }
1483         }
1484 }