Fix fade outs on split regions.
[ardour.git] / libs / ardour / audioregion.cc
1 /*
2     Copyright (C) 2000-2006 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 <cmath>
21 #include <climits>
22 #include <cfloat>
23 #include <algorithm>
24
25 #include <set>
26
27 #include <sigc++/bind.h>
28 #include <sigc++/class_slot.h>
29
30 #include <glibmm/thread.h>
31
32 #include <pbd/basename.h>
33 #include <pbd/xml++.h>
34 #include <pbd/stacktrace.h>
35 #include <pbd/enumwriter.h>
36 #include <pbd/convert.h>
37
38 #include <ardour/audioregion.h>
39 #include <ardour/session.h>
40 #include <ardour/gain.h>
41 #include <ardour/dB.h>
42 #include <ardour/playlist.h>
43 #include <ardour/audiofilesource.h>
44 #include <ardour/region_factory.h>
45 #include <ardour/runtime_functions.h>
46 #include <ardour/transient_detector.h>
47
48 #include "i18n.h"
49 #include <locale.h>
50
51 using namespace std;
52 using namespace ARDOUR;
53 using namespace PBD;
54
55 /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
56
57 Change AudioRegion::FadeInChanged         = ARDOUR::new_change();
58 Change AudioRegion::FadeOutChanged        = ARDOUR::new_change();
59 Change AudioRegion::FadeInActiveChanged   = ARDOUR::new_change();
60 Change AudioRegion::FadeOutActiveChanged  = ARDOUR::new_change();
61 Change AudioRegion::EnvelopeActiveChanged = ARDOUR::new_change();
62 Change AudioRegion::ScaleAmplitudeChanged = ARDOUR::new_change();
63 Change AudioRegion::EnvelopeChanged       = ARDOUR::new_change();
64
65 void
66 AudioRegion::init ()
67 {
68         _scale_amplitude = 1.0;
69
70         set_default_fades ();
71         set_default_envelope ();
72
73         listen_to_my_curves ();
74         listen_to_my_sources ();
75 }
76
77 /* constructor for use by derived types only */
78 AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name)
79         : Region (s, start, length, name, DataType::AUDIO)
80         , _automatable(s)
81         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
82         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
83         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
84 {
85         init ();
86 }
87
88 /** Basic AudioRegion constructor (one channel) */
89 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
90         : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0,  Region::Flag(Region::DefaultFlags|Region::External))
91         , _automatable(src->session())
92         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
93         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
94         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
95 {
96         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
97         if (afs) {
98                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
99         }
100
101         init ();
102 }
103
104 /* Basic AudioRegion constructor (one channel) */
105 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
106         : Region (src, start, length, name, DataType::AUDIO, layer, flags)
107         , _automatable(src->session())
108         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
109         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
110         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
111 {
112         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
113         if (afs) {
114                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
115         }
116
117         init ();
118 }
119
120 /* Basic AudioRegion constructor (many channels) */
121 AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
122         : Region (srcs, start, length, name, DataType::AUDIO, layer, flags)
123         , _automatable(srcs[0]->session())
124         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
125         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
126         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
127 {
128         init ();
129         listen_to_my_sources ();
130 }
131
132 /** Create a new AudioRegion, that is part of an existing one */
133 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
134         : Region (other, offset, length, name, layer, flags)
135         , _automatable(other->session())
136         , _fade_in (new AutomationList(*other->_fade_in))
137         , _fade_out (new AutomationList(*other->_fade_out))
138         , _envelope (new AutomationList(*other->_envelope, offset, offset + length))
139 {
140         set<boost::shared_ptr<Source> > unique_srcs;
141
142         for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
143                 _sources.push_back (*i);
144
145                 pair<set<boost::shared_ptr<Source> >::iterator,bool> result;
146
147                 result = unique_srcs.insert (*i);
148                 
149                 if (result.second) {
150                         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
151                         if (afs) {
152                                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
153                         }
154                 }
155         }
156
157         /* return to default fades if the existing ones are too long */
158
159         if (_flags & LeftOfSplit) {
160                 if (_fade_in->back()->when >= _length) {
161                         set_default_fade_in ();
162                 } else {
163                         _fade_in_disabled = other->_fade_in_disabled;
164                 }
165                 set_default_fade_out ();
166                 _flags = Flag (_flags & ~Region::LeftOfSplit);
167         }
168
169         if (_flags & RightOfSplit) {
170                 if (_fade_out->back()->when >= _length) {
171                         set_default_fade_out ();
172                 } else {
173                         _fade_out_disabled = other->_fade_out_disabled;
174                 }
175                 set_default_fade_in ();
176                 _flags = Flag (_flags & ~Region::RightOfSplit);
177         }
178
179         _scale_amplitude = other->_scale_amplitude;
180
181         assert(_type == DataType::AUDIO);
182         
183         listen_to_my_curves ();
184         listen_to_my_sources ();
185 }
186
187 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
188         : Region (other)
189         , _automatable (other->session())
190         , _fade_in (new AutomationList (*other->_fade_in))
191         , _fade_out (new AutomationList (*other->_fade_out))
192         , _envelope (new AutomationList (*other->_envelope))
193 {
194         assert(_type == DataType::AUDIO);
195         _scale_amplitude = other->_scale_amplitude;
196
197         set_default_fades ();
198
199         listen_to_my_curves ();
200         listen_to_my_sources ();
201 }
202
203 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const SourceList& srcs,
204                           nframes_t length, const string& name, layer_t layer, Flag flags)
205         : Region (other, length, name, layer, flags)
206         , _automatable (other->session())
207         , _fade_in (new AutomationList (*other->_fade_in))
208         , _fade_out (new AutomationList (*other->_fade_out))
209         , _envelope (new AutomationList (*other->_envelope))
210 {
211         /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */
212
213         set<boost::shared_ptr<AudioSource> > unique_srcs;
214
215         for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
216
217                 _sources.push_back (*i);
218                 _master_sources.push_back (*i);
219
220                 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> ((*i));
221                 if (afs) {
222                         afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
223                 }
224         }
225
226         _scale_amplitude = other->_scale_amplitude;
227
228         _fade_in_disabled = 0;
229         _fade_out_disabled = 0;
230
231         listen_to_my_curves ();
232         listen_to_my_sources ();
233 }
234
235 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
236         : Region (src, node)
237         , _automatable(src->session())
238         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
239         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
240         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
241 {
242         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
243         if (afs) {
244                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
245         }
246
247         init ();
248
249         if (set_state (node)) {
250                 throw failed_constructor();
251         }
252
253         assert(_type == DataType::AUDIO);
254         listen_to_my_sources ();
255 }
256
257 AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
258         : Region (srcs, node)
259         , _automatable(srcs[0]->session())
260         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
261         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
262         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
263 {
264         init ();
265
266         if (set_state (node)) {
267                 throw failed_constructor();
268         }
269
270         assert(_type == DataType::AUDIO);
271         listen_to_my_sources ();
272 }
273
274 AudioRegion::~AudioRegion ()
275 {
276 }
277
278 void
279 AudioRegion::listen_to_my_sources ()
280 {
281         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
282                 (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients));
283         }
284 }
285
286 void
287 AudioRegion::listen_to_my_curves ()
288 {
289         _envelope->StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
290         _fade_in->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_in_changed));
291         _fade_out->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_out_changed));
292 }
293
294 void
295 AudioRegion::set_envelope_active (bool yn)
296 {
297         if (envelope_active() != yn) {
298                 char buf[64];
299                 if (yn) {
300                         snprintf (buf, sizeof (buf), "envelope active");
301                         _flags = Flag (_flags|EnvelopeActive);
302                 } else {
303                         snprintf (buf, sizeof (buf), "envelope off");
304                         _flags = Flag (_flags & ~EnvelopeActive);
305                 }
306                 send_change (EnvelopeActiveChanged);
307         }
308 }
309
310 ARDOUR::nframes_t
311 AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nframes_t cnt, uint32_t chan_n, double samples_per_unit) const
312 {
313         if (chan_n >= _sources.size()) {
314                 return 0; 
315         }
316
317         if (audio_source(chan_n)->read_peaks (buf, npeaks, offset, cnt, samples_per_unit)) {
318                 return 0;
319         } else {
320                 if (_scale_amplitude != 1.0) {
321                         for (nframes_t n = 0; n < npeaks; ++n) {
322                                 buf[n].max *= _scale_amplitude;
323                                 buf[n].min *= _scale_amplitude;
324                         }
325                 }
326                 return cnt;
327         }
328 }
329
330 nframes64_t
331 AudioRegion::read (Sample* buf, nframes64_t timeline_position, nframes64_t cnt, int channel) const
332 {
333         /* raw read, no fades, no gain, nada */
334         return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
335 }
336
337 nframes64_t
338 AudioRegion::read_with_ops (Sample* buf, nframes64_t file_position, nframes64_t cnt, int channel, ReadOps rops) const
339 {
340         return _read_at (_sources, _length, buf, 0, 0, file_position, cnt, channel, 0, 0, rops);
341 }
342
343 nframes_t
344 AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t file_position, 
345                       nframes_t cnt, 
346                       uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const
347 {
348         /* regular diskstream/butler read complete with fades etc */
349         return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer, file_position, cnt, chan_n, read_frames, skip_frames, ReadOps (~0));
350 }
351
352 nframes_t
353 AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, 
354                              nframes_t cnt, uint32_t chan_n) const
355 {
356         return _read_at (_master_sources, _master_sources.front()->length(), buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0);
357 }
358
359 nframes_t
360 AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
361                        Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
362                        nframes_t position, nframes_t cnt, 
363                        uint32_t chan_n, 
364                        nframes_t read_frames, 
365                        nframes_t skip_frames,
366                        ReadOps rops) const
367 {
368         nframes_t internal_offset;
369         nframes_t buf_offset;
370         nframes_t to_read;
371         bool raw = (rops == ReadOpsNone);
372
373         if (muted() && !raw) {
374                 return 0; /* read nothing */
375         }
376
377         /* precondition: caller has verified that we cover the desired section */
378
379         if (position < _position) {
380                 internal_offset = 0;
381                 buf_offset = _position - position;
382                 cnt -= buf_offset;
383         } else {
384                 internal_offset = position - _position;
385                 buf_offset = 0;
386         }
387
388         if (internal_offset >= limit) {
389                 return 0; /* read nothing */
390         }
391
392         if ((to_read = min (cnt, limit - internal_offset)) == 0) {
393                 return 0; /* read nothing */
394         }
395
396         if (opaque() || raw) {
397                 /* overwrite whatever is there */
398                 mixdown_buffer = buf + buf_offset;
399         } else {
400                 mixdown_buffer += buf_offset;
401         }
402
403         if (rops & ReadOpsCount) {
404                 _read_data_count = 0;
405         }
406
407         if (chan_n < n_channels()) {
408
409                 boost::shared_ptr<AudioSource> src = audio_source(chan_n);
410                 if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
411                         return 0; /* "read nothing" */
412                 }
413                 
414                 if (rops & ReadOpsCount) {
415                         _read_data_count += src->read_data_count();
416                 }
417
418         } else {
419                 
420                 /* track is N-channel, this region has less channels; silence the ones
421                    we don't have.
422                 */
423
424                 memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
425         }
426
427         if (rops & ReadOpsFades) {
428         
429                 /* fade in */
430
431                 if ((_flags & FadeIn) && Config->get_use_region_fades()) {
432                         
433                         nframes_t fade_in_length = (nframes_t) _fade_in->back()->when;
434                         
435                         /* see if this read is within the fade in */
436                         
437                         if (internal_offset < fade_in_length) {
438                                 
439                                 nframes_t fi_limit;
440                                 
441                                 fi_limit = min (to_read, fade_in_length - internal_offset);
442                                 
443
444                                 _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
445                                 
446                                 for (nframes_t n = 0; n < fi_limit; ++n) {
447                                         mixdown_buffer[n] *= gain_buffer[n];
448                                 }
449                         }
450                 }
451                 
452                 /* fade out */
453                 
454                 if ((_flags & FadeOut) && Config->get_use_region_fades()) {
455
456                         /* see if some part of this read is within the fade out */
457                         
458                 /* .................        >|            REGION
459                                              limit
460                                             
461                                  {           }            FADE
462                                              fade_out_length
463                                  ^                                           
464                                  limit - fade_out_length
465                         |--------------|
466                         ^internal_offset
467                                        ^internal_offset + to_read
468                                        
469                                        we need the intersection of [internal_offset,internal_offset+to_read] with
470                                        [limit - fade_out_length, limit]
471                                        
472                 */
473
474         
475                         nframes_t fade_out_length = (nframes_t) _fade_out->back()->when;
476                         nframes_t fade_interval_start = max(internal_offset, limit-fade_out_length);
477                         nframes_t fade_interval_end   = min(internal_offset + to_read, limit);
478
479                         if (fade_interval_end > fade_interval_start) {
480                                 /* (part of the) the fade out is  in this buffer */
481
482                                 nframes_t fo_limit = fade_interval_end - fade_interval_start;
483                                 nframes_t curve_offset = fade_interval_start - (limit-fade_out_length);
484                                 nframes_t fade_offset = fade_interval_start - internal_offset;
485                                 
486                                 _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
487
488                                 for (nframes_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) {
489                                         mixdown_buffer[m] *= gain_buffer[n];
490                                 }
491                         } 
492                         
493                 }
494         }
495                 
496         /* Regular gain curves and scaling */
497         
498         if ((rops & ReadOpsOwnAutomation) && envelope_active())  {
499                 _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
500                         
501                 if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
502                         for (nframes_t n = 0; n < to_read; ++n) {
503                                 mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
504                         }
505                 } else {
506                         for (nframes_t n = 0; n < to_read; ++n) {
507                                 mixdown_buffer[n] *= gain_buffer[n];
508                         }
509                 }
510         } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
511
512                 // XXX this should be using what in 2.0 would have been: 
513                 // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
514
515                 for (nframes_t n = 0; n < to_read; ++n) {
516                         mixdown_buffer[n] *= _scale_amplitude;
517                 }
518         }
519         
520         if (!opaque()) {
521                 
522                 /* gack. the things we do for users.
523                  */
524                 
525                 buf += buf_offset;
526                         
527                 for (nframes_t n = 0; n < to_read; ++n) {
528                         buf[n] += mixdown_buffer[n];
529                 }
530         } 
531
532         return to_read;
533 }
534         
535 XMLNode&
536 AudioRegion::state (bool full)
537 {
538         XMLNode& node (Region::state (full));
539         XMLNode *child;
540         char buf[64];
541         char buf2[64];
542         LocaleGuard lg (X_("POSIX"));
543         
544         node.add_property ("flags", enum_2_string (_flags));
545
546         snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude);
547         node.add_property ("scale-gain", buf);
548
549         // XXX these should move into Region
550
551         for (uint32_t n=0; n < _sources.size(); ++n) {
552                 snprintf (buf2, sizeof(buf2), "source-%d", n);
553                 _sources[n]->id().print (buf, sizeof (buf));
554                 node.add_property (buf2, buf);
555         }
556
557         for (uint32_t n=0; n < _master_sources.size(); ++n) {
558                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
559                 _master_sources[n]->id().print (buf, sizeof (buf));
560                 node.add_property (buf2, buf);
561         }
562
563         snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
564         node.add_property ("channels", buf);
565
566         if (full) {
567         
568                 child = node.add_child (X_("FadeIn"));
569                 
570                 if ((_flags & DefaultFadeIn)) {
571                         child->add_property (X_("default"), X_("yes"));
572                 } else {
573                         child->add_child_nocopy (_fade_in->get_state ());
574                 }
575
576                 child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes"));
577                 
578                 child = node.add_child (X_("FadeOut"));
579                 
580                 if ((_flags & DefaultFadeOut)) {
581                         child->add_property (X_("default"), X_("yes"));
582                 } else {
583                         child->add_child_nocopy (_fade_out->get_state ());
584                 }
585                 
586                 child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes"));
587         }
588         
589         child = node.add_child ("Envelope");
590
591         if (full) {
592                 bool default_env = false;
593                 
594                 // If there are only two points, the points are in the start of the region and the end of the region
595                 // so, if they are both at 1.0f, that means the default region.
596
597                 if (_envelope->size() == 2 &&
598                     _envelope->front()->value == 1.0f &&
599                     _envelope->back()->value==1.0f) {
600                         if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
601                                 default_env = true;
602                         }
603                 } 
604
605                 if (default_env) {
606                         child->add_property ("default", "yes");
607                 } else {
608                         child->add_child_nocopy (_envelope->get_state ());
609                 }
610
611         } else {
612                 child->add_property ("default", "yes");
613         }
614
615         if (full && _extra_xml) {
616                 node.add_child_copy (*_extra_xml);
617         }
618
619         return node;
620 }
621
622 int
623 AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
624 {
625         const XMLNodeList& nlist = node.children();
626         const XMLProperty *prop;
627         LocaleGuard lg (X_("POSIX"));
628
629         Region::set_live_state (node, what_changed, false);
630
631         uint32_t old_flags = _flags;
632
633         if ((prop = node.property ("flags")) != 0) {
634                 _flags = Flag (string_2_enum (prop->value(), _flags));
635
636                 //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
637
638                 _flags = Flag (_flags & ~Region::LeftOfSplit);
639                 _flags = Flag (_flags & ~Region::RightOfSplit);
640         }
641
642         if ((old_flags ^ _flags) & Muted) {
643                 what_changed = Change (what_changed|MuteChanged);
644         }
645         if ((old_flags ^ _flags) & Opaque) {
646                 what_changed = Change (what_changed|OpacityChanged);
647         }
648         if ((old_flags ^ _flags) & Locked) {
649                 what_changed = Change (what_changed|LockChanged);
650         }
651
652         if ((prop = node.property ("scale-gain")) != 0) {
653                 _scale_amplitude = atof (prop->value().c_str());
654                 what_changed = Change (what_changed|ScaleAmplitudeChanged);
655         } else {
656                 _scale_amplitude = 1.0;
657         }
658         
659         /* Now find envelope description and other misc child items */
660                                 
661         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
662                 
663                 XMLNode *child;
664                 XMLProperty *prop;
665                 
666                 child = (*niter);
667                 
668                 if (child->name() == "Envelope") {
669
670                         _envelope->clear ();
671
672                         if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child)) {
673                                 set_default_envelope ();
674                         }
675
676                         _envelope->set_max_xval (_length);
677                         _envelope->truncate_end (_length);
678
679                 } else if (child->name() == "FadeIn") {
680                         
681                         _fade_in->clear ();
682                         
683                         if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
684                                 set_default_fade_in ();
685                         } else {
686                                 XMLNode* grandchild = child->child ("AutomationList");
687                                 if (grandchild) {
688                                         _fade_in->set_state (*grandchild);
689                                 }
690                         }
691
692                         if ((prop = child->property ("active")) != 0) {
693                                 if (prop->value() == "yes") {
694                                         set_fade_in_active (true);
695                                 } else {
696                                         set_fade_in_active (true);
697                                 }
698                         }
699
700                 } else if (child->name() == "FadeOut") {
701                         
702                         _fade_out->clear ();
703
704                         if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
705                                 set_default_fade_out ();
706                         } else {
707                                 XMLNode* grandchild = child->child ("AutomationList");
708                                 if (grandchild) {
709                                         _fade_out->set_state (*grandchild);
710                                 } 
711                         }
712
713                         if ((prop = child->property ("active")) != 0) {
714                                 if (prop->value() == "yes") {
715                                         set_fade_out_active (true);
716                                 } else {
717                                         set_fade_out_active (false);
718                                 }
719                         }
720
721                 } 
722         }
723
724         if (send) {
725                 send_change (what_changed);
726         }
727
728         return 0;
729 }
730
731 int
732 AudioRegion::set_state (const XMLNode& node)
733 {
734         /* Region::set_state() calls the virtual set_live_state(),
735            which will get us back to AudioRegion::set_live_state()
736            to handle the relevant stuff.
737         */
738
739         return Region::set_state (node);
740 }
741
742 void
743 AudioRegion::set_fade_in_shape (FadeShape shape)
744 {
745         set_fade_in (shape, (nframes_t) _fade_in->back()->when);
746 }
747
748 void
749 AudioRegion::set_fade_out_shape (FadeShape shape)
750 {
751         set_fade_out (shape, (nframes_t) _fade_out->back()->when);
752 }
753
754 void
755 AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
756 {
757         _fade_in->freeze ();
758         _fade_in->clear ();
759
760         switch (shape) {
761         case Linear:
762                 _fade_in->fast_simple_add (0.0, 0.0);
763                 _fade_in->fast_simple_add (len, 1.0);
764                 break;
765
766         case Fast:
767                 _fade_in->fast_simple_add (0, 0);
768                 _fade_in->fast_simple_add (len * 0.389401, 0.0333333);
769                 _fade_in->fast_simple_add (len * 0.629032, 0.0861111);
770                 _fade_in->fast_simple_add (len * 0.829493, 0.233333);
771                 _fade_in->fast_simple_add (len * 0.9447, 0.483333);
772                 _fade_in->fast_simple_add (len * 0.976959, 0.697222);
773                 _fade_in->fast_simple_add (len, 1);
774                 break;
775
776         case Slow:
777                 _fade_in->fast_simple_add (0, 0);
778                 _fade_in->fast_simple_add (len * 0.0207373, 0.197222);
779                 _fade_in->fast_simple_add (len * 0.0645161, 0.525);
780                 _fade_in->fast_simple_add (len * 0.152074, 0.802778);
781                 _fade_in->fast_simple_add (len * 0.276498, 0.919444);
782                 _fade_in->fast_simple_add (len * 0.481567, 0.980556);
783                 _fade_in->fast_simple_add (len * 0.767281, 1);
784                 _fade_in->fast_simple_add (len, 1);
785                 break;
786
787         case LogA:
788                 _fade_in->fast_simple_add (0, 0);
789                 _fade_in->fast_simple_add (len * 0.0737327, 0.308333);
790                 _fade_in->fast_simple_add (len * 0.246544, 0.658333);
791                 _fade_in->fast_simple_add (len * 0.470046, 0.886111);
792                 _fade_in->fast_simple_add (len * 0.652074, 0.972222);
793                 _fade_in->fast_simple_add (len * 0.771889, 0.988889);
794                 _fade_in->fast_simple_add (len, 1);
795                 break;
796
797         case LogB:
798                 _fade_in->fast_simple_add (0, 0);
799                 _fade_in->fast_simple_add (len * 0.304147, 0.0694444);
800                 _fade_in->fast_simple_add (len * 0.529954, 0.152778);
801                 _fade_in->fast_simple_add (len * 0.725806, 0.333333);
802                 _fade_in->fast_simple_add (len * 0.847926, 0.558333);
803                 _fade_in->fast_simple_add (len * 0.919355, 0.730556);
804                 _fade_in->fast_simple_add (len, 1);
805                 break;
806         }
807
808         _fade_in->thaw ();
809         _fade_in_shape = shape;
810
811         send_change (FadeInChanged);
812 }
813
814 void
815 AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
816 {
817         _fade_out->freeze ();
818         _fade_out->clear ();
819
820         switch (shape) {
821         case Fast:
822                 _fade_out->fast_simple_add (len * 0, 1);
823                 _fade_out->fast_simple_add (len * 0.023041, 0.697222);
824                 _fade_out->fast_simple_add (len * 0.0553,   0.483333);
825                 _fade_out->fast_simple_add (len * 0.170507, 0.233333);
826                 _fade_out->fast_simple_add (len * 0.370968, 0.0861111);
827                 _fade_out->fast_simple_add (len * 0.610599, 0.0333333);
828                 _fade_out->fast_simple_add (len * 1, 0);
829                 break;
830
831         case LogA:
832                 _fade_out->fast_simple_add (len * 0, 1);
833                 _fade_out->fast_simple_add (len * 0.228111, 0.988889);
834                 _fade_out->fast_simple_add (len * 0.347926, 0.972222);
835                 _fade_out->fast_simple_add (len * 0.529954, 0.886111);
836                 _fade_out->fast_simple_add (len * 0.753456, 0.658333);
837                 _fade_out->fast_simple_add (len * 0.9262673, 0.308333);
838                 _fade_out->fast_simple_add (len * 1, 0);
839                 break;
840
841         case Slow:
842                 _fade_out->fast_simple_add (len * 0, 1);
843                 _fade_out->fast_simple_add (len * 0.305556, 1);
844                 _fade_out->fast_simple_add (len * 0.548611, 0.991736);
845                 _fade_out->fast_simple_add (len * 0.759259, 0.931129);
846                 _fade_out->fast_simple_add (len * 0.918981, 0.68595);
847                 _fade_out->fast_simple_add (len * 0.976852, 0.22865);
848                 _fade_out->fast_simple_add (len * 1, 0);
849                 break;
850
851         case LogB:
852                 _fade_out->fast_simple_add (len * 0, 1);
853                 _fade_out->fast_simple_add (len * 0.080645, 0.730556);
854                 _fade_out->fast_simple_add (len * 0.277778, 0.289256);
855                 _fade_out->fast_simple_add (len * 0.470046, 0.152778);
856                 _fade_out->fast_simple_add (len * 0.695853, 0.0694444);
857                 _fade_out->fast_simple_add (len * 1, 0);
858                 break;
859
860         case Linear:
861                 _fade_out->fast_simple_add (len * 0, 1);
862                 _fade_out->fast_simple_add (len * 1, 0);
863                 break;
864         }
865
866         _fade_out->thaw ();
867         _fade_out_shape = shape;
868
869         send_change (FadeOutChanged);
870 }
871
872 void
873 AudioRegion::set_fade_in_length (nframes_t len)
874 {
875         if (len > _length) {
876                 len = _length - 1;
877         }
878
879         bool changed = _fade_in->extend_to (len);
880
881         if (changed) {
882                 _flags = Flag (_flags & ~DefaultFadeIn);
883                 send_change (FadeInChanged);
884         }
885 }
886
887 void
888 AudioRegion::set_fade_out_length (nframes_t len)
889 {
890         if (len > _length) {
891                 len = _length - 1;
892         }
893
894         bool changed =  _fade_out->extend_to (len);
895
896         if (changed) {
897                 _flags = Flag (_flags & ~DefaultFadeOut);
898                 send_change (FadeOutChanged);
899         }
900 }
901
902 void
903 AudioRegion::set_fade_in_active (bool yn)
904 {
905         if (yn == (_flags & FadeIn)) {
906                 return;
907         }
908         if (yn) {
909                 _flags = Flag (_flags|FadeIn);
910         } else {
911                 _flags = Flag (_flags & ~FadeIn);
912         }
913
914         send_change (FadeInActiveChanged);
915 }
916
917 void
918 AudioRegion::set_fade_out_active (bool yn)
919 {
920         if (yn == (_flags & FadeOut)) {
921                 return;
922         }
923         if (yn) {
924                 _flags = Flag (_flags | FadeOut);
925         } else {
926                 _flags = Flag (_flags & ~FadeOut);
927         }
928
929         send_change (FadeOutActiveChanged);
930 }
931
932 bool
933 AudioRegion::fade_in_is_default () const
934 {
935         return _fade_in_shape == Linear && _fade_in->back()->when == 64;
936 }
937
938 bool
939 AudioRegion::fade_out_is_default () const
940 {
941         return _fade_out_shape == Linear && _fade_out->back()->when == 64;
942 }
943
944 void
945 AudioRegion::set_default_fade_in ()
946 {
947         set_fade_in (Linear, 64);
948 }
949
950 void
951 AudioRegion::set_default_fade_out ()
952 {
953         set_fade_out (Linear, 64);
954 }
955
956 void
957 AudioRegion::set_default_fades ()
958 {
959         _fade_in_disabled = 0;
960         _fade_out_disabled = 0;
961         set_default_fade_in ();
962         set_default_fade_out ();
963 }
964
965 void
966 AudioRegion::set_default_envelope ()
967 {
968         _envelope->freeze ();
969         _envelope->clear ();
970         _envelope->fast_simple_add (0, 1.0f);
971         _envelope->fast_simple_add (_length, 1.0f);
972         _envelope->thaw ();
973 }
974
975 void
976 AudioRegion::recompute_at_end ()
977 {
978         /* our length has changed. recompute a new final point by interpolating 
979            based on the the existing curve.
980         */
981         
982         _envelope->freeze ();
983         _envelope->truncate_end (_length);
984         _envelope->set_max_xval (_length);
985         _envelope->thaw ();
986
987         if (_fade_in->back()->when > _length) {
988                 _fade_in->extend_to (_length);
989                 send_change (FadeInChanged);
990         }
991
992         if (_fade_out->back()->when > _length) {
993                 _fade_out->extend_to (_length);
994                 send_change (FadeOutChanged);
995         }
996 }       
997
998 void
999 AudioRegion::recompute_at_start ()
1000 {
1001         /* as above, but the shift was from the front */
1002
1003         _envelope->truncate_start (_length);
1004
1005         if (_fade_in->back()->when > _length) {
1006                 _fade_in->extend_to (_length);
1007                 send_change (FadeInChanged);
1008         }
1009
1010         if (_fade_out->back()->when > _length) {
1011                 _fade_out->extend_to (_length);
1012                 send_change (FadeOutChanged);
1013         }
1014 }
1015
1016 int
1017 AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<AudioRegion> >& v) const
1018 {
1019         SourceList srcs;
1020         string new_name;
1021         int n;
1022
1023         if (_sources.size() < 2) {
1024                 return 0;
1025         }
1026
1027         n = 0;
1028
1029         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1030
1031                 srcs.clear ();
1032                 srcs.push_back (*i);
1033
1034                 new_name = _name;
1035
1036                 if (_sources.size() == 2) {
1037                         if (n == 0) {
1038                                 new_name += "-L";
1039                         } else {
1040                                 new_name += "-R";
1041                         }
1042                 } else {
1043                         new_name += '-';
1044                         new_name += ('0' + n + 1);
1045                 }
1046
1047                 /* create a copy with just one source. prevent if from being thought of as "whole file" even if 
1048                    it covers the entire source file(s).
1049                  */
1050
1051                 Flag f = Flag (_flags & ~WholeFile);
1052
1053                 boost::shared_ptr<Region> r = RegionFactory::create (srcs, _start, _length, new_name, _layer, f);
1054                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1055
1056                 v.push_back (ar);
1057                 
1058                 ++n;
1059         }
1060
1061         return 0;
1062 }
1063
1064 nframes_t
1065 AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const
1066 {
1067         return audio_source()->read  (buf, pos, cnt);
1068 }
1069
1070 int
1071 AudioRegion::exportme (Session& session, ARDOUR::ExportSpecification& spec)
1072 {
1073         // TODO EXPORT
1074 //      const nframes_t blocksize = 4096;
1075 //      nframes_t to_read;
1076 //      int status = -1;
1077 // 
1078 //      spec.channels = _sources.size();
1079 // 
1080 //      if (spec.prepare (blocksize, session.frame_rate())) {
1081 //              goto out;
1082 //      }
1083 // 
1084 //      spec.pos = 0;
1085 //      spec.total_frames = _length;
1086 // 
1087 //      while (spec.pos < _length && !spec.stop) {
1088 //              
1089 //              
1090 //              /* step 1: interleave */
1091 //              
1092 //              to_read = min (_length - spec.pos, blocksize);
1093 //              
1094 //              if (spec.channels == 1) {
1095 // 
1096 //                      if (read_raw_internal (spec.dataF, _start + spec.pos, to_read) != to_read) {
1097 //                              goto out;
1098 //                      }
1099 // 
1100 //              } else {
1101 // 
1102 //                      Sample buf[blocksize];
1103 // 
1104 //                      for (uint32_t chan = 0; chan < spec.channels; ++chan) {
1105 //                              
1106 //                              if (audio_source(chan)->read (buf, _start + spec.pos, to_read) != to_read) {
1107 //                                      goto out;
1108 //                              }
1109 //                              
1110 //                              for (nframes_t x = 0; x < to_read; ++x) {
1111 //                                      spec.dataF[chan+(x*spec.channels)] = buf[x];
1112 //                              }
1113 //                      }
1114 //              }
1115 //              
1116 //              if (spec.process (to_read)) {
1117 //                      goto out;
1118 //              }
1119 //              
1120 //              spec.pos += to_read;
1121 //              spec.progress = (double) spec.pos /_length;
1122 //              
1123 //      }
1124 //      
1125 //      status = 0;
1126 // 
1127 //   out:       
1128 //      spec.running = false;
1129 //      spec.status = status;
1130 //      spec.clear();
1131 //      
1132 //      return status;
1133         return 0;
1134 }
1135
1136 void
1137 AudioRegion::set_scale_amplitude (gain_t g)
1138 {
1139         boost::shared_ptr<Playlist> pl (playlist());
1140
1141         _scale_amplitude = g;
1142
1143         /* tell the diskstream we're in */
1144         
1145         if (pl) {
1146                 pl->Modified();
1147         }
1148
1149         /* tell everybody else */
1150
1151         send_change (ScaleAmplitudeChanged);
1152 }
1153
1154 void
1155 AudioRegion::normalize_to (float target_dB)
1156 {
1157         const nframes_t blocksize = 64 * 1024;
1158         Sample buf[blocksize];
1159         nframes_t fpos;
1160         nframes_t fend;
1161         nframes_t to_read;
1162         double maxamp = 0;
1163         gain_t target = dB_to_coefficient (target_dB);
1164
1165         if (target == 1.0f) {
1166                 /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
1167                    that we may have clipped.
1168                 */
1169                 target -= FLT_EPSILON;
1170         }
1171
1172         fpos = _start;
1173         fend = _start + _length;
1174
1175         /* first pass: find max amplitude */
1176
1177         while (fpos < fend) {
1178
1179                 uint32_t n;
1180
1181                 to_read = min (fend - fpos, blocksize);
1182
1183                 for (n = 0; n < n_channels(); ++n) {
1184
1185                         /* read it in */
1186
1187                         if (read_raw_internal (buf, fpos, to_read) != to_read) {
1188                                 return;
1189                         }
1190                         
1191                         maxamp = compute_peak (buf, to_read, maxamp);
1192                 }
1193
1194                 fpos += to_read;
1195         };
1196
1197         if (maxamp == 0.0f) {
1198                 /* don't even try */
1199                 return;
1200         }
1201
1202         if (maxamp == target) {
1203                 /* we can't do anything useful */
1204                 return;
1205         }
1206
1207         /* compute scale factor */
1208
1209         _scale_amplitude = target/maxamp;
1210
1211         /* tell the diskstream we're in */
1212
1213         boost::shared_ptr<Playlist> pl (playlist());
1214
1215         if (pl) {
1216                 pl->Modified();
1217         }
1218
1219         /* tell everybody else */
1220
1221         send_change (ScaleAmplitudeChanged);
1222 }
1223
1224 void
1225 AudioRegion::fade_in_changed ()
1226 {
1227         send_change (FadeInChanged);
1228 }
1229
1230 void
1231 AudioRegion::fade_out_changed ()
1232 {
1233         send_change (FadeOutChanged);
1234 }
1235
1236 void
1237 AudioRegion::envelope_changed ()
1238 {
1239         send_change (EnvelopeChanged);
1240 }
1241
1242 void
1243 AudioRegion::suspend_fade_in ()
1244 {
1245         if (++_fade_in_disabled == 1) {
1246                 if (fade_in_is_default()) {
1247                         set_fade_in_active (false);
1248                 }
1249         }
1250 }
1251
1252 void
1253 AudioRegion::resume_fade_in ()
1254 {
1255         if (--_fade_in_disabled == 0 && _fade_in_disabled) {
1256                 set_fade_in_active (true);
1257         }
1258 }
1259
1260 void
1261 AudioRegion::suspend_fade_out ()
1262 {
1263         if (++_fade_out_disabled == 1) {
1264                 if (fade_out_is_default()) {
1265                         set_fade_out_active (false);
1266                 }
1267         }
1268 }
1269
1270 void
1271 AudioRegion::resume_fade_out ()
1272 {
1273         if (--_fade_out_disabled == 0 &&_fade_out_disabled) {
1274                 set_fade_out_active (true);
1275         }
1276 }
1277
1278 bool
1279 AudioRegion::speed_mismatch (float sr) const
1280 {
1281         if (_sources.empty()) {
1282                 /* impossible, but ... */
1283                 return false;
1284         }
1285
1286         float fsr = audio_source()->sample_rate();
1287
1288         return fsr != sr;
1289 }
1290
1291 void
1292 AudioRegion::source_offset_changed ()
1293 {
1294         /* XXX this fixes a crash that should not occur. It does occur
1295            becauses regions are not being deleted when a session
1296            is unloaded. That bug must be fixed.
1297         */
1298
1299         if (_sources.empty()) {
1300                 return;
1301         }
1302
1303         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(_sources.front());
1304
1305         if (afs && afs->destructive()) {
1306                 // set_start (source()->natural_position(), this);
1307                 set_position (source()->natural_position(), this);
1308         } 
1309 }
1310
1311 boost::shared_ptr<AudioSource>
1312 AudioRegion::audio_source (uint32_t n) const
1313 {
1314         // Guaranteed to succeed (use a static cast for speed?)
1315         return boost::dynamic_pointer_cast<AudioSource>(source(n));
1316 }
1317
1318 int
1319 AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
1320 {
1321         boost::shared_ptr<Playlist> pl = playlist();
1322
1323         if (!pl) {
1324                 return -1;
1325         }
1326
1327         if (_valid_transients && !force_new) {
1328                 results = _transients;
1329                 return 0;
1330         }
1331
1332         SourceList::iterator s;
1333         
1334         for (s = _sources.begin() ; s != _sources.end(); ++s) {
1335                 if (!(*s)->has_been_analysed()) {
1336                         cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
1337                         break;
1338                 }
1339         }
1340         
1341         if (s == _sources.end()) {
1342                 /* all sources are analyzed, merge data from each one */
1343
1344                 for (s = _sources.begin() ; s != _sources.end(); ++s) {
1345
1346                         /* find the set of transients within the bounds of this region */
1347
1348                         AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(),
1349                                                                          (*s)->transients.end(),
1350                                                                          _start);
1351
1352                         AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
1353                                                                           (*s)->transients.end(),
1354                                                                           _start + _length);
1355                                                                          
1356                         /* and add them */
1357
1358                         results.insert (results.end(), low, high);
1359                 }
1360
1361                 TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
1362                 
1363                 /* translate all transients to current position */
1364
1365                 for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) {
1366                         (*x) -= _start;
1367                         (*x) += _position;
1368                 }
1369
1370                 _transients = results;
1371                 _valid_transients = true;
1372
1373                 return 0;
1374         }
1375
1376         /* no existing/complete transient info */
1377
1378         if (!Config->get_auto_analyse_audio()) {
1379                 pl->session().Dialog (_("\
1380 You have requested an operation that requires audio analysis.\n\n\
1381 You currently have \"auto-analyse-audio\" disabled, which means\n\
1382 that transient data must be generated every time it is required.\n\n\
1383 If you are doing work that will require transient data on a\n\
1384 regular basis, you should probably enable \"auto-analyse-audio\"\n\
1385 then quit ardour and restart."));
1386         }
1387
1388         TransientDetector t (pl->session().frame_rate());
1389         bool existing_results = !results.empty();
1390
1391         _transients.clear ();
1392         _valid_transients = false;
1393
1394         for (uint32_t i = 0; i < n_channels(); ++i) {
1395
1396                 AnalysisFeatureList these_results;
1397
1398                 t.reset ();
1399
1400                 if (t.run ("", this, i, these_results)) {
1401                         return -1;
1402                 }
1403
1404                 /* translate all transients to give absolute position */
1405                 
1406                 for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) {
1407                         (*i) += _position;
1408                 }
1409
1410                 /* merge */
1411                 
1412                 _transients.insert (_transients.end(), these_results.begin(), these_results.end());
1413         }
1414         
1415         if (!results.empty()) {
1416                 if (existing_results) {
1417                         
1418                         /* merge our transients into the existing ones, then clean up
1419                            those.
1420                         */
1421
1422                         results.insert (results.end(), _transients.begin(), _transients.end());
1423                         TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
1424                 }
1425
1426                 /* make sure ours are clean too */
1427
1428                 TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
1429
1430         } else {
1431
1432                 TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
1433                 results = _transients;
1434         }
1435
1436         _valid_transients = true;
1437
1438         return 0;
1439 }
1440
1441 extern "C" {
1442
1443         int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t cnt, intptr_t data, uint32_t n_chan, double samples_per_unit) 
1444 {
1445         return ((AudioRegion *) arg)->read_peaks ((PeakData *) data, (nframes_t) npeaks, (nframes_t) start, (nframes_t) cnt, n_chan,samples_per_unit);
1446 }
1447
1448 uint32_t region_length_from_c (void *arg)
1449 {
1450
1451         return ((AudioRegion *) arg)->length();
1452 }
1453
1454 uint32_t sourcefile_length_from_c (void *arg, double zoom_factor)
1455 {
1456         return ( (AudioRegion *) arg)->audio_source()->available_peaks (zoom_factor) ;
1457 }
1458
1459 } /* extern "C" */