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