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