merged with 1697 revision of trunk (which is post-rc1 but pre-rc2
[ardour.git] / libs / ardour / region.cc
1 /*
2     Copyright (C) 2000-2003 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 <iostream>
21 #include <cmath>
22 #include <climits>
23 #include <algorithm>
24
25 #include <sigc++/bind.h>
26 #include <sigc++/class_slot.h>
27
28 #include <glibmm/thread.h>
29 #include <pbd/xml++.h>
30 #include <pbd/stacktrace.h>
31
32 #include <ardour/region.h>
33 #include <ardour/playlist.h>
34 #include <ardour/session.h>
35 #include <ardour/region_factory.h>
36
37 #include "i18n.h"
38
39 using namespace std;
40 using namespace ARDOUR;
41 using namespace PBD;
42
43 Change Region::FadeChanged       = ARDOUR::new_change ();
44 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
45 Change Region::MuteChanged       = ARDOUR::new_change ();
46 Change Region::OpacityChanged    = ARDOUR::new_change ();
47 Change Region::LockChanged       = ARDOUR::new_change ();
48 Change Region::LayerChanged      = ARDOUR::new_change ();
49 Change Region::HiddenChanged     = ARDOUR::new_change ();
50
51 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
52 {
53         /* basic Region constructor */
54
55         _flags = flags;
56         _read_data_count = 0;
57         _frozen = 0;
58         pending_changed = Change (0);
59
60         _name = name;
61         _start = start; 
62         _sync_position = _start;
63         _length = length; 
64         _position = 0; 
65         _layer = layer;
66         _read_data_count = 0;
67         _first_edit = EditChangesNothing;
68         _last_layer_op = 0;
69 }
70
71 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
72 {
73         /* create a new Region from part of an existing one */
74
75         _frozen = 0;
76         pending_changed = Change (0);
77         _read_data_count = 0;
78
79         _start = other->_start + offset; 
80         if (other->_sync_position < offset) {
81                 _sync_position = other->_sync_position;
82         } else {
83                 _sync_position = _start;
84         }
85         _length = length; 
86         _name = name;
87         _position = 0; 
88         _layer = layer; 
89         _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
90         _first_edit = EditChangesNothing;
91         _last_layer_op = 0;
92 }
93
94 Region::Region (boost::shared_ptr<const Region> other)
95 {
96         /* Pure copy constructor */
97
98         _frozen = 0;
99         pending_changed = Change (0);
100         _read_data_count = 0;
101
102         _first_edit = EditChangesID;
103         other->_first_edit = EditChangesName;
104
105         if (other->_extra_xml) {
106                 _extra_xml = new XMLNode (*other->_extra_xml);
107         } else {
108                 _extra_xml = 0;
109         }
110
111         _start = other->_start;
112         _sync_position = other->_sync_position;
113         _length = other->_length; 
114         _name = other->_name;
115         _position = other->_position; 
116         _layer = other->_layer; 
117         _flags = Flag (other->_flags & ~Locked);
118         _last_layer_op = other->_last_layer_op;
119 }
120
121 Region::Region (const XMLNode& node)
122 {
123         _frozen = 0;
124         pending_changed = Change (0);
125         _read_data_count = 0;
126         _start = 0; 
127         _sync_position = _start;
128         _length = 0;
129         _name = X_("error: XML did not reset this");
130         _position = 0; 
131         _layer = 0;
132         _flags = Flag (0);
133         _first_edit = EditChangesNothing;
134
135         if (set_state (node)) {
136                 throw failed_constructor();
137         }
138 }
139
140 Region::~Region ()
141 {
142         /* derived classes must call notify_callbacks() and then emit GoingAway */
143 }
144
145 void
146 Region::set_playlist (boost::weak_ptr<Playlist> pl)
147 {
148         _playlist = pl;
149 }
150
151 void
152 Region::set_name (string str)
153 {
154         if (_name != str) {
155                 _name = str; 
156                 send_change (NameChanged);
157         }
158 }
159
160 void
161 Region::set_length (nframes_t len, void *src)
162 {
163         if (_flags & Locked) {
164                 return;
165         }
166
167         if (_length != len && len != 0) {
168
169                 /* check that the current _position wouldn't make the new 
170                    length impossible.
171                 */
172
173                 if (max_frames - len < _position) {
174                         return;
175                 }
176
177                 if (!verify_length (len)) {
178                         return;
179                 }
180                 
181                 _length = len;
182
183                 _flags = Region::Flag (_flags & ~WholeFile);
184
185                 first_edit ();
186                 maybe_uncopy ();
187
188                 if (!_frozen) {
189                         recompute_at_end ();
190                 }
191
192                 send_change (LengthChanged);
193         }
194 }
195
196 void
197 Region::maybe_uncopy ()
198 {
199 }
200
201 void
202 Region::first_edit ()
203 {
204         boost::shared_ptr<Playlist> pl (playlist());
205
206         if (_first_edit != EditChangesNothing && pl) {
207
208                 _name = pl->session().new_region_name (_name);
209                 _first_edit = EditChangesNothing;
210
211                 send_change (NameChanged);
212                 RegionFactory::CheckNewRegion (shared_from_this());
213         }
214 }
215
216 bool
217 Region::at_natural_position () const
218 {
219         boost::shared_ptr<Playlist> pl (playlist());
220
221         if (!pl) {
222                 return false;
223         }
224         
225         boost::shared_ptr<Region> whole_file_region = get_parent();
226
227         if (whole_file_region) {
228                 if (_position == whole_file_region->position() + _start) {
229                         return true;
230                 }
231         }
232
233         return false;
234 }
235
236 void
237 Region::move_to_natural_position (void *src)
238 {
239         boost::shared_ptr<Playlist> pl (playlist());
240
241         if (!pl) {
242                 return;
243         }
244         
245         boost::shared_ptr<Region> whole_file_region = get_parent();
246
247         if (whole_file_region) {
248                 set_position (whole_file_region->position() + _start, src);
249         }
250 }
251         
252 void
253 Region::special_set_position (nframes_t pos)
254 {
255         /* this is used when creating a whole file region as 
256            a way to store its "natural" or "captured" position.
257         */
258
259         _position = pos;
260 }
261
262 void
263 Region::set_position (nframes_t pos, void *src)
264 {
265         if (_flags & Locked) {
266                 return;
267         }
268
269         if (_position != pos) {
270                 _position = pos;
271
272                 /* check that the new _position wouldn't make the current
273                    length impossible - if so, change the length. 
274
275                    XXX is this the right thing to do?
276                 */
277
278                 if (max_frames - _length < _position) {
279                         _length = max_frames - _position;
280                 }
281         }
282
283         /* do this even if the position is the same. this helps out
284            a GUI that has moved its representation already.
285         */
286
287         send_change (PositionChanged);
288 }
289
290 void
291 Region::set_position_on_top (nframes_t pos, void *src)
292 {
293         if (_flags & Locked) {
294                 return;
295         }
296
297         if (_position != pos) {
298                 _position = pos;
299         }
300
301         boost::shared_ptr<Playlist> pl (playlist());
302
303         if (pl) {
304                 pl->raise_region_to_top (shared_from_this ());
305         }
306
307         /* do this even if the position is the same. this helps out
308            a GUI that has moved its representation already.
309         */
310         
311         send_change (PositionChanged);
312 }
313
314 void
315 Region::nudge_position (long n, void *src)
316 {
317         if (_flags & Locked) {
318                 return;
319         }
320
321         if (n == 0) {
322                 return;
323         }
324         
325         if (n > 0) {
326                 if (_position > max_frames - n) {
327                         _position = max_frames;
328                 } else {
329                         _position += n;
330                 }
331         } else {
332                 if (_position < (nframes_t) -n) {
333                         _position = 0;
334                 } else {
335                         _position += n;
336                 }
337         }
338
339         send_change (PositionChanged);
340 }
341
342 void
343 Region::set_start (nframes_t pos, void *src)
344 {
345         if (_flags & Locked) {
346                 return;
347         }
348         /* This just sets the start, nothing else. It effectively shifts
349            the contents of the Region within the overall extent of the Source,
350            without changing the Region's position or length
351         */
352
353         if (_start != pos) {
354
355                 if (!verify_start (pos)) {
356                         return;
357                 }
358
359                 _start = pos;
360                 _flags = Region::Flag (_flags & ~WholeFile);
361                 first_edit ();
362
363                 send_change (StartChanged);
364         }
365 }
366
367 void
368 Region::trim_start (nframes_t new_position, void *src)
369 {
370         if (_flags & Locked) {
371                 return;
372         }
373         nframes_t new_start;
374         int32_t start_shift;
375         
376         if (new_position > _position) {
377                 start_shift = new_position - _position;
378         } else {
379                 start_shift = -(_position - new_position);
380         }
381
382         if (start_shift > 0) {
383
384                 if (_start > max_frames - start_shift) {
385                         new_start = max_frames;
386                 } else {
387                         new_start = _start + start_shift;
388                 }
389
390                 if (!verify_start (new_start)) {
391                         return;
392                 }
393
394         } else if (start_shift < 0) {
395
396                 if (_start < (nframes_t) -start_shift) {
397                         new_start = 0;
398                 } else {
399                         new_start = _start + start_shift;
400                 }
401         } else {
402                 return;
403         }
404
405         if (new_start == _start) {
406                 return;
407         }
408         
409         _start = new_start;
410         _flags = Region::Flag (_flags & ~WholeFile);
411         first_edit ();
412
413         send_change (StartChanged);
414 }
415
416 void
417 Region::trim_front (nframes_t new_position, void *src)
418 {
419         if (_flags & Locked) {
420                 return;
421         }
422
423         nframes_t end = last_frame();
424         nframes_t source_zero;
425
426         if (_position > _start) {
427                 source_zero = _position - _start;
428         } else {
429                 source_zero = 0; // its actually negative, but this will work for us
430         }
431
432         if (new_position < end) { /* can't trim it zero or negative length */
433                 
434                 nframes_t newlen;
435
436                 /* can't trim it back passed where source position zero is located */
437                 
438                 new_position = max (new_position, source_zero);
439                 
440                 
441                 if (new_position > _position) {
442                         newlen = _length - (new_position - _position);
443                 } else {
444                         newlen = _length + (_position - new_position);
445                 }
446                 
447                 trim_to_internal (new_position, newlen, src);
448                 if (!_frozen) {
449                         recompute_at_start ();
450                 }
451         }
452 }
453
454 void
455 Region::trim_end (nframes_t new_endpoint, void *src)
456 {
457         if (_flags & Locked) {
458                 return;
459         }
460
461         if (new_endpoint > _position) {
462                 trim_to_internal (_position, new_endpoint - _position, this);
463                 if (!_frozen) {
464                         recompute_at_end ();
465                 }
466         }
467 }
468
469 void
470 Region::trim_to (nframes_t position, nframes_t length, void *src)
471 {
472         if (_flags & Locked) {
473                 return;
474         }
475
476         trim_to_internal (position, length, src);
477
478         if (!_frozen) {
479                 recompute_at_start ();
480                 recompute_at_end ();
481         }
482 }
483
484 void
485 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
486 {
487         int32_t start_shift;
488         nframes_t new_start;
489
490         if (_flags & Locked) {
491                 return;
492         }
493
494         if (position > _position) {
495                 start_shift = position - _position;
496         } else {
497                 start_shift = -(_position - position);
498         }
499
500         if (start_shift > 0) {
501
502                 if (_start > max_frames - start_shift) {
503                         new_start = max_frames;
504                 } else {
505                         new_start = _start + start_shift;
506                 }
507
508
509         } else if (start_shift < 0) {
510
511                 if (_start < (nframes_t) -start_shift) {
512                         new_start = 0;
513                 } else {
514                         new_start = _start + start_shift;
515                 }
516         } else {
517                 new_start = _start;
518         }
519
520         if (!verify_start_and_length (new_start, length)) {
521                 return;
522         }
523
524         Change what_changed = Change (0);
525
526         if (_start != new_start) {
527                 _start = new_start;
528                 what_changed = Change (what_changed|StartChanged);
529         }
530         if (_length != length) {
531                 _length = length;
532                 what_changed = Change (what_changed|LengthChanged);
533         }
534         if (_position != position) {
535                 _position = position;
536                 what_changed = Change (what_changed|PositionChanged);
537         }
538         
539         _flags = Region::Flag (_flags & ~WholeFile);
540
541         if (what_changed & (StartChanged|LengthChanged)) {
542                 first_edit ();
543         } 
544
545         if (what_changed) {
546                 send_change (what_changed);
547         }
548 }       
549
550 void
551 Region::set_hidden (bool yn)
552 {
553         if (hidden() != yn) {
554
555                 if (yn) {
556                         _flags = Flag (_flags|Hidden);
557                 } else {
558                         _flags = Flag (_flags & ~Hidden);
559                 }
560
561                 send_change (HiddenChanged);
562         }
563 }
564
565 void
566 Region::set_muted (bool yn)
567 {
568         if (muted() != yn) {
569
570                 if (yn) {
571                         _flags = Flag (_flags|Muted);
572                 } else {
573                         _flags = Flag (_flags & ~Muted);
574                 }
575
576                 send_change (MuteChanged);
577         }
578 }
579
580 void
581 Region::set_opaque (bool yn)
582 {
583         if (opaque() != yn) {
584                 if (yn) {
585                         _flags = Flag (_flags|Opaque);
586                 } else {
587                         _flags = Flag (_flags & ~Opaque);
588                 }
589                 send_change (OpacityChanged);
590         }
591 }
592
593 void
594 Region::set_locked (bool yn)
595 {
596         if (locked() != yn) {
597                 if (yn) {
598                         _flags = Flag (_flags|Locked);
599                 } else {
600                         _flags = Flag (_flags & ~Locked);
601                 }
602                 send_change (LockChanged);
603         }
604 }
605
606 void
607 Region::set_sync_position (nframes_t absolute_pos)
608 {
609         nframes_t file_pos;
610
611         file_pos = _start + (absolute_pos - _position);
612
613         if (file_pos != _sync_position) {
614                 
615                 _sync_position = file_pos;
616                 _flags = Flag (_flags|SyncMarked);
617
618                 if (!_frozen) {
619                         maybe_uncopy ();
620                 }
621                 send_change (SyncOffsetChanged);
622         }
623 }
624
625 void
626 Region::clear_sync_position ()
627 {
628         if (_flags & SyncMarked) {
629                 _flags = Flag (_flags & ~SyncMarked);
630
631                 if (!_frozen) {
632                         maybe_uncopy ();
633                 }
634                 send_change (SyncOffsetChanged);
635         }
636 }
637
638 nframes_t
639 Region::sync_offset (int& dir) const
640 {
641         /* returns the sync point relative the first frame of the region */
642
643         if (_flags & SyncMarked) {
644                 if (_sync_position > _start) {
645                         dir = 1;
646                         return _sync_position - _start; 
647                 } else {
648                         dir = -1;
649                         return _start - _sync_position;
650                 }
651         } else {
652                 dir = 0;
653                 return 0;
654         }
655 }
656
657 nframes_t 
658 Region::adjust_to_sync (nframes_t pos)
659 {
660         int sync_dir;
661         nframes_t offset = sync_offset (sync_dir);
662         
663         if (sync_dir > 0) {
664                 if (max_frames - pos > offset) {
665                         pos += offset;
666                 }
667         } else {
668                 if (pos > offset) {
669                         pos -= offset;
670                 } else {
671                         pos = 0;
672                 }
673         }
674
675         return pos;
676 }
677
678 nframes_t
679 Region::sync_position() const
680 {
681         if (_flags & SyncMarked) {
682                 return _sync_position; 
683         } else {
684                 return _start;
685         }
686 }
687
688
689 void
690 Region::raise ()
691 {
692         boost::shared_ptr<Playlist> pl (playlist());
693         if (pl) {
694                 pl->raise_region (shared_from_this ());
695         }
696 }
697
698 void
699 Region::lower ()
700 {
701         boost::shared_ptr<Playlist> pl (playlist());
702         if (pl) {
703                 pl->lower_region (shared_from_this ());
704         }
705 }
706
707 void
708 Region::raise_to_top ()
709 {
710         boost::shared_ptr<Playlist> pl (playlist());
711         if (pl) {
712                 pl->raise_region_to_top (shared_from_this());
713         }
714 }
715
716 void
717 Region::lower_to_bottom ()
718 {
719         boost::shared_ptr<Playlist> pl (playlist());
720         if (pl) {
721                 pl->lower_region_to_bottom (shared_from_this());
722         }
723 }
724
725 void
726 Region::set_layer (layer_t l)
727 {
728         if (_layer != l) {
729                 _layer = l;
730                 
731                 send_change (LayerChanged);
732         }
733 }
734
735 XMLNode&
736 Region::state (bool full_state)
737 {
738         XMLNode *node = new XMLNode ("Region");
739         char buf[64];
740         char* fe = NULL;
741
742         _id.print (buf, sizeof (buf));
743         node->add_property ("id", buf);
744         node->add_property ("name", _name);
745         snprintf (buf, sizeof (buf), "%u", _start);
746         node->add_property ("start", buf);
747         snprintf (buf, sizeof (buf), "%u", _length);
748         node->add_property ("length", buf);
749         snprintf (buf, sizeof (buf), "%u", _position);
750         node->add_property ("position", buf);
751         
752         switch (_first_edit) {
753         case EditChangesNothing:
754                 fe = X_("nothing");
755                 break;
756         case EditChangesName:
757                 fe = X_("name");
758                 break;
759         case EditChangesID:
760                 fe = X_("id");
761                 break;
762         default: /* should be unreachable but makes g++ happy */
763                 cerr << "Odd region property found\n";
764                 fe = X_("nothing");
765                 break;
766         }
767
768         node->add_property ("first_edit", fe);
769
770         /* note: flags are stored by derived classes */
771
772         snprintf (buf, sizeof (buf), "%d", (int) _layer);
773         node->add_property ("layer", buf);
774         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
775         node->add_property ("sync-position", buf);
776
777         return *node;
778 }
779
780 XMLNode&
781 Region::get_state ()
782 {
783         return state (true);
784 }
785
786 int
787 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
788 {
789         const XMLNodeList& nlist = node.children();
790         const XMLProperty *prop;
791         nframes_t val;
792
793         /* this is responsible for setting those aspects of Region state 
794            that are mutable after construction.
795         */
796
797         if ((prop = node.property ("name")) == 0) {
798                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
799                 return -1;
800         }
801
802         _name = prop->value();
803
804         if ((prop = node.property ("start")) != 0) {
805                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
806                 if (val != _start) {
807                         what_changed = Change (what_changed|StartChanged);      
808                         _start = val;
809                 }
810         } else {
811                 _start = 0;
812         }
813
814         if ((prop = node.property ("length")) != 0) {
815                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
816                 if (val != _length) {
817                         what_changed = Change (what_changed|LengthChanged);
818                         _length = val;
819                 }
820         } else {
821                 _length = 1;
822         }
823
824         if ((prop = node.property ("position")) != 0) {
825                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
826                 if (val != _position) {
827                         what_changed = Change (what_changed|PositionChanged);
828                         _position = val;
829                 }
830         } else {
831                 _position = 0;
832         }
833
834         if ((prop = node.property ("layer")) != 0) {
835                 layer_t x;
836                 x = (layer_t) atoi (prop->value().c_str());
837                 if (x != _layer) {
838                         what_changed = Change (what_changed|LayerChanged);
839                         _layer = x;
840                 }
841         } else {
842                 _layer = 0;
843         }
844
845         if ((prop = node.property ("sync-position")) != 0) {
846                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
847                 if (val != _sync_position) {
848                         what_changed = Change (what_changed|SyncOffsetChanged);
849                         _sync_position = val;
850                 }
851         } else {
852                 _sync_position = _start;
853         }
854
855         /* XXX FIRST EDIT !!! */
856         
857         /* note: derived classes set flags */
858
859         if (_extra_xml) {
860                 delete _extra_xml;
861                 _extra_xml = 0;
862         }
863
864         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
865                 
866                 XMLNode *child;
867                 
868                 child = (*niter);
869                 
870                 if (child->name () == "extra") {
871                         _extra_xml = new XMLNode (*child);
872                         break;
873                 }
874         }
875
876         if (send) {
877                 send_change (what_changed);
878         }
879
880         return 0;
881 }
882
883 int
884 Region::set_state (const XMLNode& node)
885 {
886         const XMLProperty *prop;
887         Change what_changed = Change (0);
888
889         /* ID is not allowed to change, ever */
890
891         if ((prop = node.property ("id")) == 0) {
892                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
893                 return -1;
894         }
895
896         _id = prop->value();
897         
898         _first_edit = EditChangesNothing;
899         
900         set_live_state (node, what_changed, true);
901
902         return 0;
903 }
904
905 void
906 Region::freeze ()
907 {
908         _frozen++;
909 }
910
911 void
912 Region::thaw (const string& why)
913 {
914         Change what_changed = Change (0);
915
916         {
917                 Glib::Mutex::Lock lm (lock);
918
919                 if (_frozen && --_frozen > 0) {
920                         return;
921                 }
922
923                 if (pending_changed) {
924                         what_changed = pending_changed;
925                         pending_changed = Change (0);
926                 }
927         }
928
929         if (what_changed == Change (0)) {
930                 return;
931         }
932
933         if (what_changed & LengthChanged) {
934                 if (what_changed & PositionChanged) {
935                         recompute_at_start ();
936                 } 
937                 recompute_at_end ();
938         }
939                 
940         StateChanged (what_changed);
941 }
942
943 void
944 Region::send_change (Change what_changed)
945 {
946         {
947                 Glib::Mutex::Lock lm (lock);
948                 if (_frozen) {
949                         pending_changed = Change (pending_changed|what_changed);
950                         return;
951                 } 
952         }
953
954         StateChanged (what_changed);
955 }
956
957 void
958 Region::set_last_layer_op (uint64_t when)
959 {
960         _last_layer_op = when;
961 }
962
963 bool
964 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
965 {
966         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
967 }
968
969 bool
970 Region::equivalent (boost::shared_ptr<const Region> other) const
971 {
972         return _start == other->_start &&
973                 _position == other->_position &&
974                 _length == other->_length;
975 }
976
977 bool
978 Region::size_equivalent (boost::shared_ptr<const Region> other) const
979 {
980         return _start == other->_start &&
981                 _length == other->_length;
982 }
983
984 bool
985 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
986 {
987         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
988 }