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