Clean up and simplify code to find marks before and after a position, and hence impro...
[ardour.git] / libs / ardour / location.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <set>
22 #include <cstdio> /* for sprintf */
23 #include <unistd.h>
24 #include <cerrno>
25 #include <ctime>
26 #include <list>
27
28 #include <sigc++/bind.h>
29
30 #include "pbd/stl_delete.h"
31 #include "pbd/xml++.h"
32 #include "pbd/enumwriter.h"
33
34 #include "ardour/location.h"
35 #include "ardour/session.h"
36 #include "ardour/audiofilesource.h"
37
38 #include "i18n.h"
39
40 #define SUFFIX_MAX 32
41
42 using namespace std;
43 using namespace ARDOUR;
44 using namespace sigc;
45 using namespace PBD;
46
47 Location::Location (const Location& other)
48         : StatefulDestructible(),
49           _name (other._name),
50           _start (other._start),
51           _end (other._end),
52           _flags (other._flags)
53 {
54         /* start and end flags can never be copied, because there can only ever be one of each */
55
56         _flags = Flags (_flags & ~IsStart);
57         _flags = Flags (_flags & ~IsEnd);
58
59         /* copy is not locked even if original was */
60
61         _locked = false;
62 }
63
64 Location::Location (const XMLNode& node)
65 {
66         if (set_state (node, Stateful::loading_state_version)) {
67                 throw failed_constructor ();
68         }
69 }
70
71 Location*
72 Location::operator= (const Location& other)
73 {
74         if (this == &other) {
75                 return this;
76         }
77
78         _name = other._name;
79         _start = other._start;
80         _end = other._end;
81         _flags = other._flags;
82
83         /* copy is not locked even if original was */
84
85         _locked = false;
86
87         /* "changed" not emitted on purpose */
88
89         return this;
90 }
91
92 int
93 Location::set_start (nframes64_t s)
94 {
95         if (_locked) {
96                 return -1;
97         }
98
99         if (is_mark()) {
100                 if (_start != s) {
101
102                         _start = s;
103                         _end = s;
104
105                         start_changed(this); /* EMIT SIGNAL */
106                         end_changed(this); /* EMIT SIGNAL */
107
108                         if ( is_start() ) {
109
110                                 Session::StartTimeChanged (); /* EMIT SIGNAL */
111                                 AudioFileSource::set_header_position_offset ( s );
112                         }
113
114                         if ( is_end() ) {
115                                 Session::EndTimeChanged (); /* EMIT SIGNAL */
116                         }
117                 }
118                 return 0;
119         }
120
121         if (((is_auto_punch() || is_auto_loop()) && s >= _end) || s > _end) {
122                 return -1;
123         }
124
125         if (s != _start) {
126                 _start = s;
127                 start_changed(this); /* EMIT SIGNAL */
128         }
129
130         return 0;
131 }
132
133 int
134 Location::set_end (nframes64_t e)
135 {
136         if (_locked) {
137                 return -1;
138         }
139
140         if (is_mark()) {
141                 if (_start != e) {
142                         _start = e;
143                         _end = e;
144                         start_changed(this); /* EMIT SIGNAL */
145                         end_changed(this); /* EMIT SIGNAL */
146
147                         if ( is_start() ) {
148                                 Session::StartTimeChanged (); /* EMIT SIGNAL */
149                         }
150
151                         if ( is_end() ) {
152                                 Session::EndTimeChanged (); /* EMIT SIGNAL */
153                         }
154
155                 }
156                 return 0;
157         }
158
159         if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
160                 return -1;
161         }
162
163         if (e != _end) {
164                 _end = e;
165                  end_changed(this); /* EMIT SIGNAL */
166         }
167         return 0;
168 }
169
170 int
171 Location::set (nframes64_t start, nframes64_t end)
172 {
173         if (_locked) {
174                 return -1;
175         }
176
177         if (is_mark() && start != end) {
178                 return -1;
179         } else if (((is_auto_punch() || is_auto_loop()) && start >= end) || (start > end)) {
180                 return -1;
181         }
182
183         if (_start != start) {
184                 _start = start;
185                 start_changed(this); /* EMIT SIGNAL */
186         }
187
188         if (_end != end) {
189                 _end = end;
190                 end_changed(this); /* EMIT SIGNAL */
191         }
192         return 0;
193 }
194
195 int
196 Location::move_to (nframes64_t pos)
197 {
198         if (_locked) {
199                 return -1;
200         }
201
202         if (_start != pos) {
203                 _start = pos;
204                 _end = _start + length();
205
206                 changed (this); /* EMIT SIGNAL */
207         }
208
209         return 0;
210 }
211
212 void
213 Location::set_hidden (bool yn, void *src)
214 {
215         if (set_flag_internal (yn, IsHidden)) {
216                  FlagsChanged (this, src); /* EMIT SIGNAL */
217         }
218 }
219
220 void
221 Location::set_cd (bool yn, void *src)
222 {
223         // XXX this really needs to be session start
224         // but its not available here - leave to GUI
225
226         if (_start == 0) {
227                 error << _("You cannot put a CD marker at this position") << endmsg;
228                 return;
229         }
230
231         if (set_flag_internal (yn, IsCDMarker)) {
232                  FlagsChanged (this, src); /* EMIT SIGNAL */
233         }
234 }
235
236 void
237 Location::set_is_end (bool yn, void *src)
238 {
239         if (set_flag_internal (yn, IsEnd)) {
240                  FlagsChanged (this, src); /* EMIT SIGNAL */
241         }
242 }
243
244 void
245 Location::set_is_start (bool yn, void *src)
246 {
247         if (set_flag_internal (yn, IsStart)) {
248                  FlagsChanged (this, src); /* EMIT SIGNAL */
249         }
250 }
251
252 void
253 Location::set_is_range_marker (bool yn, void *src)
254 {
255        if (set_flag_internal (yn, IsRangeMarker)) {
256                 FlagsChanged (this, src); /* EMIT SIGNAL */
257        }
258 }
259
260 void
261 Location::set_auto_punch (bool yn, void *src)
262 {
263         if (is_mark() || _start == _end) {
264                 return;
265         }
266
267         if (set_flag_internal (yn, IsAutoPunch)) {
268                  FlagsChanged (this, src); /* EMIT SIGNAL */
269         }
270 }
271
272 void
273 Location::set_auto_loop (bool yn, void *src)
274 {
275         if (is_mark() || _start == _end) {
276                 return;
277         }
278
279         if (set_flag_internal (yn, IsAutoLoop)) {
280                  FlagsChanged (this, src); /* EMIT SIGNAL */
281         }
282 }
283
284 bool
285 Location::set_flag_internal (bool yn, Flags flag)
286 {
287         if (yn) {
288                 if (!(_flags & flag)) {
289                         _flags = Flags (_flags | flag);
290                         return true;
291                 }
292         } else {
293                 if (_flags & flag) {
294                         _flags = Flags (_flags & ~flag);
295                         return true;
296                 }
297         }
298         return false;
299 }
300
301 void
302 Location::set_mark (bool yn)
303 {
304         /* This function is private, and so does not emit signals */
305
306         if (_start != _end) {
307                 return;
308         }
309
310         set_flag_internal (yn, IsMark);
311 }
312
313
314 XMLNode&
315 Location::cd_info_node(const string & name, const string & value)
316 {
317         XMLNode* root = new XMLNode("CD-Info");
318
319         root->add_property("name", name);
320         root->add_property("value", value);
321
322         return *root;
323 }
324
325
326 XMLNode&
327 Location::get_state (void)
328 {
329         XMLNode *node = new XMLNode ("Location");
330         char buf[64];
331
332         typedef map<string, string>::const_iterator CI;
333
334         for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
335                 node->add_child_nocopy(cd_info_node(m->first, m->second));
336         }
337
338         id().print (buf, sizeof (buf));
339         node->add_property("id", buf);
340         node->add_property ("name", name());
341         snprintf (buf, sizeof (buf), "%" PRId64, start());
342         node->add_property ("start", buf);
343         snprintf (buf, sizeof (buf), "%" PRId64, end());
344         node->add_property ("end", buf);
345         node->add_property ("flags", enum_2_string (_flags));
346         node->add_property ("locked", (_locked ? "yes" : "no"));
347
348         return *node;
349 }
350
351 int
352 Location::set_state (const XMLNode& node, int /*version*/)
353 {
354         const XMLProperty *prop;
355
356         XMLNodeList cd_list = node.children();
357         XMLNodeConstIterator cd_iter;
358         XMLNode *cd_node;
359
360         string cd_name;
361         string cd_value;
362
363         if (node.name() != "Location") {
364                 error << _("incorrect XML node passed to Location::set_state") << endmsg;
365                 return -1;
366         }
367
368         if ((prop = node.property ("id")) == 0) {
369                 warning << _("XML node for Location has no ID information") << endmsg;
370         } else {
371                 _id = prop->value ();
372         }
373
374         if ((prop = node.property ("name")) == 0) {
375                 error << _("XML node for Location has no name information") << endmsg;
376                 return -1;
377         }
378
379         set_name (prop->value());
380
381         if ((prop = node.property ("start")) == 0) {
382                 error << _("XML node for Location has no start information") << endmsg;
383                 return -1;
384         }
385
386                 /* can't use set_start() here, because _end
387                    may make the value of _start illegal.
388                 */
389
390         sscanf (prop->value().c_str(), "%" PRId64, &_start);
391
392         if ((prop = node.property ("end")) == 0) {
393                   error << _("XML node for Location has no end information") << endmsg;
394                   return -1;
395         }
396
397         sscanf (prop->value().c_str(), "%" PRId64, &_end);
398
399         if ((prop = node.property ("flags")) == 0) {
400                   error << _("XML node for Location has no flags information") << endmsg;
401                   return -1;
402         }
403
404         _flags = Flags (string_2_enum (prop->value(), _flags));
405
406         if ((prop = node.property ("locked")) != 0) {
407                 _locked = string_is_affirmative (prop->value());
408         } else {
409                 _locked = false;
410         }
411
412         for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
413
414                   cd_node = *cd_iter;
415
416                   if (cd_node->name() != "CD-Info") {
417                     continue;
418                   }
419
420                   if ((prop = cd_node->property ("name")) != 0) {
421                     cd_name = prop->value();
422                   } else {
423                     throw failed_constructor ();
424                   }
425
426                   if ((prop = cd_node->property ("value")) != 0) {
427                     cd_value = prop->value();
428                   } else {
429                     throw failed_constructor ();
430                   }
431
432
433                   cd_info[cd_name] = cd_value;
434
435         }
436
437         changed(this); /* EMIT SIGNAL */
438
439         return 0;
440 }
441
442 /*---------------------------------------------------------------------- */
443
444 Locations::Locations ()
445
446 {
447         current_location = 0;
448 }
449
450 Locations::~Locations ()
451 {
452         for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
453                 LocationList::iterator tmp = i;
454                 ++tmp;
455                 delete *i;
456                 i = tmp;
457         }
458 }
459
460 int
461 Locations::set_current (Location *loc, bool want_lock)
462
463 {
464         int ret;
465
466         if (want_lock) {
467                 Glib::Mutex::Lock lm (lock);
468                 ret = set_current_unlocked (loc);
469         } else {
470                 ret = set_current_unlocked (loc);
471         }
472
473         if (ret == 0) {
474                  current_changed (current_location); /* EMIT SIGNAL */
475         }
476         return ret;
477 }
478
479 int
480 Locations::next_available_name(string& result,string base)
481 {
482         LocationList::iterator i;
483         Location* location;
484         string temp;
485         string::size_type l;
486         int suffix;
487         char buf[32];
488         bool available[SUFFIX_MAX+1];
489
490         result = base;
491         for (int k=1; k<SUFFIX_MAX; k++) {
492                 available[k] = true;
493         }
494         l = base.length();
495         for (i = locations.begin(); i != locations.end(); ++i) {
496                 location =* i;
497                 temp = location->name();
498                 if (l && !temp.find(base,0)) {
499                         suffix = atoi(temp.substr(l,3).c_str());
500                         if (suffix) available[suffix] = false;
501                 }
502         }
503         for (int k=1; k<=SUFFIX_MAX; k++) {
504                 if (available[k]) {
505                         snprintf (buf, 31, "%d", k);
506                         result += buf;
507                         return 1;
508                 }
509         }
510         return 0;
511 }
512
513 int
514 Locations::set_current_unlocked (Location *loc)
515 {
516         if (find (locations.begin(), locations.end(), loc) == locations.end()) {
517                 error << _("Locations: attempt to use unknown location as selected location") << endmsg;
518                 return -1;
519         }
520
521         current_location = loc;
522         return 0;
523 }
524
525 void
526 Locations::clear ()
527 {
528         {
529                 Glib::Mutex::Lock lm (lock);
530
531                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
532
533                         LocationList::iterator tmp = i;
534                         ++tmp;
535
536                         if (!(*i)->is_end() && !(*i)->is_start()) {
537                                 locations.erase (i);
538                         }
539
540                         i = tmp;
541                 }
542
543                 current_location = 0;
544         }
545
546         changed (); /* EMIT SIGNAL */
547         current_changed (0); /* EMIT SIGNAL */
548 }
549
550 void
551 Locations::clear_markers ()
552 {
553         {
554                 Glib::Mutex::Lock lm (lock);
555                 LocationList::iterator tmp;
556
557                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
558                         tmp = i;
559                         ++tmp;
560
561                         if ((*i)->is_mark() && !(*i)->is_end() && !(*i)->is_start()) {
562                                 locations.erase (i);
563                         }
564
565                         i = tmp;
566                 }
567         }
568
569         changed (); /* EMIT SIGNAL */
570 }
571
572 void
573 Locations::clear_ranges ()
574 {
575         {
576                 Glib::Mutex::Lock lm (lock);
577                 LocationList::iterator tmp;
578
579                 for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
580
581                         tmp = i;
582                         ++tmp;
583
584                         if (!(*i)->is_mark()) {
585                                 locations.erase (i);
586
587                         }
588
589                         i = tmp;
590                 }
591
592                 current_location = 0;
593         }
594
595         changed (); /* EMIT SIGNAL */
596         current_changed (0); /* EMIT SIGNAL */
597 }
598
599 void
600 Locations::add (Location *loc, bool make_current)
601 {
602         {
603                 Glib::Mutex::Lock lm (lock);
604                 locations.push_back (loc);
605
606                 if (make_current) {
607                         current_location = loc;
608                 }
609         }
610
611         added (loc); /* EMIT SIGNAL */
612
613         if (make_current) {
614                  current_changed (current_location); /* EMIT SIGNAL */
615         }
616 }
617
618 void
619 Locations::remove (Location *loc)
620
621 {
622         bool was_removed = false;
623         bool was_current = false;
624         LocationList::iterator i;
625
626         if (loc->is_end() || loc->is_start()) {
627                 return;
628         }
629
630         {
631                 Glib::Mutex::Lock lm (lock);
632
633                 for (i = locations.begin(); i != locations.end(); ++i) {
634                         if ((*i) == loc) {
635                                 locations.erase (i);
636                                 was_removed = true;
637                                 if (current_location == loc) {
638                                         current_location = 0;
639                                         was_current = true;
640                                 }
641                                 break;
642                         }
643                 }
644         }
645
646         if (was_removed) {
647
648                 removed (loc); /* EMIT SIGNAL */
649
650                 if (was_current) {
651                          current_changed (0); /* EMIT SIGNAL */
652                 }
653
654                 changed (); /* EMIT_SIGNAL */
655         }
656 }
657
658 void
659 Locations::location_changed (Location* /*loc*/)
660 {
661         changed (); /* EMIT SIGNAL */
662 }
663
664 XMLNode&
665 Locations::get_state ()
666 {
667         XMLNode *node = new XMLNode ("Locations");
668         LocationList::iterator iter;
669         Glib::Mutex::Lock lm (lock);
670
671         for (iter  = locations.begin(); iter != locations.end(); ++iter) {
672                 node->add_child_nocopy ((*iter)->get_state ());
673         }
674
675         return *node;
676 }
677
678 int
679 Locations::set_state (const XMLNode& node, int /*version*/)
680 {
681         XMLNodeList nlist;
682         XMLNodeConstIterator niter;
683
684         if (node.name() != "Locations") {
685                 error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
686                 return -1;
687         }
688
689         nlist = node.children();
690
691         locations.clear ();
692         current_location = 0;
693
694         {
695                 Glib::Mutex::Lock lm (lock);
696
697                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
698
699                         try {
700
701                                 Location *loc = new Location (**niter);
702                                 locations.push_back (loc);
703                         }
704
705                         catch (failed_constructor& err) {
706                                 error << _("could not load location from session file - ignored") << endmsg;
707                         }
708                 }
709
710                 if (locations.size()) {
711
712                         current_location = locations.front();
713                 } else {
714                         current_location = 0;
715                 }
716         }
717
718         changed (); /* EMIT SIGNAL */
719
720         return 0;
721 }
722
723 struct LocationStartEarlierComparison
724 {
725     bool operator() (Location *a, Location *b) {
726         return a->start() < b->start();
727     }
728 };
729
730 struct LocationStartLaterComparison
731 {
732     bool operator() (Location *a, Location *b) {
733         return a->start() > b->start();
734     }
735 };
736
737 Location *
738 Locations::first_location_before (nframes64_t frame, bool include_special_ranges)
739 {
740         LocationList locs;
741
742         {
743                 Glib::Mutex::Lock lm (lock);
744                 locs = locations;
745         }
746
747         LocationStartLaterComparison cmp;
748         locs.sort (cmp);
749
750         /* locs is now sorted latest..earliest */
751
752         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
753                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
754                         continue;
755                 }
756                 if (!(*i)->is_hidden() && (*i)->start() < frame) {
757                         return (*i);
758                 }
759         }
760
761         return 0;
762 }
763
764 Location *
765 Locations::first_location_after (nframes64_t frame, bool include_special_ranges)
766 {
767         LocationList locs;
768
769         {
770                 Glib::Mutex::Lock lm (lock);
771                 locs = locations;
772         }
773
774         LocationStartEarlierComparison cmp;
775         locs.sort (cmp);
776
777         /* locs is now sorted earliest..latest */
778
779         for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
780                 if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
781                         continue;
782                 }
783                 if (!(*i)->is_hidden() && (*i)->start() > frame) {
784                         return (*i);
785                 }
786         }
787
788         return 0;
789 }
790
791 /** Look for the `marks' (either locations which are marks, or start/end points of range markers) either
792  *  side of a frame.
793  *  @param frame Frame to look for.
794  *  @param before Filled in with the position of the last `mark' before `frame' (or max_frames if none exists)
795  *  @param after Filled in with the position of the last `mark' after `frame' (or max_frames if none exists)
796  */
797 void
798 Locations::marks_either_side (nframes64_t const frame, nframes64_t& before, nframes64_t& after) const
799 {
800         before = after = max_frames;
801         
802         LocationList locs;
803
804         {
805                 Glib::Mutex::Lock lm (lock);
806                 locs = locations;
807         }
808
809         std::list<nframes64_t> positions;
810
811         for (LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
812                 if (((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
813                         continue;
814                 }
815
816                 if (!(*i)->is_hidden()) {
817                         if ((*i)->is_mark ()) {
818                                 positions.push_back ((*i)->start ());
819                         } else {
820                                 positions.push_back ((*i)->start ());
821                                 positions.push_back ((*i)->end ());
822                         }
823                 }
824         }
825
826         if (positions.empty ()) {
827                 return;
828         }
829
830         positions.sort ();
831
832         std::list<nframes64_t>::iterator i = positions.begin ();
833         while (i != positions.end () && *i < frame) {
834                 ++i;
835         }
836
837         if (i == positions.end ()) {
838                 /* run out of marks */
839                 before = positions.back ();
840                 return;
841         }
842
843         after = *i;
844
845         if (i == positions.begin ()) {
846                 /* none before */
847                 return;
848         }
849         
850         --i;
851         before = *i;
852 }
853
854 Location*
855 Locations::end_location () const
856 {
857         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
858                 if ((*i)->is_end()) {
859                         return const_cast<Location*> (*i);
860                 }
861         }
862         return 0;
863 }
864
865 Location*
866 Locations::start_location () const
867 {
868         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
869                 if ((*i)->is_start()) {
870                         return const_cast<Location*> (*i);
871                 }
872         }
873         return 0;
874 }
875
876 Location*
877 Locations::auto_loop_location () const
878 {
879         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
880                 if ((*i)->is_auto_loop()) {
881                         return const_cast<Location*> (*i);
882                 }
883         }
884         return 0;
885 }
886
887 Location*
888 Locations::auto_punch_location () const
889 {
890         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
891                 if ((*i)->is_auto_punch()) {
892                         return const_cast<Location*> (*i);
893                 }
894         }
895        return 0;
896 }
897
898 uint32_t
899 Locations::num_range_markers () const
900 {
901         uint32_t cnt = 0;
902         Glib::Mutex::Lock lm (lock);
903         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
904                 if ((*i)->is_range_marker()) {
905                         ++cnt;
906                 }
907         }
908         return cnt;
909 }
910
911 Location *
912 Locations::get_location_by_id(PBD::ID id)
913 {
914     LocationList::iterator it;
915     for (it  = locations.begin(); it != locations.end(); it++)
916         if (id == (*it)->id())
917             return *it;
918
919     return 0;
920 }
921
922 void
923 Locations::find_all_between (nframes64_t start, nframes64_t end, LocationList& ll, Location::Flags flags)
924 {
925         Glib::Mutex::Lock lm (lock);
926
927         for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
928                 if ((flags == 0 || (*i)->matches (flags)) &&
929                     ((*i)->start() >= start && (*i)->end() < end)) {
930                         ll.push_back (*i);
931                 }
932         }
933 }