1559827b42bf2a55c9e054f069223e5a99cc4d96
[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<Region> >& v) const
1018 {
1019         SourceList srcs;
1020         string new_name;
1021         int n = 0;
1022
1023         if (_sources.size() < 2) {
1024                 return 0;
1025         }
1026
1027         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1028                 srcs.clear ();
1029                 srcs.push_back (*i);
1030
1031                 new_name = _name;
1032
1033                 if (_sources.size() == 2) {
1034                         if (n == 0) {
1035                                 new_name += "-L";
1036                         } else {
1037                                 new_name += "-R";
1038                         }
1039                 } else {
1040                         new_name += '-';
1041                         new_name += ('0' + n + 1);
1042                 }
1043
1044                 /* create a copy with just one source. prevent if from being thought of as
1045                    "whole file" even if it covers the entire source file(s).
1046                  */
1047
1048                 Flag f = Flag (_flags & ~WholeFile);
1049
1050                 v.push_back(RegionFactory::create (srcs, _start, _length, new_name, _layer, f));
1051                 
1052                 ++n;
1053         }
1054
1055         return 0;
1056 }
1057
1058 nframes_t
1059 AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const
1060 {
1061         return audio_source()->read  (buf, pos, cnt);
1062 }
1063
1064 int
1065 AudioRegion::exportme (Session& session, ARDOUR::ExportSpecification& spec)
1066 {
1067         // TODO EXPORT
1068 //      const nframes_t blocksize = 4096;
1069 //      nframes_t to_read;
1070 //      int status = -1;
1071 // 
1072 //      spec.channels = _sources.size();
1073 // 
1074 //      if (spec.prepare (blocksize, session.frame_rate())) {
1075 //              goto out;
1076 //      }
1077 // 
1078 //      spec.pos = 0;
1079 //      spec.total_frames = _length;
1080 // 
1081 //      while (spec.pos < _length && !spec.stop) {
1082 //              
1083 //              
1084 //              /* step 1: interleave */
1085 //              
1086 //              to_read = min (_length - spec.pos, blocksize);
1087 //              
1088 //              if (spec.channels == 1) {
1089 // 
1090 //                      if (read_raw_internal (spec.dataF, _start + spec.pos, to_read) != to_read) {
1091 //                              goto out;
1092 //                      }
1093 // 
1094 //              } else {
1095 // 
1096 //                      Sample buf[blocksize];
1097 // 
1098 //                      for (uint32_t chan = 0; chan < spec.channels; ++chan) {
1099 //                              
1100 //                              if (audio_source(chan)->read (buf, _start + spec.pos, to_read) != to_read) {
1101 //                                      goto out;
1102 //                              }
1103 //                              
1104 //                              for (nframes_t x = 0; x < to_read; ++x) {
1105 //                                      spec.dataF[chan+(x*spec.channels)] = buf[x];
1106 //                              }
1107 //                      }
1108 //              }
1109 //              
1110 //              if (spec.process (to_read)) {
1111 //                      goto out;
1112 //              }
1113 //              
1114 //              spec.pos += to_read;
1115 //              spec.progress = (double) spec.pos /_length;
1116 //              
1117 //      }
1118 //      
1119 //      status = 0;
1120 // 
1121 //   out:       
1122 //      spec.running = false;
1123 //      spec.status = status;
1124 //      spec.clear();
1125 //      
1126 //      return status;
1127         return 0;
1128 }
1129
1130 void
1131 AudioRegion::set_scale_amplitude (gain_t g)
1132 {
1133         boost::shared_ptr<Playlist> pl (playlist());
1134
1135         _scale_amplitude = g;
1136
1137         /* tell the diskstream we're in */
1138         
1139         if (pl) {
1140                 pl->Modified();
1141         }
1142
1143         /* tell everybody else */
1144
1145         send_change (ScaleAmplitudeChanged);
1146 }
1147
1148 void
1149 AudioRegion::normalize_to (float target_dB)
1150 {
1151         const nframes_t blocksize = 64 * 1024;
1152         Sample buf[blocksize];
1153         nframes_t fpos;
1154         nframes_t fend;
1155         nframes_t to_read;
1156         double maxamp = 0;
1157         gain_t target = dB_to_coefficient (target_dB);
1158
1159         if (target == 1.0f) {
1160                 /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
1161                    that we may have clipped.
1162                 */
1163                 target -= FLT_EPSILON;
1164         }
1165
1166         fpos = _start;
1167         fend = _start + _length;
1168
1169         /* first pass: find max amplitude */
1170
1171         while (fpos < fend) {
1172
1173                 uint32_t n;
1174
1175                 to_read = min (fend - fpos, blocksize);
1176
1177                 for (n = 0; n < n_channels(); ++n) {
1178
1179                         /* read it in */
1180
1181                         if (read_raw_internal (buf, fpos, to_read) != to_read) {
1182                                 return;
1183                         }
1184                         
1185                         maxamp = compute_peak (buf, to_read, maxamp);
1186                 }
1187
1188                 fpos += to_read;
1189         };
1190
1191         if (maxamp == 0.0f) {
1192                 /* don't even try */
1193                 return;
1194         }
1195
1196         if (maxamp == target) {
1197                 /* we can't do anything useful */
1198                 return;
1199         }
1200
1201         /* compute scale factor */
1202
1203         _scale_amplitude = target/maxamp;
1204
1205         /* tell the diskstream we're in */
1206
1207         boost::shared_ptr<Playlist> pl (playlist());
1208
1209         if (pl) {
1210                 pl->Modified();
1211         }
1212
1213         /* tell everybody else */
1214
1215         send_change (ScaleAmplitudeChanged);
1216 }
1217
1218 void
1219 AudioRegion::fade_in_changed ()
1220 {
1221         send_change (FadeInChanged);
1222 }
1223
1224 void
1225 AudioRegion::fade_out_changed ()
1226 {
1227         send_change (FadeOutChanged);
1228 }
1229
1230 void
1231 AudioRegion::envelope_changed ()
1232 {
1233         send_change (EnvelopeChanged);
1234 }
1235
1236 void
1237 AudioRegion::suspend_fade_in ()
1238 {
1239         if (++_fade_in_disabled == 1) {
1240                 if (fade_in_is_default()) {
1241                         set_fade_in_active (false);
1242                 }
1243         }
1244 }
1245
1246 void
1247 AudioRegion::resume_fade_in ()
1248 {
1249         if (--_fade_in_disabled == 0 && _fade_in_disabled) {
1250                 set_fade_in_active (true);
1251         }
1252 }
1253
1254 void
1255 AudioRegion::suspend_fade_out ()
1256 {
1257         if (++_fade_out_disabled == 1) {
1258                 if (fade_out_is_default()) {
1259                         set_fade_out_active (false);
1260                 }
1261         }
1262 }
1263
1264 void
1265 AudioRegion::resume_fade_out ()
1266 {
1267         if (--_fade_out_disabled == 0 &&_fade_out_disabled) {
1268                 set_fade_out_active (true);
1269         }
1270 }
1271
1272 bool
1273 AudioRegion::speed_mismatch (float sr) const
1274 {
1275         if (_sources.empty()) {
1276                 /* impossible, but ... */
1277                 return false;
1278         }
1279
1280         float fsr = audio_source()->sample_rate();
1281
1282         return fsr != sr;
1283 }
1284
1285 void
1286 AudioRegion::source_offset_changed ()
1287 {
1288         /* XXX this fixes a crash that should not occur. It does occur
1289            becauses regions are not being deleted when a session
1290            is unloaded. That bug must be fixed.
1291         */
1292
1293         if (_sources.empty()) {
1294                 return;
1295         }
1296
1297         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(_sources.front());
1298
1299         if (afs && afs->destructive()) {
1300                 // set_start (source()->natural_position(), this);
1301                 set_position (source()->natural_position(), this);
1302         } 
1303 }
1304
1305 boost::shared_ptr<AudioSource>
1306 AudioRegion::audio_source (uint32_t n) const
1307 {
1308         // Guaranteed to succeed (use a static cast for speed?)
1309         return boost::dynamic_pointer_cast<AudioSource>(source(n));
1310 }
1311
1312 int
1313 AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
1314 {
1315         boost::shared_ptr<Playlist> pl = playlist();
1316
1317         if (!pl) {
1318                 return -1;
1319         }
1320
1321         if (_valid_transients && !force_new) {
1322                 results = _transients;
1323                 return 0;
1324         }
1325
1326         SourceList::iterator s;
1327         
1328         for (s = _sources.begin() ; s != _sources.end(); ++s) {
1329                 if (!(*s)->has_been_analysed()) {
1330                         cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
1331                         break;
1332                 }
1333         }
1334         
1335         if (s == _sources.end()) {
1336                 /* all sources are analyzed, merge data from each one */
1337
1338                 for (s = _sources.begin() ; s != _sources.end(); ++s) {
1339
1340                         /* find the set of transients within the bounds of this region */
1341
1342                         AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(),
1343                                                                          (*s)->transients.end(),
1344                                                                          _start);
1345
1346                         AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
1347                                                                           (*s)->transients.end(),
1348                                                                           _start + _length);
1349                                                                          
1350                         /* and add them */
1351
1352                         results.insert (results.end(), low, high);
1353                 }
1354
1355                 TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
1356                 
1357                 /* translate all transients to current position */
1358
1359                 for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) {
1360                         (*x) -= _start;
1361                         (*x) += _position;
1362                 }
1363
1364                 _transients = results;
1365                 _valid_transients = true;
1366
1367                 return 0;
1368         }
1369
1370         /* no existing/complete transient info */
1371
1372         if (!Config->get_auto_analyse_audio()) {
1373                 pl->session().Dialog (_("\
1374 You have requested an operation that requires audio analysis.\n\n\
1375 You currently have \"auto-analyse-audio\" disabled, which means\n\
1376 that transient data must be generated every time it is required.\n\n\
1377 If you are doing work that will require transient data on a\n\
1378 regular basis, you should probably enable \"auto-analyse-audio\"\n\
1379 then quit ardour and restart."));
1380         }
1381
1382         TransientDetector t (pl->session().frame_rate());
1383         bool existing_results = !results.empty();
1384
1385         _transients.clear ();
1386         _valid_transients = false;
1387
1388         for (uint32_t i = 0; i < n_channels(); ++i) {
1389
1390                 AnalysisFeatureList these_results;
1391
1392                 t.reset ();
1393
1394                 if (t.run ("", this, i, these_results)) {
1395                         return -1;
1396                 }
1397
1398                 /* translate all transients to give absolute position */
1399                 
1400                 for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) {
1401                         (*i) += _position;
1402                 }
1403
1404                 /* merge */
1405                 
1406                 _transients.insert (_transients.end(), these_results.begin(), these_results.end());
1407         }
1408         
1409         if (!results.empty()) {
1410                 if (existing_results) {
1411                         
1412                         /* merge our transients into the existing ones, then clean up
1413                            those.
1414                         */
1415
1416                         results.insert (results.end(), _transients.begin(), _transients.end());
1417                         TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
1418                 }
1419
1420                 /* make sure ours are clean too */
1421
1422                 TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
1423
1424         } else {
1425
1426                 TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
1427                 results = _transients;
1428         }
1429
1430         _valid_transients = true;
1431
1432         return 0;
1433 }
1434
1435 extern "C" {
1436
1437         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) 
1438 {
1439         return ((AudioRegion *) arg)->read_peaks ((PeakData *) data, (nframes_t) npeaks, (nframes_t) start, (nframes_t) cnt, n_chan,samples_per_unit);
1440 }
1441
1442 uint32_t region_length_from_c (void *arg)
1443 {
1444
1445         return ((AudioRegion *) arg)->length();
1446 }
1447
1448 uint32_t sourcefile_length_from_c (void *arg, double zoom_factor)
1449 {
1450         return ( (AudioRegion *) arg)->audio_source()->available_peaks (zoom_factor) ;
1451 }
1452
1453 } /* extern "C" */