Make source length a dynamic thing.
[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 <evoral/Curve.hpp>
39
40 #include <ardour/audioregion.h>
41 #include <ardour/session.h>
42 #include <ardour/gain.h>
43 #include <ardour/dB.h>
44 #include <ardour/playlist.h>
45 #include <ardour/audiofilesource.h>
46 #include <ardour/region_factory.h>
47 #include <ardour/runtime_functions.h>
48 #include <ardour/transient_detector.h>
49
50 #include "i18n.h"
51 #include <locale.h>
52
53 using namespace std;
54 using namespace ARDOUR;
55 using namespace PBD;
56
57 /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
58
59 Change AudioRegion::FadeInChanged         = ARDOUR::new_change();
60 Change AudioRegion::FadeOutChanged        = ARDOUR::new_change();
61 Change AudioRegion::FadeInActiveChanged   = ARDOUR::new_change();
62 Change AudioRegion::FadeOutActiveChanged  = ARDOUR::new_change();
63 Change AudioRegion::EnvelopeActiveChanged = ARDOUR::new_change();
64 Change AudioRegion::ScaleAmplitudeChanged = ARDOUR::new_change();
65 Change AudioRegion::EnvelopeChanged       = ARDOUR::new_change();
66
67 void
68 AudioRegion::init ()
69 {
70         _scale_amplitude = 1.0;
71
72         set_default_fades ();
73         set_default_envelope ();
74
75         listen_to_my_curves ();
76         listen_to_my_sources ();
77 }
78
79 /** Constructor for use by derived types only */
80 AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name)
81         : Region (s, start, length, name, DataType::AUDIO)
82         , _automatable(s)
83         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
84         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
85         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
86 {
87         init ();
88 }
89
90 /** Basic AudioRegion constructor (one channel) */
91 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
92         : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0,  Region::Flag(Region::DefaultFlags|Region::External))
93         , _automatable(src->session())
94         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
95         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
96         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
97 {
98         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
99         if (afs) {
100                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
101         }
102
103         init ();
104 }
105
106 /* Basic AudioRegion constructor (one channel) */
107 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
108         : Region (src, start, length, name, DataType::AUDIO, layer, flags)
109         , _automatable(src->session())
110         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
111         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
112         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
113 {
114         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
115         if (afs) {
116                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
117         }
118
119         init ();
120 }
121
122 /** Basic AudioRegion constructor (many channels) */
123 AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
124         : Region (srcs, start, length, name, DataType::AUDIO, layer, flags)
125         , _automatable(srcs[0]->session())
126         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
127         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
128         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
129 {
130         init ();
131         listen_to_my_sources ();
132 }
133
134 /** Create a new AudioRegion, that is part of an existing one */
135 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
136         : Region (other, offset, length, name, layer, flags)
137         , _automatable(other->session())
138         , _fade_in (new AutomationList(*other->_fade_in))
139         , _fade_out (new AutomationList(*other->_fade_out))
140         , _envelope (new AutomationList(*other->_envelope, offset, offset + length))
141 {
142         set<boost::shared_ptr<Source> > unique_srcs;
143
144         for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
145                 _sources.push_back (*i);
146
147                 pair<set<boost::shared_ptr<Source> >::iterator,bool> result;
148
149                 result = unique_srcs.insert (*i);
150                 
151                 if (result.second) {
152                         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
153                         if (afs) {
154                                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
155                         }
156                 }
157         }
158
159         /* return to default fades if the existing ones are too long */
160
161         if (_flags & LeftOfSplit) {
162                 if (_fade_in->back()->when >= _length) {
163                         set_default_fade_in ();
164                 } else {
165                         _fade_in_disabled = other->_fade_in_disabled;
166                 }
167                 set_default_fade_out ();
168                 _flags = Flag (_flags & ~Region::LeftOfSplit);
169         }
170
171         if (_flags & RightOfSplit) {
172                 if (_fade_out->back()->when >= _length) {
173                         set_default_fade_out ();
174                 } else {
175                         _fade_out_disabled = other->_fade_out_disabled;
176                 }
177                 set_default_fade_in ();
178                 _flags = Flag (_flags & ~Region::RightOfSplit);
179         }
180
181         _scale_amplitude = other->_scale_amplitude;
182
183         assert(_type == DataType::AUDIO);
184         
185         listen_to_my_curves ();
186         listen_to_my_sources ();
187 }
188
189 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
190         : Region (other)
191         , _automatable (other->session())
192         , _fade_in (new AutomationList (*other->_fade_in))
193         , _fade_out (new AutomationList (*other->_fade_out))
194         , _envelope (new AutomationList (*other->_envelope))
195 {
196         assert(_type == DataType::AUDIO);
197         _scale_amplitude = other->_scale_amplitude;
198
199         set_default_fades ();
200
201         listen_to_my_curves ();
202         listen_to_my_sources ();
203 }
204
205 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const SourceList& srcs,
206                           nframes_t length, const string& name, layer_t layer, Flag flags)
207         : Region (other, length, name, layer, flags)
208         , _automatable (other->session())
209         , _fade_in (new AutomationList (*other->_fade_in))
210         , _fade_out (new AutomationList (*other->_fade_out))
211         , _envelope (new AutomationList (*other->_envelope))
212 {
213         /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */
214
215         set<boost::shared_ptr<AudioSource> > unique_srcs;
216
217         for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
218
219                 _sources.push_back (*i);
220                 _master_sources.push_back (*i);
221
222                 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> ((*i));
223                 if (afs) {
224                         afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
225                 }
226         }
227
228         _scale_amplitude = other->_scale_amplitude;
229
230         _fade_in_disabled = 0;
231         _fade_out_disabled = 0;
232
233         listen_to_my_curves ();
234         listen_to_my_sources ();
235 }
236
237 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
238         : Region (src, node)
239         , _automatable(src->session())
240         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
241         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
242         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
243 {
244         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
245         if (afs) {
246                 afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
247         }
248
249         init ();
250
251         if (set_state (node)) {
252                 throw failed_constructor();
253         }
254
255         assert(_type == DataType::AUDIO);
256         listen_to_my_sources ();
257 }
258
259 AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
260         : Region (srcs, node)
261         , _automatable(srcs[0]->session())
262         , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
263         , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
264         , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
265 {
266         init ();
267
268         if (set_state (node)) {
269                 throw failed_constructor();
270         }
271
272         assert(_type == DataType::AUDIO);
273         listen_to_my_sources ();
274 }
275
276 AudioRegion::~AudioRegion ()
277 {
278 }
279
280 void
281 AudioRegion::listen_to_my_sources ()
282 {
283         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
284                 (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients));
285         }
286 }
287
288 void
289 AudioRegion::listen_to_my_curves ()
290 {
291         _envelope->StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
292         _fade_in->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_in_changed));
293         _fade_out->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_out_changed));
294 }
295
296 void
297 AudioRegion::set_envelope_active (bool yn)
298 {
299         if (envelope_active() != yn) {
300                 char buf[64];
301                 if (yn) {
302                         snprintf (buf, sizeof (buf), "envelope active");
303                         _flags = Flag (_flags|EnvelopeActive);
304                 } else {
305                         snprintf (buf, sizeof (buf), "envelope off");
306                         _flags = Flag (_flags & ~EnvelopeActive);
307                 }
308                 send_change (EnvelopeActiveChanged);
309         }
310 }
311
312 ARDOUR::nframes_t
313 AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nframes_t cnt, uint32_t chan_n, double samples_per_unit) const
314 {
315         if (chan_n >= _sources.size()) {
316                 return 0; 
317         }
318
319         if (audio_source(chan_n)->read_peaks (buf, npeaks, offset, cnt, samples_per_unit)) {
320                 return 0;
321         } else {
322                 if (_scale_amplitude != 1.0) {
323                         for (nframes_t n = 0; n < npeaks; ++n) {
324                                 buf[n].max *= _scale_amplitude;
325                                 buf[n].min *= _scale_amplitude;
326                         }
327                 }
328                 return cnt;
329         }
330 }
331
332 nframes_t
333 AudioRegion::read (Sample* buf, sframes_t timeline_position, nframes_t cnt, int channel) const
334 {
335         /* raw read, no fades, no gain, nada */
336         return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
337 }
338
339 nframes_t
340 AudioRegion::read_with_ops (Sample* buf, sframes_t file_position, nframes_t cnt, int channel, ReadOps rops) const
341 {
342         return _read_at (_sources, _length, buf, 0, 0, file_position, cnt, channel, 0, 0, rops);
343 }
344
345 nframes_t
346 AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
347                 sframes_t file_position, nframes_t cnt, uint32_t chan_n,
348                 nframes_t read_frames, nframes_t skip_frames) const
349 {
350         /* regular diskstream/butler read complete with fades etc */
351         return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer,
352                         file_position, cnt, chan_n, read_frames, skip_frames, ReadOps (~0));
353 }
354
355 nframes_t
356 AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
357                 sframes_t position, nframes_t cnt, uint32_t chan_n) const
358 {
359         return _read_at (_master_sources,
360                         _master_sources.front()->length(_master_sources.front()->timeline_position()),
361                         buf, mixdown_buffer, gain_buffer,
362                         position, cnt, chan_n, 0, 0);
363 }
364
365 nframes_t
366 AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
367                 Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
368                 sframes_t position, nframes_t cnt, 
369                 uint32_t chan_n, 
370                 nframes_t read_frames, 
371                 nframes_t skip_frames,
372                 ReadOps rops) const
373 {
374         nframes_t internal_offset;
375         nframes_t buf_offset;
376         nframes_t to_read;
377         bool raw = (rops == ReadOpsNone);
378
379         if (muted() && !raw) {
380                 return 0; /* read nothing */
381         }
382
383         /* precondition: caller has verified that we cover the desired section */
384
385         if (position < _position) {
386                 internal_offset = 0;
387                 buf_offset = _position - position;
388                 cnt -= buf_offset;
389         } else {
390                 internal_offset = position - _position;
391                 buf_offset = 0;
392         }
393
394         if (internal_offset >= limit) {
395                 return 0; /* read nothing */
396         }
397
398         if ((to_read = min (cnt, limit - internal_offset)) == 0) {
399                 return 0; /* read nothing */
400         }
401
402         if (opaque() || raw) {
403                 /* overwrite whatever is there */
404                 mixdown_buffer = buf + buf_offset;
405         } else {
406                 mixdown_buffer += buf_offset;
407         }
408
409         if (rops & ReadOpsCount) {
410                 _read_data_count = 0;
411         }
412
413         if (chan_n < n_channels()) {
414
415                 boost::shared_ptr<AudioSource> src = audio_source(chan_n);
416                 if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
417                         return 0; /* "read nothing" */
418                 }
419                 
420                 if (rops & ReadOpsCount) {
421                         _read_data_count += src->read_data_count();
422                 }
423
424         } else {
425                 
426                 /* track is N-channel, this region has less channels; silence the ones
427                    we don't have.
428                 */
429
430                 memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
431         }
432
433         if (rops & ReadOpsFades) {
434         
435                 /* fade in */
436
437                 if ((_flags & FadeIn) && Config->get_use_region_fades()) {
438                         
439                         nframes_t fade_in_length = (nframes_t) _fade_in->back()->when;
440                         
441                         /* see if this read is within the fade in */
442                         
443                         if (internal_offset < fade_in_length) {
444                                 
445                                 nframes_t fi_limit;
446                                 
447                                 fi_limit = min (to_read, fade_in_length - internal_offset);
448                                 
449
450                                 _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
451                                 
452                                 for (nframes_t n = 0; n < fi_limit; ++n) {
453                                         mixdown_buffer[n] *= gain_buffer[n];
454                                 }
455                         }
456                 }
457                 
458                 /* fade out */
459                 
460                 if ((_flags & FadeOut) && Config->get_use_region_fades()) {
461
462                         /* see if some part of this read is within the fade out */
463                         
464                 /* .................        >|            REGION
465                                              limit
466                                             
467                                  {           }            FADE
468                                              fade_out_length
469                                  ^                                           
470                                  limit - fade_out_length
471                         |--------------|
472                         ^internal_offset
473                                        ^internal_offset + to_read
474                                        
475                                        we need the intersection of [internal_offset,internal_offset+to_read] with
476                                        [limit - fade_out_length, limit]
477                                        
478                 */
479
480         
481                         nframes_t fade_out_length = (nframes_t) _fade_out->back()->when;
482                         nframes_t fade_interval_start = max(internal_offset, limit-fade_out_length);
483                         nframes_t fade_interval_end   = min(internal_offset + to_read, limit);
484
485                         if (fade_interval_end > fade_interval_start) {
486                                 /* (part of the) the fade out is  in this buffer */
487
488                                 nframes_t fo_limit = fade_interval_end - fade_interval_start;
489                                 nframes_t curve_offset = fade_interval_start - (limit-fade_out_length);
490                                 nframes_t fade_offset = fade_interval_start - internal_offset;
491                                 
492                                 _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
493
494                                 for (nframes_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) {
495                                         mixdown_buffer[m] *= gain_buffer[n];
496                                 }
497                         } 
498                         
499                 }
500         }
501                 
502         /* Regular gain curves and scaling */
503         
504         if ((rops & ReadOpsOwnAutomation) && envelope_active())  {
505                 _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
506                         
507                 if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
508                         for (nframes_t n = 0; n < to_read; ++n) {
509                                 mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
510                         }
511                 } else {
512                         for (nframes_t n = 0; n < to_read; ++n) {
513                                 mixdown_buffer[n] *= gain_buffer[n];
514                         }
515                 }
516         } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
517
518                 // XXX this should be using what in 2.0 would have been: 
519                 // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
520
521                 for (nframes_t n = 0; n < to_read; ++n) {
522                         mixdown_buffer[n] *= _scale_amplitude;
523                 }
524         }
525         
526         if (!opaque()) {
527                 
528                 /* gack. the things we do for users.
529                  */
530                 
531                 buf += buf_offset;
532                         
533                 for (nframes_t n = 0; n < to_read; ++n) {
534                         buf[n] += mixdown_buffer[n];
535                 }
536         } 
537
538         return to_read;
539 }
540         
541 XMLNode&
542 AudioRegion::state (bool full)
543 {
544         XMLNode& node (Region::state (full));
545         XMLNode *child;
546         char buf[64];
547         char buf2[64];
548         LocaleGuard lg (X_("POSIX"));
549         
550         node.add_property ("flags", enum_2_string (_flags));
551
552         snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude);
553         node.add_property ("scale-gain", buf);
554
555         // XXX these should move into Region
556
557         for (uint32_t n=0; n < _sources.size(); ++n) {
558                 snprintf (buf2, sizeof(buf2), "source-%d", n);
559                 _sources[n]->id().print (buf, sizeof (buf));
560                 node.add_property (buf2, buf);
561         }
562
563         for (uint32_t n=0; n < _master_sources.size(); ++n) {
564                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
565                 _master_sources[n]->id().print (buf, sizeof (buf));
566                 node.add_property (buf2, buf);
567         }
568
569         snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
570         node.add_property ("channels", buf);
571
572         if (full) {
573         
574                 child = node.add_child (X_("FadeIn"));
575                 
576                 if ((_flags & DefaultFadeIn)) {
577                         child->add_property (X_("default"), X_("yes"));
578                 } else {
579                         child->add_child_nocopy (_fade_in->get_state ());
580                 }
581
582                 child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes"));
583                 
584                 child = node.add_child (X_("FadeOut"));
585                 
586                 if ((_flags & DefaultFadeOut)) {
587                         child->add_property (X_("default"), X_("yes"));
588                 } else {
589                         child->add_child_nocopy (_fade_out->get_state ());
590                 }
591                 
592                 child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes"));
593         }
594         
595         child = node.add_child ("Envelope");
596
597         if (full) {
598                 bool default_env = false;
599                 
600                 // If there are only two points, the points are in the start of the region and the end of the region
601                 // so, if they are both at 1.0f, that means the default region.
602
603                 if (_envelope->size() == 2 &&
604                     _envelope->front()->value == 1.0f &&
605                     _envelope->back()->value==1.0f) {
606                         if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
607                                 default_env = true;
608                         }
609                 } 
610
611                 if (default_env) {
612                         child->add_property ("default", "yes");
613                 } else {
614                         child->add_child_nocopy (_envelope->get_state ());
615                 }
616
617         } else {
618                 child->add_property ("default", "yes");
619         }
620
621         if (full && _extra_xml) {
622                 node.add_child_copy (*_extra_xml);
623         }
624
625         return node;
626 }
627
628 int
629 AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
630 {
631         const XMLNodeList& nlist = node.children();
632         const XMLProperty *prop;
633         LocaleGuard lg (X_("POSIX"));
634
635         Region::set_live_state (node, what_changed, false);
636
637         uint32_t old_flags = _flags;
638
639         if ((prop = node.property ("flags")) != 0) {
640                 _flags = Flag (string_2_enum (prop->value(), _flags));
641
642                 //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
643
644                 _flags = Flag (_flags & ~Region::LeftOfSplit);
645                 _flags = Flag (_flags & ~Region::RightOfSplit);
646         }
647
648         if ((old_flags ^ _flags) & Muted) {
649                 what_changed = Change (what_changed|MuteChanged);
650         }
651         if ((old_flags ^ _flags) & Opaque) {
652                 what_changed = Change (what_changed|OpacityChanged);
653         }
654         if ((old_flags ^ _flags) & Locked) {
655                 what_changed = Change (what_changed|LockChanged);
656         }
657
658         if ((prop = node.property ("scale-gain")) != 0) {
659                 _scale_amplitude = atof (prop->value().c_str());
660                 what_changed = Change (what_changed|ScaleAmplitudeChanged);
661         } else {
662                 _scale_amplitude = 1.0;
663         }
664         
665         /* Now find envelope description and other misc child items */
666                                 
667         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
668                 
669                 XMLNode *child;
670                 XMLProperty *prop;
671                 
672                 child = (*niter);
673                 
674                 if (child->name() == "Envelope") {
675
676                         _envelope->clear ();
677
678                         if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child)) {
679                                 set_default_envelope ();
680                         }
681
682                         _envelope->set_max_xval (_length);
683                         _envelope->truncate_end (_length);
684
685                 } else if (child->name() == "FadeIn") {
686                         
687                         _fade_in->clear ();
688                         
689                         if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
690                                 set_default_fade_in ();
691                         } else {
692                                 XMLNode* grandchild = child->child ("AutomationList");
693                                 if (grandchild) {
694                                         _fade_in->set_state (*grandchild);
695                                 }
696                         }
697
698                         if ((prop = child->property ("active")) != 0) {
699                                 if (prop->value() == "yes") {
700                                         set_fade_in_active (true);
701                                 } else {
702                                         set_fade_in_active (true);
703                                 }
704                         }
705
706                 } else if (child->name() == "FadeOut") {
707                         
708                         _fade_out->clear ();
709
710                         if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
711                                 set_default_fade_out ();
712                         } else {
713                                 XMLNode* grandchild = child->child ("AutomationList");
714                                 if (grandchild) {
715                                         _fade_out->set_state (*grandchild);
716                                 } 
717                         }
718
719                         if ((prop = child->property ("active")) != 0) {
720                                 if (prop->value() == "yes") {
721                                         set_fade_out_active (true);
722                                 } else {
723                                         set_fade_out_active (false);
724                                 }
725                         }
726
727                 } 
728         }
729
730         if (send) {
731                 send_change (what_changed);
732         }
733
734         return 0;
735 }
736
737 int
738 AudioRegion::set_state (const XMLNode& node)
739 {
740         /* Region::set_state() calls the virtual set_live_state(),
741            which will get us back to AudioRegion::set_live_state()
742            to handle the relevant stuff.
743         */
744
745         return Region::set_state (node);
746 }
747
748 void
749 AudioRegion::set_fade_in_shape (FadeShape shape)
750 {
751         set_fade_in (shape, (nframes_t) _fade_in->back()->when);
752 }
753
754 void
755 AudioRegion::set_fade_out_shape (FadeShape shape)
756 {
757         set_fade_out (shape, (nframes_t) _fade_out->back()->when);
758 }
759
760 void
761 AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
762 {
763         _fade_in->freeze ();
764         _fade_in->clear ();
765
766         switch (shape) {
767         case Linear:
768                 _fade_in->fast_simple_add (0.0, 0.0);
769                 _fade_in->fast_simple_add (len, 1.0);
770                 break;
771
772         case Fast:
773                 _fade_in->fast_simple_add (0, 0);
774                 _fade_in->fast_simple_add (len * 0.389401, 0.0333333);
775                 _fade_in->fast_simple_add (len * 0.629032, 0.0861111);
776                 _fade_in->fast_simple_add (len * 0.829493, 0.233333);
777                 _fade_in->fast_simple_add (len * 0.9447, 0.483333);
778                 _fade_in->fast_simple_add (len * 0.976959, 0.697222);
779                 _fade_in->fast_simple_add (len, 1);
780                 break;
781
782         case Slow:
783                 _fade_in->fast_simple_add (0, 0);
784                 _fade_in->fast_simple_add (len * 0.0207373, 0.197222);
785                 _fade_in->fast_simple_add (len * 0.0645161, 0.525);
786                 _fade_in->fast_simple_add (len * 0.152074, 0.802778);
787                 _fade_in->fast_simple_add (len * 0.276498, 0.919444);
788                 _fade_in->fast_simple_add (len * 0.481567, 0.980556);
789                 _fade_in->fast_simple_add (len * 0.767281, 1);
790                 _fade_in->fast_simple_add (len, 1);
791                 break;
792
793         case LogA:
794                 _fade_in->fast_simple_add (0, 0);
795                 _fade_in->fast_simple_add (len * 0.0737327, 0.308333);
796                 _fade_in->fast_simple_add (len * 0.246544, 0.658333);
797                 _fade_in->fast_simple_add (len * 0.470046, 0.886111);
798                 _fade_in->fast_simple_add (len * 0.652074, 0.972222);
799                 _fade_in->fast_simple_add (len * 0.771889, 0.988889);
800                 _fade_in->fast_simple_add (len, 1);
801                 break;
802
803         case LogB:
804                 _fade_in->fast_simple_add (0, 0);
805                 _fade_in->fast_simple_add (len * 0.304147, 0.0694444);
806                 _fade_in->fast_simple_add (len * 0.529954, 0.152778);
807                 _fade_in->fast_simple_add (len * 0.725806, 0.333333);
808                 _fade_in->fast_simple_add (len * 0.847926, 0.558333);
809                 _fade_in->fast_simple_add (len * 0.919355, 0.730556);
810                 _fade_in->fast_simple_add (len, 1);
811                 break;
812         }
813
814         _fade_in->thaw ();
815         _fade_in_shape = shape;
816
817         send_change (FadeInChanged);
818 }
819
820 void
821 AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
822 {
823         _fade_out->freeze ();
824         _fade_out->clear ();
825
826         switch (shape) {
827         case Fast:
828                 _fade_out->fast_simple_add (len * 0, 1);
829                 _fade_out->fast_simple_add (len * 0.023041, 0.697222);
830                 _fade_out->fast_simple_add (len * 0.0553,   0.483333);
831                 _fade_out->fast_simple_add (len * 0.170507, 0.233333);
832                 _fade_out->fast_simple_add (len * 0.370968, 0.0861111);
833                 _fade_out->fast_simple_add (len * 0.610599, 0.0333333);
834                 _fade_out->fast_simple_add (len * 1, 0);
835                 break;
836
837         case LogA:
838                 _fade_out->fast_simple_add (len * 0, 1);
839                 _fade_out->fast_simple_add (len * 0.228111, 0.988889);
840                 _fade_out->fast_simple_add (len * 0.347926, 0.972222);
841                 _fade_out->fast_simple_add (len * 0.529954, 0.886111);
842                 _fade_out->fast_simple_add (len * 0.753456, 0.658333);
843                 _fade_out->fast_simple_add (len * 0.9262673, 0.308333);
844                 _fade_out->fast_simple_add (len * 1, 0);
845                 break;
846
847         case Slow:
848                 _fade_out->fast_simple_add (len * 0, 1);
849                 _fade_out->fast_simple_add (len * 0.305556, 1);
850                 _fade_out->fast_simple_add (len * 0.548611, 0.991736);
851                 _fade_out->fast_simple_add (len * 0.759259, 0.931129);
852                 _fade_out->fast_simple_add (len * 0.918981, 0.68595);
853                 _fade_out->fast_simple_add (len * 0.976852, 0.22865);
854                 _fade_out->fast_simple_add (len * 1, 0);
855                 break;
856
857         case LogB:
858                 _fade_out->fast_simple_add (len * 0, 1);
859                 _fade_out->fast_simple_add (len * 0.080645, 0.730556);
860                 _fade_out->fast_simple_add (len * 0.277778, 0.289256);
861                 _fade_out->fast_simple_add (len * 0.470046, 0.152778);
862                 _fade_out->fast_simple_add (len * 0.695853, 0.0694444);
863                 _fade_out->fast_simple_add (len * 1, 0);
864                 break;
865
866         case Linear:
867                 _fade_out->fast_simple_add (len * 0, 1);
868                 _fade_out->fast_simple_add (len * 1, 0);
869                 break;
870         }
871
872         _fade_out->thaw ();
873         _fade_out_shape = shape;
874
875         send_change (FadeOutChanged);
876 }
877
878 void
879 AudioRegion::set_fade_in_length (nframes_t len)
880 {
881         if (len > _length) {
882                 len = _length - 1;
883         }
884
885         bool changed = _fade_in->extend_to (len);
886
887         if (changed) {
888                 _flags = Flag (_flags & ~DefaultFadeIn);
889                 send_change (FadeInChanged);
890         }
891 }
892
893 void
894 AudioRegion::set_fade_out_length (nframes_t len)
895 {
896         if (len > _length) {
897                 len = _length - 1;
898         }
899
900         bool changed =  _fade_out->extend_to (len);
901
902         if (changed) {
903                 _flags = Flag (_flags & ~DefaultFadeOut);
904                 send_change (FadeOutChanged);
905         }
906 }
907
908 void
909 AudioRegion::set_fade_in_active (bool yn)
910 {
911         if (yn == (_flags & FadeIn)) {
912                 return;
913         }
914         if (yn) {
915                 _flags = Flag (_flags|FadeIn);
916         } else {
917                 _flags = Flag (_flags & ~FadeIn);
918         }
919
920         send_change (FadeInActiveChanged);
921 }
922
923 void
924 AudioRegion::set_fade_out_active (bool yn)
925 {
926         if (yn == (_flags & FadeOut)) {
927                 return;
928         }
929         if (yn) {
930                 _flags = Flag (_flags | FadeOut);
931         } else {
932                 _flags = Flag (_flags & ~FadeOut);
933         }
934
935         send_change (FadeOutActiveChanged);
936 }
937
938 bool
939 AudioRegion::fade_in_is_default () const
940 {
941         return _fade_in_shape == Linear && _fade_in->back()->when == 64;
942 }
943
944 bool
945 AudioRegion::fade_out_is_default () const
946 {
947         return _fade_out_shape == Linear && _fade_out->back()->when == 64;
948 }
949
950 void
951 AudioRegion::set_default_fade_in ()
952 {
953         set_fade_in (Linear, 64);
954 }
955
956 void
957 AudioRegion::set_default_fade_out ()
958 {
959         set_fade_out (Linear, 64);
960 }
961
962 void
963 AudioRegion::set_default_fades ()
964 {
965         _fade_in_disabled = 0;
966         _fade_out_disabled = 0;
967         set_default_fade_in ();
968         set_default_fade_out ();
969 }
970
971 void
972 AudioRegion::set_default_envelope ()
973 {
974         _envelope->freeze ();
975         _envelope->clear ();
976         _envelope->fast_simple_add (0, 1.0f);
977         _envelope->fast_simple_add (_length, 1.0f);
978         _envelope->thaw ();
979 }
980
981 void
982 AudioRegion::recompute_at_end ()
983 {
984         /* our length has changed. recompute a new final point by interpolating 
985            based on the the existing curve.
986         */
987         
988         _envelope->freeze ();
989         _envelope->truncate_end (_length);
990         _envelope->set_max_xval (_length);
991         _envelope->thaw ();
992
993         if (_fade_in->back()->when > _length) {
994                 _fade_in->extend_to (_length);
995                 send_change (FadeInChanged);
996         }
997
998         if (_fade_out->back()->when > _length) {
999                 _fade_out->extend_to (_length);
1000                 send_change (FadeOutChanged);
1001         }
1002 }       
1003
1004 void
1005 AudioRegion::recompute_at_start ()
1006 {
1007         /* as above, but the shift was from the front */
1008
1009         _envelope->truncate_start (_length);
1010
1011         if (_fade_in->back()->when > _length) {
1012                 _fade_in->extend_to (_length);
1013                 send_change (FadeInChanged);
1014         }
1015
1016         if (_fade_out->back()->when > _length) {
1017                 _fade_out->extend_to (_length);
1018                 send_change (FadeOutChanged);
1019         }
1020 }
1021
1022 int
1023 AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<Region> >& v) const
1024 {
1025         SourceList srcs;
1026         string new_name;
1027         int n = 0;
1028
1029         if (_sources.size() < 2) {
1030                 return 0;
1031         }
1032
1033         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1034                 srcs.clear ();
1035                 srcs.push_back (*i);
1036
1037                 new_name = _name;
1038
1039                 if (_sources.size() == 2) {
1040                         if (n == 0) {
1041                                 new_name += "-L";
1042                         } else {
1043                                 new_name += "-R";
1044                         }
1045                 } else {
1046                         new_name += '-';
1047                         new_name += ('0' + n + 1);
1048                 }
1049
1050                 /* create a copy with just one source. prevent if from being thought of as
1051                    "whole file" even if it covers the entire source file(s).
1052                  */
1053
1054                 Flag f = Flag (_flags & ~WholeFile);
1055
1056                 v.push_back(RegionFactory::create (srcs, _start, _length, new_name, _layer, f));
1057                 
1058                 ++n;
1059         }
1060
1061         return 0;
1062 }
1063
1064 nframes_t
1065 AudioRegion::read_raw_internal (Sample* buf, sframes_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" */