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