Merged with trunk R861
[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     $Id$
19 */
20
21 #include <iostream>
22 #include <cmath>
23 #include <climits>
24 #include <algorithm>
25
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
28
29 #include <glibmm/thread.h>
30 #include <pbd/xml++.h>
31
32 #include <ardour/region.h>
33 #include <ardour/playlist.h>
34 #include <ardour/session.h>
35 #include <ardour/source.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 /** Basic Region constructor (single source) */
52 Region::Region (boost::shared_ptr<Source> src, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
53         : _name(name)
54         , _type(type)
55         , _flags(flags)
56         , _start(start) 
57         , _length(length) 
58         , _position(0) 
59         , _sync_position(_start)
60         , _layer(layer)
61         , _first_edit(EditChangesNothing)
62         , _frozen(0)
63         , _read_data_count(0)
64         , _pending_changed(Change (0))
65         , _last_layer_op(0)
66         , _playlist(0)
67 {
68         _current_state_id = 0;
69         
70         _sources.push_back (src);
71         _master_sources.push_back (src);
72         src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
73
74         assert(_sources.size() > 0);
75 }
76
77 /** Basic Region constructor (many sources) */
78 Region::Region (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
79         : _name(name)
80         , _type(type)
81         , _flags(flags)
82         , _start(start) 
83         , _length(length) 
84         , _position(0) 
85         , _sync_position(_start)
86         , _layer(layer)
87         , _first_edit(EditChangesNothing)
88         , _frozen(0)
89         , _read_data_count(0)
90         , _pending_changed(Change (0))
91         , _last_layer_op(0)
92         , _playlist(0)
93 {
94         _current_state_id = 0;
95         
96         set<boost::shared_ptr<Source> > unique_srcs;
97
98         for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
99                 _sources.push_back (*i);
100                 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
101                 unique_srcs.insert (*i);
102         }
103
104         for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
105                 _master_sources.push_back (*i);
106                 if (unique_srcs.find (*i) == unique_srcs.end()) {
107                         (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
108                 }
109         }
110         
111         assert(_sources.size() > 0);
112 }
113
114 /** Create a new Region from part of an existing one */
115 Region::Region (boost::shared_ptr<const Region> other, jack_nframes_t offset, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
116         : _name(name)
117         , _type(other->data_type())
118         , _flags(Flag(flags & ~(Locked|WholeFile|Hidden)))
119         , _start(other->_start + offset) 
120         , _length(length) 
121         , _position(0) 
122         , _sync_position(_start)
123         , _layer(layer)
124         , _first_edit(EditChangesNothing)
125         , _frozen(0)
126         , _read_data_count(0)
127         , _pending_changed(Change (0))
128         , _last_layer_op(0)
129         , _playlist(0)
130 {
131         _current_state_id = 0;
132         
133         if (other->_sync_position < offset)
134                 _sync_position = other->_sync_position;
135
136         set<boost::shared_ptr<Source> > unique_srcs;
137
138         for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
139                 _sources.push_back (*i);
140                 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
141                 unique_srcs.insert (*i);
142         }
143         
144         if (other->_sync_position < offset) {
145                 _sync_position = other->_sync_position;
146         }
147
148         for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
149                 if (unique_srcs.find (*i) == unique_srcs.end()) {
150                         (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
151                 }
152                 _master_sources.push_back (*i);
153         }
154         
155         assert(_sources.size() > 0);
156 }
157
158 /** Pure copy constructor */
159 Region::Region (boost::shared_ptr<const Region> other)
160         : _name(other->_name)
161         , _type(other->data_type())
162         , _flags(Flag(other->_flags & ~Locked))
163         , _start(other->_start) 
164         , _length(other->_length) 
165         , _position(other->_position) 
166         , _sync_position(other->_sync_position)
167         , _layer(other->_layer)
168         , _first_edit(EditChangesID)
169         , _frozen(0)
170         , _read_data_count(0)
171         , _pending_changed(Change(0))
172         , _last_layer_op(other->_last_layer_op)
173         , _playlist(0)
174 {
175         _current_state_id = 0;
176         
177         other->_first_edit = EditChangesName;
178
179         if (other->_extra_xml) {
180                 _extra_xml = new XMLNode (*other->_extra_xml);
181         } else {
182                 _extra_xml = 0;
183         }
184
185         set<boost::shared_ptr<Source> > unique_srcs;
186
187         for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
188                 _sources.push_back (*i);
189                 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
190                 unique_srcs.insert (*i);
191         }
192
193         for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
194                 _master_sources.push_back (*i);
195                 if (unique_srcs.find (*i) == unique_srcs.end()) {
196                         (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
197                 }
198         }
199         
200         assert(_sources.size() > 0);
201 }
202
203 Region::Region (SourceList& srcs, const XMLNode& node)
204         : _name(X_("error: XML did not reset this"))
205         , _type(DataType::NIL) // to be loaded from XML
206         , _flags(Flag(0))
207         , _start(0) 
208         , _length(0) 
209         , _position(0) 
210         , _sync_position(_start)
211         , _layer(0)
212         , _first_edit(EditChangesNothing)
213         , _frozen(0)
214         , _read_data_count(0)
215         , _pending_changed(Change(0))
216         , _last_layer_op(0)
217         , _playlist(0)
218
219 {
220         _current_state_id = 0;
221
222         set<boost::shared_ptr<Source> > unique_srcs;
223
224         for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
225                 _sources.push_back (*i);
226                 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
227                 unique_srcs.insert (*i);
228         }
229
230         for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
231                 _master_sources.push_back (*i);
232                 if (unique_srcs.find (*i) == unique_srcs.end()) {
233                         (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
234                 }
235         }
236
237         if (set_state (node)) {
238                 throw failed_constructor();
239         }
240
241         assert(_type != DataType::NIL);
242         assert(_sources.size() > 0);
243 }
244
245 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
246         : _name(X_("error: XML did not reset this"))
247         , _type(DataType::NIL)
248         , _flags(Flag(0))
249         , _start(0) 
250         , _length(0) 
251         , _position(0) 
252         , _sync_position(_start)
253         , _layer(0)
254         , _first_edit(EditChangesNothing)
255         , _frozen(0)
256         , _read_data_count(0)
257         , _pending_changed(Change(0))
258         , _last_layer_op(0)
259         , _playlist(0)
260 {
261         _sources.push_back (src);
262
263         _current_state_id = 0;
264
265         if (set_state (node)) {
266                 throw failed_constructor();
267         }
268         
269         assert(_type != DataType::NIL);
270         assert(_sources.size() > 0);
271 }
272
273 Region::~Region ()
274 {
275         notify_callbacks ();
276
277         /* derived classes must emit GoingAway */
278 }
279
280 void
281 Region::set_playlist (Playlist* pl)
282 {
283         _playlist = pl;
284 }
285
286 void
287 Region::store_state (RegionState& state) const
288 {
289         state._start = _start;
290         state._length = _length;
291         state._position = _position;
292         state._flags = _flags;
293         state._sync_position = _sync_position;
294         state._layer = _layer;
295         state._name = _name;
296         state._first_edit = _first_edit;
297 }       
298
299 Change
300 Region::restore_and_return_flags (RegionState& state)
301 {
302         Change what_changed = Change (0);
303
304         {
305                 Glib::Mutex::Lock lm (_lock);
306                 
307                 if (_start != state._start) {
308                         what_changed = Change (what_changed|StartChanged);      
309                         _start = state._start;
310                 }
311                 if (_length != state._length) {
312                         what_changed = Change (what_changed|LengthChanged);
313                         _length = state._length;
314                 }
315                 if (_position != state._position) {
316                         what_changed = Change (what_changed|PositionChanged);
317                         _position = state._position;
318                 } 
319                 if (_sync_position != state._sync_position) {
320                         _sync_position = state._sync_position;
321                         what_changed = Change (what_changed|SyncOffsetChanged);
322                 }
323                 if (_layer != state._layer) {
324                         what_changed = Change (what_changed|LayerChanged);
325                         _layer = state._layer;
326                 }
327
328                 uint32_t old_flags = _flags;
329                 _flags = Flag (state._flags);
330                 
331                 if ((old_flags ^ state._flags) & Muted) {
332                         what_changed = Change (what_changed|MuteChanged);
333                 }
334                 if ((old_flags ^ state._flags) & Opaque) {
335                         what_changed = Change (what_changed|OpacityChanged);
336                 }
337                 if ((old_flags ^ state._flags) & Locked) {
338                         what_changed = Change (what_changed|LockChanged);
339                 }
340
341                 _first_edit = state._first_edit;
342         }
343
344         return what_changed;
345 }
346
347 void
348 Region::set_name (string str)
349
350 {
351         if (_name != str) {
352                 _name = str; 
353                 send_change (NameChanged);
354         }
355 }
356
357 void
358 Region::set_length (jack_nframes_t len, void *src)
359 {
360         if (_flags & Locked) {
361                 return;
362         }
363
364         if (_length != len && len != 0) {
365
366                 if (!verify_length (len)) {
367                         return;
368                 }
369                 
370                 _length = len;
371
372                 _flags = Region::Flag (_flags & ~WholeFile);
373
374                 first_edit ();
375                 maybe_uncopy ();
376
377                 if (!_frozen) {
378                         recompute_at_end ();
379
380                         char buf[64];
381                         snprintf (buf, sizeof (buf), "length set to %u", len);
382                         save_state (buf);
383                 }
384
385                 send_change (LengthChanged);
386         }
387 }
388
389 void
390 Region::maybe_uncopy ()
391 {
392 }
393
394 void
395 Region::first_edit ()
396 {
397         if (_first_edit != EditChangesNothing && _playlist) {
398
399                 _name = _playlist->session().new_region_name (_name);
400                 _first_edit = EditChangesNothing;
401
402                 send_change (NameChanged);
403                 /// XXX CheckNewRegion (boost::shared_ptr<Region>(this));
404         }
405 }
406
407 void
408 Region::move_to_natural_position (void *src)
409 {
410         if (!_playlist) {
411                 return;
412         }
413
414         boost::shared_ptr<Region> whole_file_region = get_parent();
415
416         if (whole_file_region) {
417                 set_position (whole_file_region->position() + _start, src);
418         }
419 }
420         
421 void
422 Region::special_set_position (jack_nframes_t pos)
423 {
424         /* this is used when creating a whole file region as 
425            a way to store its "natural" or "captured" position.
426         */
427
428         _position = pos;
429 }
430
431 void
432 Region::set_position (jack_nframes_t pos, void *src)
433 {
434         if (_flags & Locked) {
435                 return;
436         }
437
438         if (_position != pos) {
439                 _position = pos;
440
441                 if (!_frozen) {
442                         char buf[64];
443                         snprintf (buf, sizeof (buf), "position set to %u", pos);
444                         save_state (buf);
445                 }
446         }
447
448         /* do this even if the position is the same. this helps out
449            a GUI that has moved its representation already.
450         */
451
452         send_change (PositionChanged);
453 }
454
455 void
456 Region::set_position_on_top (jack_nframes_t pos, void *src)
457 {
458         if (_flags & Locked) {
459                 return;
460         }
461
462         if (_position != pos) {
463                 _position = pos;
464
465                 if (!_frozen) {
466                         char buf[64];
467                         snprintf (buf, sizeof (buf), "position set to %u", pos);
468                         save_state (buf);
469                 }
470         }
471
472         _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
473
474         /* do this even if the position is the same. this helps out
475            a GUI that has moved its representation already.
476         */
477         
478         send_change (PositionChanged);
479 }
480
481 void
482 Region::nudge_position (long n, void *src)
483 {
484         if (_flags & Locked) {
485                 return;
486         }
487
488         if (n == 0) {
489                 return;
490         }
491         
492         if (n > 0) {
493                 if (_position > max_frames - n) {
494                         _position = max_frames;
495                 } else {
496                         _position += n;
497                 }
498         } else {
499                 if (_position < (jack_nframes_t) -n) {
500                         _position = 0;
501                 } else {
502                         _position += n;
503                 }
504         }
505
506         if (!_frozen) {
507                 char buf[64];
508                 snprintf (buf, sizeof (buf), "position set to %u", _position);
509                 save_state (buf);
510         }
511
512         send_change (PositionChanged);
513 }
514
515 void
516 Region::set_start (jack_nframes_t pos, void *src)
517 {
518         if (_flags & Locked) {
519                 return;
520         }
521         /* This just sets the start, nothing else. It effectively shifts
522            the contents of the Region within the overall extent of the Source,
523            without changing the Region's position or length
524         */
525
526         if (_start != pos) {
527
528                 if (!verify_start (pos)) {
529                         return;
530                 }
531
532                 _start = pos;
533                 _flags = Region::Flag (_flags & ~WholeFile);
534                 first_edit ();
535
536                 if (!_frozen) {
537                         char buf[64];
538                         snprintf (buf, sizeof (buf), "start set to %u", pos);
539                         save_state (buf);
540                 }
541
542                 send_change (StartChanged);
543         }
544 }
545
546 void
547 Region::trim_start (jack_nframes_t new_position, void *src)
548 {
549         if (_flags & Locked) {
550                 return;
551         }
552         jack_nframes_t new_start;
553         int32_t start_shift;
554         
555         if (new_position > _position) {
556                 start_shift = new_position - _position;
557         } else {
558                 start_shift = -(_position - new_position);
559         }
560
561         if (start_shift > 0) {
562
563                 if (_start > max_frames - start_shift) {
564                         new_start = max_frames;
565                 } else {
566                         new_start = _start + start_shift;
567                 }
568
569                 if (!verify_start (new_start)) {
570                         return;
571                 }
572
573         } else if (start_shift < 0) {
574
575                 if (_start < (jack_nframes_t) -start_shift) {
576                         new_start = 0;
577                 } else {
578                         new_start = _start + start_shift;
579                 }
580         } else {
581                 return;
582         }
583
584         if (new_start == _start) {
585                 return;
586         }
587         
588         _start = new_start;
589         _flags = Region::Flag (_flags & ~WholeFile);
590         first_edit ();
591
592         if (!_frozen) {
593                 char buf[64];
594                 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
595                 save_state (buf);
596         }
597
598         send_change (StartChanged);
599 }
600
601 void
602 Region::trim_front (jack_nframes_t new_position, void *src)
603 {
604         if (_flags & Locked) {
605                 return;
606         }
607
608         jack_nframes_t end = _position + _length - 1;
609         jack_nframes_t source_zero;
610
611         if (_position > _start) {
612                 source_zero = _position - _start;
613         } else {
614                 source_zero = 0; // its actually negative, but this will work for us
615         }
616
617         if (new_position < end) { /* can't trim it zero or negative length */
618                 
619                 jack_nframes_t newlen;
620
621                 /* can't trim it back passed where source position zero is located */
622                 
623                 new_position = max (new_position, source_zero);
624                 
625                 
626                 if (new_position > _position) {
627                         newlen = _length - (new_position - _position);
628                 } else {
629                         newlen = _length + (_position - new_position);
630                 }
631                 
632                 trim_to_internal (new_position, newlen, src);
633                 if (!_frozen) {
634                         recompute_at_start ();
635                 }
636         }
637 }
638
639 void
640 Region::trim_end (jack_nframes_t new_endpoint, void *src)
641 {
642         if (_flags & Locked) {
643                 return;
644         }
645
646         if (new_endpoint > _position) {
647                 trim_to_internal (_position, new_endpoint - _position, this);
648                 if (!_frozen) {
649                         recompute_at_end ();
650                 }
651         }
652 }
653
654 void
655 Region::trim_to (jack_nframes_t position, jack_nframes_t length, void *src)
656 {
657         if (_flags & Locked) {
658                 return;
659         }
660
661         trim_to_internal (position, length, src);
662
663         if (!_frozen) {
664                 recompute_at_start ();
665                 recompute_at_end ();
666         }
667 }
668
669 void
670 Region::trim_to_internal (jack_nframes_t position, jack_nframes_t length, void *src)
671 {
672         int32_t start_shift;
673         jack_nframes_t new_start;
674
675         if (_flags & Locked) {
676                 return;
677         }
678
679         if (position > _position) {
680                 start_shift = position - _position;
681         } else {
682                 start_shift = -(_position - position);
683         }
684
685         if (start_shift > 0) {
686
687                 if (_start > max_frames - start_shift) {
688                         new_start = max_frames;
689                 } else {
690                         new_start = _start + start_shift;
691                 }
692
693
694         } else if (start_shift < 0) {
695
696                 if (_start < (jack_nframes_t) -start_shift) {
697                         new_start = 0;
698                 } else {
699                         new_start = _start + start_shift;
700                 }
701         } else {
702                 new_start = _start;
703         }
704
705         if (!verify_start_and_length (new_start, length)) {
706                 return;
707         }
708
709         Change what_changed = Change (0);
710
711         if (_start != new_start) {
712                 _start = new_start;
713                 what_changed = Change (what_changed|StartChanged);
714         }
715         if (_length != length) {
716                 _length = length;
717                 what_changed = Change (what_changed|LengthChanged);
718         }
719         if (_position != position) {
720                 _position = position;
721                 what_changed = Change (what_changed|PositionChanged);
722         }
723         
724         _flags = Region::Flag (_flags & ~WholeFile);
725
726         if (what_changed & (StartChanged|LengthChanged)) {
727                 first_edit ();
728         } 
729
730         if (what_changed) {
731                 
732                 if (!_frozen) {
733                         char buf[64];
734                         snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
735                         save_state (buf);
736                 }
737
738                 send_change (what_changed);
739         }
740 }       
741
742 void
743 Region::set_hidden (bool yn)
744 {
745         if (hidden() != yn) {
746
747                 if (yn) {
748                         _flags = Flag (_flags|Hidden);
749                 } else {
750                         _flags = Flag (_flags & ~Hidden);
751                 }
752
753                 send_change (HiddenChanged);
754         }
755 }
756
757 void
758 Region::set_muted (bool yn)
759 {
760         if (muted() != yn) {
761
762                 if (yn) {
763                         _flags = Flag (_flags|Muted);
764                 } else {
765                         _flags = Flag (_flags & ~Muted);
766                 }
767
768                 if (!_frozen) {
769                         char buf[64];
770                         if (yn) {
771                                 snprintf (buf, sizeof (buf), "muted");
772                         } else {
773                                 snprintf (buf, sizeof (buf), "unmuted");
774                         }
775                         save_state (buf);
776                 }
777
778                 send_change (MuteChanged);
779         }
780 }
781
782 void
783 Region::set_opaque (bool yn)
784 {
785         if (opaque() != yn) {
786                 if (!_frozen) {
787                         char buf[64];
788                         if (yn) {
789                                 snprintf (buf, sizeof (buf), "opaque");
790                                 _flags = Flag (_flags|Opaque);
791                         } else {
792                                 snprintf (buf, sizeof (buf), "translucent");
793                                 _flags = Flag (_flags & ~Opaque);
794                         }
795                         save_state (buf);
796                 }
797                 send_change (OpacityChanged);
798         }
799 }
800
801 void
802 Region::set_locked (bool yn)
803 {
804         if (locked() != yn) {
805                 if (!_frozen) {
806                         char buf[64];
807                         if (yn) {
808                                 snprintf (buf, sizeof (buf), "locked");
809                                 _flags = Flag (_flags|Locked);
810                         } else {
811                                 snprintf (buf, sizeof (buf), "unlocked");
812                                 _flags = Flag (_flags & ~Locked);
813                         }
814                         save_state (buf);
815                 }
816                 send_change (LockChanged);
817         }
818 }
819
820 void
821 Region::set_sync_position (jack_nframes_t absolute_pos)
822 {
823         jack_nframes_t file_pos;
824
825         file_pos = _start + (absolute_pos - _position);
826
827         if (file_pos != _sync_position) {
828                 
829                 _sync_position = file_pos;
830                 _flags = Flag (_flags|SyncMarked);
831
832                 if (!_frozen) {
833                         char buf[64];
834                         maybe_uncopy ();
835                         snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
836                         save_state (buf);
837                 }
838                 send_change (SyncOffsetChanged);
839         }
840 }
841
842 void
843 Region::clear_sync_position ()
844 {
845         if (_flags & SyncMarked) {
846                 _flags = Flag (_flags & ~SyncMarked);
847
848                 if (!_frozen) {
849                         maybe_uncopy ();
850                         save_state ("sync point removed");
851                 }
852                 send_change (SyncOffsetChanged);
853         }
854 }
855
856 jack_nframes_t
857 Region::sync_offset (int& dir) const
858 {
859         /* returns the sync point relative the first frame of the region */
860
861         if (_flags & SyncMarked) {
862                 if (_sync_position > _start) {
863                         dir = 1;
864                         return _sync_position - _start; 
865                 } else {
866                         dir = -1;
867                         return _start - _sync_position;
868                 }
869         } else {
870                 dir = 0;
871                 return 0;
872         }
873 }
874
875 jack_nframes_t 
876 Region::adjust_to_sync (jack_nframes_t pos)
877 {
878         int sync_dir;
879         jack_nframes_t offset = sync_offset (sync_dir);
880         
881         if (sync_dir > 0) {
882                 if (max_frames - pos > offset) {
883                         pos += offset;
884                 }
885         } else {
886                 if (pos > offset) {
887                         pos -= offset;
888                 } else {
889                         pos = 0;
890                 }
891         }
892
893         return pos;
894 }
895
896 jack_nframes_t
897 Region::sync_position() const
898 {
899         if (_flags & SyncMarked) {
900                 return _sync_position; 
901         } else {
902                 return _start;
903         }
904 }
905
906
907 void
908 Region::raise ()
909 {
910         if (_playlist == 0) {
911                 return;
912         }
913
914         _playlist->raise_region (boost::shared_ptr<Region>(this));
915 }
916
917 void
918 Region::lower ()
919 {
920         if (_playlist == 0) {
921                 return;
922         }
923
924         _playlist->lower_region (boost::shared_ptr<Region>(this));
925 }
926
927 void
928 Region::raise_to_top ()
929 {
930
931         if (_playlist == 0) {
932                 return;
933         }
934
935         _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
936 }
937
938 void
939 Region::lower_to_bottom ()
940 {
941         if (_playlist == 0) {
942                 return;
943         }
944
945         _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
946 }
947
948 void
949 Region::set_layer (layer_t l)
950 {
951         if (_layer != l) {
952                 _layer = l;
953                 
954                 if (!_frozen) {
955                         char buf[64];
956                         snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
957                         save_state (buf);
958                 }
959                 
960                 send_change (LayerChanged);
961         }
962 }
963
964 XMLNode&
965 Region::state (bool full_state)
966 {
967         XMLNode *node = new XMLNode ("Region");
968         char buf[64];
969         
970         _id.print (buf);
971         node->add_property ("id", buf);
972         node->add_property ("name", _name);
973         node->add_property ("type", _type.to_string());
974         snprintf (buf, sizeof (buf), "%u", _start);
975         node->add_property ("start", buf);
976         snprintf (buf, sizeof (buf), "%u", _length);
977         node->add_property ("length", buf);
978         snprintf (buf, sizeof (buf), "%u", _position);
979         node->add_property ("position", buf);
980
981         /* note: flags are stored by derived classes */
982
983         snprintf (buf, sizeof (buf), "%d", (int) _layer);
984         node->add_property ("layer", buf);
985         snprintf (buf, sizeof (buf), "%u", _sync_position);
986         node->add_property ("sync-position", buf);
987
988         return *node;
989 }
990
991 XMLNode&
992 Region::get_state ()
993 {
994         return state (true);
995 }
996
997 int
998 Region::set_state (const XMLNode& node)
999 {
1000         const XMLNodeList& nlist = node.children();
1001         const XMLProperty *prop;
1002
1003         if (_extra_xml) {
1004                 delete _extra_xml;
1005                 _extra_xml = 0;
1006         }
1007
1008         if ((prop = node.property ("id")) == 0) {
1009                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1010                 return -1;
1011         }
1012
1013         _id = prop->value();
1014
1015         if ((prop = node.property ("name")) == 0) {
1016                 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
1017                 return -1;
1018         }
1019
1020         _name = prop->value();
1021         
1022         if ((prop = node.property ("type")) == 0) {
1023                 _type = DataType::AUDIO;
1024         } else {
1025                 _type = DataType(prop->value());
1026         }
1027
1028         if ((prop = node.property ("start")) != 0) {
1029                 _start = (jack_nframes_t) atoi (prop->value().c_str());
1030         }
1031
1032         if ((prop = node.property ("length")) != 0) {
1033                 _length = (jack_nframes_t) atoi (prop->value().c_str());
1034         }
1035
1036         if ((prop = node.property ("position")) != 0) {
1037                 _position = (jack_nframes_t) atoi (prop->value().c_str());
1038         }
1039
1040         if ((prop = node.property ("layer")) != 0) {
1041                 _layer = (layer_t) atoi (prop->value().c_str());
1042         }
1043
1044         /* note: derived classes set flags */
1045
1046         if ((prop = node.property ("sync-position")) != 0) {
1047                 _sync_position = (jack_nframes_t) atoi (prop->value().c_str());
1048         } else {
1049                 _sync_position = _start;
1050         }
1051         
1052         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1053                 
1054                 XMLNode *child;
1055                 
1056                 child = (*niter);
1057                 
1058                 if (child->name () == "extra") {
1059                         _extra_xml = new XMLNode (*child);
1060                         break;
1061                 }
1062         }
1063
1064         _first_edit = EditChangesNothing;
1065
1066         return 0;
1067 }
1068
1069 void
1070 Region::freeze ()
1071 {
1072         _frozen++;
1073 }
1074
1075 void
1076 Region::thaw (const string& why)
1077 {
1078         Change what_changed = Change (0);
1079
1080         {
1081                 Glib::Mutex::Lock lm (_lock);
1082
1083                 if (_frozen && --_frozen > 0) {
1084                         return;
1085                 }
1086
1087                 if (_pending_changed) {
1088                         what_changed = _pending_changed;
1089                         _pending_changed = Change (0);
1090                 }
1091         }
1092
1093         if (what_changed == Change (0)) {
1094                 return;
1095         }
1096
1097         if (what_changed & LengthChanged) {
1098                 if (what_changed & PositionChanged) {
1099                         recompute_at_start ();
1100                 } 
1101                 recompute_at_end ();
1102         }
1103                 
1104         save_state (why);
1105         StateChanged (what_changed);
1106 }
1107
1108 void
1109 Region::send_change (Change what_changed)
1110 {
1111         {
1112                 Glib::Mutex::Lock lm (_lock);
1113                 if (_frozen) {
1114                         _pending_changed = Change (_pending_changed|what_changed);
1115                         return;
1116                 } 
1117         }
1118
1119         StateManager::send_state_changed (what_changed);
1120 }
1121
1122 void
1123 Region::set_last_layer_op (uint64_t when)
1124 {
1125         _last_layer_op = when;
1126 }
1127
1128 bool
1129 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1130 {
1131         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1132 }
1133
1134 bool
1135 Region::equivalent (boost::shared_ptr<const Region> other) const
1136 {
1137         return _start == other->_start &&
1138                 _position == other->_position &&
1139                 _length == other->_length;
1140 }
1141
1142 bool
1143 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1144 {
1145         return _start == other->_start &&
1146                 _length == other->_length;
1147 }
1148
1149 bool
1150 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1151 {
1152         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1153 }
1154
1155 void
1156 Region::source_deleted (boost::shared_ptr<Source>)
1157 {
1158         delete this;
1159 }
1160
1161 vector<string>
1162 Region::master_source_names ()
1163 {
1164         SourceList::iterator i;
1165
1166         vector<string> names;
1167         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1168                 names.push_back((*i)->name());
1169         }
1170
1171         return names;
1172 }
1173
1174 bool
1175 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1176 {
1177         if (!other)
1178                 return false;
1179
1180         SourceList::const_iterator i;
1181         SourceList::const_iterator io;
1182
1183         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1184                 if ((*i)->id() != (*io)->id()) {
1185                         return false;
1186                 }
1187         }
1188
1189         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1190                 if ((*i)->id() != (*io)->id()) {
1191                         return false;
1192                 }
1193         }
1194
1195         return true;
1196 }
1197
1198 bool
1199 Region::verify_length (jack_nframes_t len)
1200 {
1201         for (uint32_t n=0; n < _sources.size(); ++n) {
1202                 if (_start > _sources[n]->length() - len) {
1203                         return false;
1204                 }
1205         }
1206         return true;
1207 }
1208
1209 bool
1210 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1211 {
1212         for (uint32_t n=0; n < _sources.size(); ++n) {
1213                 if (new_length > _sources[n]->length() - new_start) {
1214                         return false;
1215                 }
1216         }
1217         return true;
1218 }
1219 bool
1220 Region::verify_start (jack_nframes_t pos)
1221 {
1222         for (uint32_t n=0; n < _sources.size(); ++n) {
1223                 if (pos > _sources[n]->length() - _length) {
1224                         return false;
1225                 }
1226         }
1227         return true;
1228 }
1229
1230 bool
1231 Region::verify_start_mutable (jack_nframes_t& new_start)
1232 {
1233         for (uint32_t n=0; n < _sources.size(); ++n) {
1234                 if (new_start > _sources[n]->length() - _length) {
1235                         new_start = _sources[n]->length() - _length;
1236                 }
1237         }
1238         return true;
1239 }
1240
1241 boost::shared_ptr<Region>
1242 Region::get_parent()
1243 {
1244         boost::shared_ptr<Region> r;
1245
1246         if (_playlist) {
1247                 r = _playlist->session().find_whole_file_parent (*this);
1248         }
1249         
1250         return r;
1251 }
1252