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