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