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