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