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