Clean up Region interface, remove Readable stub kludge.
[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, file_position, cnt, chan_n, read_frames, skip_frames, ReadOps (~0));
352 }
353
354 nframes_t
355 AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
356                 sframes_t position, nframes_t cnt, uint32_t chan_n) const
357 {
358         return _read_at (_master_sources, _master_sources.front()->length(), buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0);
359 }
360
361 nframes_t
362 AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
363                 Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
364                 sframes_t position, nframes_t cnt, 
365                 uint32_t chan_n, 
366                 nframes_t read_frames, 
367                 nframes_t skip_frames,
368                 ReadOps rops) const
369 {
370         nframes_t internal_offset;
371         nframes_t buf_offset;
372         nframes_t to_read;
373         bool raw = (rops == ReadOpsNone);
374
375         if (muted() && !raw) {
376                 return 0; /* read nothing */
377         }
378
379         /* precondition: caller has verified that we cover the desired section */
380
381         if (position < _position) {
382                 internal_offset = 0;
383                 buf_offset = _position - position;
384                 cnt -= buf_offset;
385         } else {
386                 internal_offset = position - _position;
387                 buf_offset = 0;
388         }
389
390         if (internal_offset >= limit) {
391                 return 0; /* read nothing */
392         }
393
394         if ((to_read = min (cnt, limit - internal_offset)) == 0) {
395                 return 0; /* read nothing */
396         }
397
398         if (opaque() || raw) {
399                 /* overwrite whatever is there */
400                 mixdown_buffer = buf + buf_offset;
401         } else {
402                 mixdown_buffer += buf_offset;
403         }
404
405         if (rops & ReadOpsCount) {
406                 _read_data_count = 0;
407         }
408
409         if (chan_n < n_channels()) {
410
411                 boost::shared_ptr<AudioSource> src = audio_source(chan_n);
412                 if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
413                         return 0; /* "read nothing" */
414                 }
415                 
416                 if (rops & ReadOpsCount) {
417                         _read_data_count += src->read_data_count();
418                 }
419
420         } else {
421                 
422                 /* track is N-channel, this region has less channels; silence the ones
423                    we don't have.
424                 */
425
426                 memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
427         }
428
429         if (rops & ReadOpsFades) {
430         
431                 /* fade in */
432
433                 if ((_flags & FadeIn) && Config->get_use_region_fades()) {
434                         
435                         nframes_t fade_in_length = (nframes_t) _fade_in->back()->when;
436                         
437                         /* see if this read is within the fade in */
438                         
439                         if (internal_offset < fade_in_length) {
440                                 
441                                 nframes_t fi_limit;
442                                 
443                                 fi_limit = min (to_read, fade_in_length - internal_offset);
444                                 
445
446                                 _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
447                                 
448                                 for (nframes_t n = 0; n < fi_limit; ++n) {
449                                         mixdown_buffer[n] *= gain_buffer[n];
450                                 }
451                         }
452                 }
453                 
454                 /* fade out */
455                 
456                 if ((_flags & FadeOut) && Config->get_use_region_fades()) {
457
458                         /* see if some part of this read is within the fade out */
459                         
460                 /* .................        >|            REGION
461                                              limit
462                                             
463                                  {           }            FADE
464                                              fade_out_length
465                                  ^                                           
466                                  limit - fade_out_length
467                         |--------------|
468                         ^internal_offset
469                                        ^internal_offset + to_read
470                                        
471                                        we need the intersection of [internal_offset,internal_offset+to_read] with
472                                        [limit - fade_out_length, limit]
473                                        
474                 */
475
476         
477                         nframes_t fade_out_length = (nframes_t) _fade_out->back()->when;
478                         nframes_t fade_interval_start = max(internal_offset, limit-fade_out_length);
479                         nframes_t fade_interval_end   = min(internal_offset + to_read, limit);
480
481                         if (fade_interval_end > fade_interval_start) {
482                                 /* (part of the) the fade out is  in this buffer */
483
484                                 nframes_t fo_limit = fade_interval_end - fade_interval_start;
485                                 nframes_t curve_offset = fade_interval_start - (limit-fade_out_length);
486                                 nframes_t fade_offset = fade_interval_start - internal_offset;
487                                 
488                                 _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
489
490                                 for (nframes_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) {
491                                         mixdown_buffer[m] *= gain_buffer[n];
492                                 }
493                         } 
494                         
495                 }
496         }
497                 
498         /* Regular gain curves and scaling */
499         
500         if ((rops & ReadOpsOwnAutomation) && envelope_active())  {
501                 _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
502                         
503                 if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
504                         for (nframes_t n = 0; n < to_read; ++n) {
505                                 mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
506                         }
507                 } else {
508                         for (nframes_t n = 0; n < to_read; ++n) {
509                                 mixdown_buffer[n] *= gain_buffer[n];
510                         }
511                 }
512         } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
513
514                 // XXX this should be using what in 2.0 would have been: 
515                 // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
516
517                 for (nframes_t n = 0; n < to_read; ++n) {
518                         mixdown_buffer[n] *= _scale_amplitude;
519                 }
520         }
521         
522         if (!opaque()) {
523                 
524                 /* gack. the things we do for users.
525                  */
526                 
527                 buf += buf_offset;
528                         
529                 for (nframes_t n = 0; n < to_read; ++n) {
530                         buf[n] += mixdown_buffer[n];
531                 }
532         } 
533
534         return to_read;
535 }
536         
537 XMLNode&
538 AudioRegion::state (bool full)
539 {
540         XMLNode& node (Region::state (full));
541         XMLNode *child;
542         char buf[64];
543         char buf2[64];
544         LocaleGuard lg (X_("POSIX"));
545         
546         node.add_property ("flags", enum_2_string (_flags));
547
548         snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude);
549         node.add_property ("scale-gain", buf);
550
551         // XXX these should move into Region
552
553         for (uint32_t n=0; n < _sources.size(); ++n) {
554                 snprintf (buf2, sizeof(buf2), "source-%d", n);
555                 _sources[n]->id().print (buf, sizeof (buf));
556                 node.add_property (buf2, buf);
557         }
558
559         for (uint32_t n=0; n < _master_sources.size(); ++n) {
560                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
561                 _master_sources[n]->id().print (buf, sizeof (buf));
562                 node.add_property (buf2, buf);
563         }
564
565         snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
566         node.add_property ("channels", buf);
567
568         if (full) {
569         
570                 child = node.add_child (X_("FadeIn"));
571                 
572                 if ((_flags & DefaultFadeIn)) {
573                         child->add_property (X_("default"), X_("yes"));
574                 } else {
575                         child->add_child_nocopy (_fade_in->get_state ());
576                 }
577
578                 child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes"));
579                 
580                 child = node.add_child (X_("FadeOut"));
581                 
582                 if ((_flags & DefaultFadeOut)) {
583                         child->add_property (X_("default"), X_("yes"));
584                 } else {
585                         child->add_child_nocopy (_fade_out->get_state ());
586                 }
587                 
588                 child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes"));
589         }
590         
591         child = node.add_child ("Envelope");
592
593         if (full) {
594                 bool default_env = false;
595                 
596                 // If there are only two points, the points are in the start of the region and the end of the region
597                 // so, if they are both at 1.0f, that means the default region.
598
599                 if (_envelope->size() == 2 &&
600                     _envelope->front()->value == 1.0f &&
601                     _envelope->back()->value==1.0f) {
602                         if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
603                                 default_env = true;
604                         }
605                 } 
606
607                 if (default_env) {
608                         child->add_property ("default", "yes");
609                 } else {
610                         child->add_child_nocopy (_envelope->get_state ());
611                 }
612
613         } else {
614                 child->add_property ("default", "yes");
615         }
616
617         if (full && _extra_xml) {
618                 node.add_child_copy (*_extra_xml);
619         }
620
621         return node;
622 }
623
624 int
625 AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
626 {
627         const XMLNodeList& nlist = node.children();
628         const XMLProperty *prop;
629         LocaleGuard lg (X_("POSIX"));
630
631         Region::set_live_state (node, what_changed, false);
632
633         uint32_t old_flags = _flags;
634
635         if ((prop = node.property ("flags")) != 0) {
636                 _flags = Flag (string_2_enum (prop->value(), _flags));
637
638                 //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
639
640                 _flags = Flag (_flags & ~Region::LeftOfSplit);
641                 _flags = Flag (_flags & ~Region::RightOfSplit);
642         }
643
644         if ((old_flags ^ _flags) & Muted) {
645                 what_changed = Change (what_changed|MuteChanged);
646         }
647         if ((old_flags ^ _flags) & Opaque) {
648                 what_changed = Change (what_changed|OpacityChanged);
649         }
650         if ((old_flags ^ _flags) & Locked) {
651                 what_changed = Change (what_changed|LockChanged);
652         }
653
654         if ((prop = node.property ("scale-gain")) != 0) {
655                 _scale_amplitude = atof (prop->value().c_str());
656                 what_changed = Change (what_changed|ScaleAmplitudeChanged);
657         } else {
658                 _scale_amplitude = 1.0;
659         }
660         
661         /* Now find envelope description and other misc child items */
662                                 
663         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
664                 
665                 XMLNode *child;
666                 XMLProperty *prop;
667                 
668                 child = (*niter);
669                 
670                 if (child->name() == "Envelope") {
671
672                         _envelope->clear ();
673
674                         if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child)) {
675                                 set_default_envelope ();
676                         }
677
678                         _envelope->set_max_xval (_length);
679                         _envelope->truncate_end (_length);
680
681                 } else if (child->name() == "FadeIn") {
682                         
683                         _fade_in->clear ();
684                         
685                         if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
686                                 set_default_fade_in ();
687                         } else {
688                                 XMLNode* grandchild = child->child ("AutomationList");
689                                 if (grandchild) {
690                                         _fade_in->set_state (*grandchild);
691                                 }
692                         }
693
694                         if ((prop = child->property ("active")) != 0) {
695                                 if (prop->value() == "yes") {
696                                         set_fade_in_active (true);
697                                 } else {
698                                         set_fade_in_active (true);
699                                 }
700                         }
701
702                 } else if (child->name() == "FadeOut") {
703                         
704                         _fade_out->clear ();
705
706                         if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
707                                 set_default_fade_out ();
708                         } else {
709                                 XMLNode* grandchild = child->child ("AutomationList");
710                                 if (grandchild) {
711                                         _fade_out->set_state (*grandchild);
712                                 } 
713                         }
714
715                         if ((prop = child->property ("active")) != 0) {
716                                 if (prop->value() == "yes") {
717                                         set_fade_out_active (true);
718                                 } else {
719                                         set_fade_out_active (false);
720                                 }
721                         }
722
723                 } 
724         }
725
726         if (send) {
727                 send_change (what_changed);
728         }
729
730         return 0;
731 }
732
733 int
734 AudioRegion::set_state (const XMLNode& node)
735 {
736         /* Region::set_state() calls the virtual set_live_state(),
737            which will get us back to AudioRegion::set_live_state()
738            to handle the relevant stuff.
739         */
740
741         return Region::set_state (node);
742 }
743
744 void
745 AudioRegion::set_fade_in_shape (FadeShape shape)
746 {
747         set_fade_in (shape, (nframes_t) _fade_in->back()->when);
748 }
749
750 void
751 AudioRegion::set_fade_out_shape (FadeShape shape)
752 {
753         set_fade_out (shape, (nframes_t) _fade_out->back()->when);
754 }
755
756 void
757 AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
758 {
759         _fade_in->freeze ();
760         _fade_in->clear ();
761
762         switch (shape) {
763         case Linear:
764                 _fade_in->fast_simple_add (0.0, 0.0);
765                 _fade_in->fast_simple_add (len, 1.0);
766                 break;
767
768         case Fast:
769                 _fade_in->fast_simple_add (0, 0);
770                 _fade_in->fast_simple_add (len * 0.389401, 0.0333333);
771                 _fade_in->fast_simple_add (len * 0.629032, 0.0861111);
772                 _fade_in->fast_simple_add (len * 0.829493, 0.233333);
773                 _fade_in->fast_simple_add (len * 0.9447, 0.483333);
774                 _fade_in->fast_simple_add (len * 0.976959, 0.697222);
775                 _fade_in->fast_simple_add (len, 1);
776                 break;
777
778         case Slow:
779                 _fade_in->fast_simple_add (0, 0);
780                 _fade_in->fast_simple_add (len * 0.0207373, 0.197222);
781                 _fade_in->fast_simple_add (len * 0.0645161, 0.525);
782                 _fade_in->fast_simple_add (len * 0.152074, 0.802778);
783                 _fade_in->fast_simple_add (len * 0.276498, 0.919444);
784                 _fade_in->fast_simple_add (len * 0.481567, 0.980556);
785                 _fade_in->fast_simple_add (len * 0.767281, 1);
786                 _fade_in->fast_simple_add (len, 1);
787                 break;
788
789         case LogA:
790                 _fade_in->fast_simple_add (0, 0);
791                 _fade_in->fast_simple_add (len * 0.0737327, 0.308333);
792                 _fade_in->fast_simple_add (len * 0.246544, 0.658333);
793                 _fade_in->fast_simple_add (len * 0.470046, 0.886111);
794                 _fade_in->fast_simple_add (len * 0.652074, 0.972222);
795                 _fade_in->fast_simple_add (len * 0.771889, 0.988889);
796                 _fade_in->fast_simple_add (len, 1);
797                 break;
798
799         case LogB:
800                 _fade_in->fast_simple_add (0, 0);
801                 _fade_in->fast_simple_add (len * 0.304147, 0.0694444);
802                 _fade_in->fast_simple_add (len * 0.529954, 0.152778);
803                 _fade_in->fast_simple_add (len * 0.725806, 0.333333);
804                 _fade_in->fast_simple_add (len * 0.847926, 0.558333);
805                 _fade_in->fast_simple_add (len * 0.919355, 0.730556);
806                 _fade_in->fast_simple_add (len, 1);
807                 break;
808         }
809
810         _fade_in->thaw ();
811         _fade_in_shape = shape;
812
813         send_change (FadeInChanged);
814 }
815
816 void
817 AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
818 {
819         _fade_out->freeze ();
820         _fade_out->clear ();
821
822         switch (shape) {
823         case Fast:
824                 _fade_out->fast_simple_add (len * 0, 1);
825                 _fade_out->fast_simple_add (len * 0.023041, 0.697222);
826                 _fade_out->fast_simple_add (len * 0.0553,   0.483333);
827                 _fade_out->fast_simple_add (len * 0.170507, 0.233333);
828                 _fade_out->fast_simple_add (len * 0.370968, 0.0861111);
829                 _fade_out->fast_simple_add (len * 0.610599, 0.0333333);
830                 _fade_out->fast_simple_add (len * 1, 0);
831                 break;
832
833         case LogA:
834                 _fade_out->fast_simple_add (len * 0, 1);
835                 _fade_out->fast_simple_add (len * 0.228111, 0.988889);
836                 _fade_out->fast_simple_add (len * 0.347926, 0.972222);
837                 _fade_out->fast_simple_add (len * 0.529954, 0.886111);
838                 _fade_out->fast_simple_add (len * 0.753456, 0.658333);
839                 _fade_out->fast_simple_add (len * 0.9262673, 0.308333);
840                 _fade_out->fast_simple_add (len * 1, 0);
841                 break;
842
843         case Slow:
844                 _fade_out->fast_simple_add (len * 0, 1);
845                 _fade_out->fast_simple_add (len * 0.305556, 1);
846                 _fade_out->fast_simple_add (len * 0.548611, 0.991736);
847                 _fade_out->fast_simple_add (len * 0.759259, 0.931129);
848                 _fade_out->fast_simple_add (len * 0.918981, 0.68595);
849                 _fade_out->fast_simple_add (len * 0.976852, 0.22865);
850                 _fade_out->fast_simple_add (len * 1, 0);
851                 break;
852
853         case LogB:
854                 _fade_out->fast_simple_add (len * 0, 1);
855                 _fade_out->fast_simple_add (len * 0.080645, 0.730556);
856                 _fade_out->fast_simple_add (len * 0.277778, 0.289256);
857                 _fade_out->fast_simple_add (len * 0.470046, 0.152778);
858                 _fade_out->fast_simple_add (len * 0.695853, 0.0694444);
859                 _fade_out->fast_simple_add (len * 1, 0);
860                 break;
861
862         case Linear:
863                 _fade_out->fast_simple_add (len * 0, 1);
864                 _fade_out->fast_simple_add (len * 1, 0);
865                 break;
866         }
867
868         _fade_out->thaw ();
869         _fade_out_shape = shape;
870
871         send_change (FadeOutChanged);
872 }
873
874 void
875 AudioRegion::set_fade_in_length (nframes_t len)
876 {
877         if (len > _length) {
878                 len = _length - 1;
879         }
880
881         bool changed = _fade_in->extend_to (len);
882
883         if (changed) {
884                 _flags = Flag (_flags & ~DefaultFadeIn);
885                 send_change (FadeInChanged);
886         }
887 }
888
889 void
890 AudioRegion::set_fade_out_length (nframes_t len)
891 {
892         if (len > _length) {
893                 len = _length - 1;
894         }
895
896         bool changed =  _fade_out->extend_to (len);
897
898         if (changed) {
899                 _flags = Flag (_flags & ~DefaultFadeOut);
900                 send_change (FadeOutChanged);
901         }
902 }
903
904 void
905 AudioRegion::set_fade_in_active (bool yn)
906 {
907         if (yn == (_flags & FadeIn)) {
908                 return;
909         }
910         if (yn) {
911                 _flags = Flag (_flags|FadeIn);
912         } else {
913                 _flags = Flag (_flags & ~FadeIn);
914         }
915
916         send_change (FadeInActiveChanged);
917 }
918
919 void
920 AudioRegion::set_fade_out_active (bool yn)
921 {
922         if (yn == (_flags & FadeOut)) {
923                 return;
924         }
925         if (yn) {
926                 _flags = Flag (_flags | FadeOut);
927         } else {
928                 _flags = Flag (_flags & ~FadeOut);
929         }
930
931         send_change (FadeOutActiveChanged);
932 }
933
934 bool
935 AudioRegion::fade_in_is_default () const
936 {
937         return _fade_in_shape == Linear && _fade_in->back()->when == 64;
938 }
939
940 bool
941 AudioRegion::fade_out_is_default () const
942 {
943         return _fade_out_shape == Linear && _fade_out->back()->when == 64;
944 }
945
946 void
947 AudioRegion::set_default_fade_in ()
948 {
949         set_fade_in (Linear, 64);
950 }
951
952 void
953 AudioRegion::set_default_fade_out ()
954 {
955         set_fade_out (Linear, 64);
956 }
957
958 void
959 AudioRegion::set_default_fades ()
960 {
961         _fade_in_disabled = 0;
962         _fade_out_disabled = 0;
963         set_default_fade_in ();
964         set_default_fade_out ();
965 }
966
967 void
968 AudioRegion::set_default_envelope ()
969 {
970         _envelope->freeze ();
971         _envelope->clear ();
972         _envelope->fast_simple_add (0, 1.0f);
973         _envelope->fast_simple_add (_length, 1.0f);
974         _envelope->thaw ();
975 }
976
977 void
978 AudioRegion::recompute_at_end ()
979 {
980         /* our length has changed. recompute a new final point by interpolating 
981            based on the the existing curve.
982         */
983         
984         _envelope->freeze ();
985         _envelope->truncate_end (_length);
986         _envelope->set_max_xval (_length);
987         _envelope->thaw ();
988
989         if (_fade_in->back()->when > _length) {
990                 _fade_in->extend_to (_length);
991                 send_change (FadeInChanged);
992         }
993
994         if (_fade_out->back()->when > _length) {
995                 _fade_out->extend_to (_length);
996                 send_change (FadeOutChanged);
997         }
998 }       
999
1000 void
1001 AudioRegion::recompute_at_start ()
1002 {
1003         /* as above, but the shift was from the front */
1004
1005         _envelope->truncate_start (_length);
1006
1007         if (_fade_in->back()->when > _length) {
1008                 _fade_in->extend_to (_length);
1009                 send_change (FadeInChanged);
1010         }
1011
1012         if (_fade_out->back()->when > _length) {
1013                 _fade_out->extend_to (_length);
1014                 send_change (FadeOutChanged);
1015         }
1016 }
1017
1018 int
1019 AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<Region> >& v) const
1020 {
1021         SourceList srcs;
1022         string new_name;
1023         int n = 0;
1024
1025         if (_sources.size() < 2) {
1026                 return 0;
1027         }
1028
1029         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1030                 srcs.clear ();
1031                 srcs.push_back (*i);
1032
1033                 new_name = _name;
1034
1035                 if (_sources.size() == 2) {
1036                         if (n == 0) {
1037                                 new_name += "-L";
1038                         } else {
1039                                 new_name += "-R";
1040                         }
1041                 } else {
1042                         new_name += '-';
1043                         new_name += ('0' + n + 1);
1044                 }
1045
1046                 /* create a copy with just one source. prevent if from being thought of as
1047                    "whole file" even if it covers the entire source file(s).
1048                  */
1049
1050                 Flag f = Flag (_flags & ~WholeFile);
1051
1052                 v.push_back(RegionFactory::create (srcs, _start, _length, new_name, _layer, f));
1053                 
1054                 ++n;
1055         }
1056
1057         return 0;
1058 }
1059
1060 nframes_t
1061 AudioRegion::read_raw_internal (Sample* buf, sframes_t pos, nframes_t cnt) const
1062 {
1063         return audio_source()->read  (buf, pos, cnt);
1064 }
1065
1066 int
1067 AudioRegion::exportme (Session& session, ARDOUR::ExportSpecification& spec)
1068 {
1069         // TODO EXPORT
1070 //      const nframes_t blocksize = 4096;
1071 //      nframes_t to_read;
1072 //      int status = -1;
1073 // 
1074 //      spec.channels = _sources.size();
1075 // 
1076 //      if (spec.prepare (blocksize, session.frame_rate())) {
1077 //              goto out;
1078 //      }
1079 // 
1080 //      spec.pos = 0;
1081 //      spec.total_frames = _length;
1082 // 
1083 //      while (spec.pos < _length && !spec.stop) {
1084 //              
1085 //              
1086 //              /* step 1: interleave */
1087 //              
1088 //              to_read = min (_length - spec.pos, blocksize);
1089 //              
1090 //              if (spec.channels == 1) {
1091 // 
1092 //                      if (read_raw_internal (spec.dataF, _start + spec.pos, to_read) != to_read) {
1093 //                              goto out;
1094 //                      }
1095 // 
1096 //              } else {
1097 // 
1098 //                      Sample buf[blocksize];
1099 // 
1100 //                      for (uint32_t chan = 0; chan < spec.channels; ++chan) {
1101 //                              
1102 //                              if (audio_source(chan)->read (buf, _start + spec.pos, to_read) != to_read) {
1103 //                                      goto out;
1104 //                              }
1105 //                              
1106 //                              for (nframes_t x = 0; x < to_read; ++x) {
1107 //                                      spec.dataF[chan+(x*spec.channels)] = buf[x];
1108 //                              }
1109 //                      }
1110 //              }
1111 //              
1112 //              if (spec.process (to_read)) {
1113 //                      goto out;
1114 //              }
1115 //              
1116 //              spec.pos += to_read;
1117 //              spec.progress = (double) spec.pos /_length;
1118 //              
1119 //      }
1120 //      
1121 //      status = 0;
1122 // 
1123 //   out:       
1124 //      spec.running = false;
1125 //      spec.status = status;
1126 //      spec.clear();
1127 //      
1128 //      return status;
1129         return 0;
1130 }
1131
1132 void
1133 AudioRegion::set_scale_amplitude (gain_t g)
1134 {
1135         boost::shared_ptr<Playlist> pl (playlist());
1136
1137         _scale_amplitude = g;
1138
1139         /* tell the diskstream we're in */
1140         
1141         if (pl) {
1142                 pl->Modified();
1143         }
1144
1145         /* tell everybody else */
1146
1147         send_change (ScaleAmplitudeChanged);
1148 }
1149
1150 void
1151 AudioRegion::normalize_to (float target_dB)
1152 {
1153         const nframes_t blocksize = 64 * 1024;
1154         Sample buf[blocksize];
1155         nframes_t fpos;
1156         nframes_t fend;
1157         nframes_t to_read;
1158         double maxamp = 0;
1159         gain_t target = dB_to_coefficient (target_dB);
1160
1161         if (target == 1.0f) {
1162                 /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
1163                    that we may have clipped.
1164                 */
1165                 target -= FLT_EPSILON;
1166         }
1167
1168         fpos = _start;
1169         fend = _start + _length;
1170
1171         /* first pass: find max amplitude */
1172
1173         while (fpos < fend) {
1174
1175                 uint32_t n;
1176
1177                 to_read = min (fend - fpos, blocksize);
1178
1179                 for (n = 0; n < n_channels(); ++n) {
1180
1181                         /* read it in */
1182
1183                         if (read_raw_internal (buf, fpos, to_read) != to_read) {
1184                                 return;
1185                         }
1186                         
1187                         maxamp = compute_peak (buf, to_read, maxamp);
1188                 }
1189
1190                 fpos += to_read;
1191         };
1192
1193         if (maxamp == 0.0f) {
1194                 /* don't even try */
1195                 return;
1196         }
1197
1198         if (maxamp == target) {
1199                 /* we can't do anything useful */
1200                 return;
1201         }
1202
1203         /* compute scale factor */
1204
1205         _scale_amplitude = target/maxamp;
1206
1207         /* tell the diskstream we're in */
1208
1209         boost::shared_ptr<Playlist> pl (playlist());
1210
1211         if (pl) {
1212                 pl->Modified();
1213         }
1214
1215         /* tell everybody else */
1216
1217         send_change (ScaleAmplitudeChanged);
1218 }
1219
1220 void
1221 AudioRegion::fade_in_changed ()
1222 {
1223         send_change (FadeInChanged);
1224 }
1225
1226 void
1227 AudioRegion::fade_out_changed ()
1228 {
1229         send_change (FadeOutChanged);
1230 }
1231
1232 void
1233 AudioRegion::envelope_changed ()
1234 {
1235         send_change (EnvelopeChanged);
1236 }
1237
1238 void
1239 AudioRegion::suspend_fade_in ()
1240 {
1241         if (++_fade_in_disabled == 1) {
1242                 if (fade_in_is_default()) {
1243                         set_fade_in_active (false);
1244                 }
1245         }
1246 }
1247
1248 void
1249 AudioRegion::resume_fade_in ()
1250 {
1251         if (--_fade_in_disabled == 0 && _fade_in_disabled) {
1252                 set_fade_in_active (true);
1253         }
1254 }
1255
1256 void
1257 AudioRegion::suspend_fade_out ()
1258 {
1259         if (++_fade_out_disabled == 1) {
1260                 if (fade_out_is_default()) {
1261                         set_fade_out_active (false);
1262                 }
1263         }
1264 }
1265
1266 void
1267 AudioRegion::resume_fade_out ()
1268 {
1269         if (--_fade_out_disabled == 0 &&_fade_out_disabled) {
1270                 set_fade_out_active (true);
1271         }
1272 }
1273
1274 bool
1275 AudioRegion::speed_mismatch (float sr) const
1276 {
1277         if (_sources.empty()) {
1278                 /* impossible, but ... */
1279                 return false;
1280         }
1281
1282         float fsr = audio_source()->sample_rate();
1283
1284         return fsr != sr;
1285 }
1286
1287 void
1288 AudioRegion::source_offset_changed ()
1289 {
1290         /* XXX this fixes a crash that should not occur. It does occur
1291            becauses regions are not being deleted when a session
1292            is unloaded. That bug must be fixed.
1293         */
1294
1295         if (_sources.empty()) {
1296                 return;
1297         }
1298
1299         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(_sources.front());
1300
1301         if (afs && afs->destructive()) {
1302                 // set_start (source()->natural_position(), this);
1303                 set_position (source()->natural_position(), this);
1304         } 
1305 }
1306
1307 boost::shared_ptr<AudioSource>
1308 AudioRegion::audio_source (uint32_t n) const
1309 {
1310         // Guaranteed to succeed (use a static cast for speed?)
1311         return boost::dynamic_pointer_cast<AudioSource>(source(n));
1312 }
1313
1314 int
1315 AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
1316 {
1317         boost::shared_ptr<Playlist> pl = playlist();
1318
1319         if (!pl) {
1320                 return -1;
1321         }
1322
1323         if (_valid_transients && !force_new) {
1324                 results = _transients;
1325                 return 0;
1326         }
1327
1328         SourceList::iterator s;
1329         
1330         for (s = _sources.begin() ; s != _sources.end(); ++s) {
1331                 if (!(*s)->has_been_analysed()) {
1332                         cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
1333                         break;
1334                 }
1335         }
1336         
1337         if (s == _sources.end()) {
1338                 /* all sources are analyzed, merge data from each one */
1339
1340                 for (s = _sources.begin() ; s != _sources.end(); ++s) {
1341
1342                         /* find the set of transients within the bounds of this region */
1343
1344                         AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(),
1345                                                                          (*s)->transients.end(),
1346                                                                          _start);
1347
1348                         AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
1349                                                                           (*s)->transients.end(),
1350                                                                           _start + _length);
1351                                                                          
1352                         /* and add them */
1353
1354                         results.insert (results.end(), low, high);
1355                 }
1356
1357                 TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
1358                 
1359                 /* translate all transients to current position */
1360
1361                 for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) {
1362                         (*x) -= _start;
1363                         (*x) += _position;
1364                 }
1365
1366                 _transients = results;
1367                 _valid_transients = true;
1368
1369                 return 0;
1370         }
1371
1372         /* no existing/complete transient info */
1373
1374         if (!Config->get_auto_analyse_audio()) {
1375                 pl->session().Dialog (_("\
1376 You have requested an operation that requires audio analysis.\n\n\
1377 You currently have \"auto-analyse-audio\" disabled, which means\n\
1378 that transient data must be generated every time it is required.\n\n\
1379 If you are doing work that will require transient data on a\n\
1380 regular basis, you should probably enable \"auto-analyse-audio\"\n\
1381 then quit ardour and restart."));
1382         }
1383
1384         TransientDetector t (pl->session().frame_rate());
1385         bool existing_results = !results.empty();
1386
1387         _transients.clear ();
1388         _valid_transients = false;
1389
1390         for (uint32_t i = 0; i < n_channels(); ++i) {
1391
1392                 AnalysisFeatureList these_results;
1393
1394                 t.reset ();
1395
1396                 if (t.run ("", this, i, these_results)) {
1397                         return -1;
1398                 }
1399
1400                 /* translate all transients to give absolute position */
1401                 
1402                 for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) {
1403                         (*i) += _position;
1404                 }
1405
1406                 /* merge */
1407                 
1408                 _transients.insert (_transients.end(), these_results.begin(), these_results.end());
1409         }
1410         
1411         if (!results.empty()) {
1412                 if (existing_results) {
1413                         
1414                         /* merge our transients into the existing ones, then clean up
1415                            those.
1416                         */
1417
1418                         results.insert (results.end(), _transients.begin(), _transients.end());
1419                         TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
1420                 }
1421
1422                 /* make sure ours are clean too */
1423
1424                 TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
1425
1426         } else {
1427
1428                 TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
1429                 results = _transients;
1430         }
1431
1432         _valid_transients = true;
1433
1434         return 0;
1435 }
1436
1437 extern "C" {
1438
1439         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) 
1440 {
1441         return ((AudioRegion *) arg)->read_peaks ((PeakData *) data, (nframes_t) npeaks, (nframes_t) start, (nframes_t) cnt, n_chan,samples_per_unit);
1442 }
1443
1444 uint32_t region_length_from_c (void *arg)
1445 {
1446
1447         return ((AudioRegion *) arg)->length();
1448 }
1449
1450 uint32_t sourcefile_length_from_c (void *arg, double zoom_factor)
1451 {
1452         return ( (AudioRegion *) arg)->audio_source()->available_peaks (zoom_factor) ;
1453 }
1454
1455 } /* extern "C" */