(Hopefully) clarify operator= and copy construction behaviour of the Property hierarc...
[ardour.git] / libs / ardour / audioregion.cc
index abddffc12e887dce54a7f7fbbf9b11469817a839..7cc8e00998c89719c3dd9bf7d640b29f83028f7e 100644 (file)
@@ -101,15 +101,14 @@ AudioRegion::register_properties ()
        , _fade_in_active (Properties::fade_in_active, true) \
        , _fade_out_active (Properties::fade_out_active, true) \
        , _scale_amplitude (Properties::scale_amplitude, 1.0)
-
+       
 #define AUDIOREGION_COPY_STATE(other) \
-        _envelope_active (other->_envelope_active) \
-       , _default_fade_in (other->_default_fade_in) \
-       , _default_fade_out (other->_default_fade_out) \
-        , _fade_in_active (other->_fade_in_active) \
-        , _fade_out_active (other->_fade_out_active) \
-       , _scale_amplitude (other->_scale_amplitude) 
-
+       _envelope_active (Properties::envelope_active, other->_envelope_active) \
+       , _default_fade_in (Properties::default_fade_in, other->_default_fade_in) \
+       , _default_fade_out (Properties::default_fade_out, other->_default_fade_out) \
+       , _fade_in_active (Properties::fade_in_active, other->_fade_in_active) \
+       , _fade_out_active (Properties::fade_out_active, other->_fade_out_active) \
+       , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude)
 /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
 
 void
@@ -161,8 +160,10 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes64_
        , _automatable (other->session())
        , _fade_in (new AutomationList (*other->_fade_in))
        , _fade_out (new AutomationList (*other->_fade_out))
-         /* XXX is this guaranteed to work for all values of offset+offset_relative? */
-       , _envelope (new AutomationList (*other->_envelope, _start, _start + _length))
+         /* As far as I can see, the _envelope's times are relative to region position, and have nothing
+            to do with sources (and hence _start).  So when we copy the envelope, we just use the supplied offset.
+         */
+       , _envelope (new AutomationList (*other->_envelope, offset, other->_length))
        , _fade_in_suspended (0)
        , _fade_out_suspended (0)
 {
@@ -243,6 +244,9 @@ AudioRegion::post_set ()
                set_default_fade_in ();
                _right_of_split = false;
        }
+
+       /* If _length changed, adjust our gain envelope accordingly */
+       _envelope->truncate_end (_length);
 }
 
 void
@@ -260,6 +264,9 @@ AudioRegion::connect_to_header_position_offset_changed ()
 
        for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
 
+                /* connect only once to HeaderPositionOffsetChanged, even if sources are replicated
+                 */
+
                if (unique_srcs.find (*i) == unique_srcs.end ()) {
                        unique_srcs.insert (*i);
                        boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
@@ -314,12 +321,6 @@ AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, in
        return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
 }
 
-framecnt_t
-AudioRegion::read_with_ops (Sample* buf, framepos_t file_position, framecnt_t cnt, int channel, ReadOps rops) const
-{
-       return _read_at (_sources, _length, buf, 0, 0, file_position, cnt, channel, 0, 0, rops);
-}
-
 framecnt_t
 AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
                      framepos_t file_position, framecnt_t cnt, uint32_t chan_n,
@@ -355,6 +356,10 @@ AudioRegion::_read_at (const SourceList& /*srcs*/, framecnt_t limit,
        framecnt_t to_read;
        bool raw = (rops == ReadOpsNone);
 
+        if (n_channels() == 0) {
+                return 0;
+        }
+
        if (muted() && !raw) {
                return 0; /* read nothing */
        }
@@ -406,7 +411,22 @@ AudioRegion::_read_at (const SourceList& /*srcs*/, framecnt_t limit,
                   we don't have.
                */
 
-               memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
+                if (Config->get_replicate_missing_region_channels()) {
+                       /* track is N-channel, this region has less channels, so use a relevant channel
+                        */
+                        
+                        uint32_t channel = n_channels() % chan_n;
+                        boost::shared_ptr<AudioSource> src = audio_source (channel);
+
+                        if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
+                                return 0; /* "read nothing" */
+                        }
+
+                        /* adjust read data count appropriately since this was a duplicate read */
+                        src->dec_read_data_count (to_read);
+                } else {
+                        memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
+                }
        }
 
        if (rops & ReadOpsFades) {
@@ -502,7 +522,7 @@ AudioRegion::_read_at (const SourceList& /*srcs*/, framecnt_t limit,
                }
        }
 
-       if (!opaque()) {
+       if (!opaque() && (buf != mixdown_buffer)) {
 
                /* gack. the things we do for users.
                 */
@@ -518,64 +538,53 @@ AudioRegion::_read_at (const SourceList& /*srcs*/, framecnt_t limit,
 }
 
 XMLNode&
-AudioRegion::state (bool full)
+AudioRegion::state ()
 {
-       XMLNode& node (Region::state (full));
+       XMLNode& node (Region::state ());
        XMLNode *child;
        char buf[64];
-       char buf2[64];
        LocaleGuard lg (X_("POSIX"));
 
-
-       // XXX these should move into Region
-
-       for (uint32_t n=0; n < _sources.size(); ++n) {
-               snprintf (buf2, sizeof(buf2), "source-%d", n);
-               _sources[n]->id().print (buf, sizeof (buf));
-               node.add_property (buf2, buf);
-       }
-
-       for (uint32_t n=0; n < _master_sources.size(); ++n) {
-               snprintf (buf2, sizeof(buf2), "master-source-%d", n);
-               _master_sources[n]->id().print (buf, sizeof (buf));
-               node.add_property (buf2, buf);
-       }
-
        snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
        node.add_property ("channels", buf);
 
-       if (full) {
-               Stateful::add_properties (node);
-       }
+       Stateful::add_properties (node);
 
        child = node.add_child ("Envelope");
 
-       if (full) {
-               bool default_env = false;
-
-               // If there are only two points, the points are in the start of the region and the end of the region
-               // so, if they are both at 1.0f, that means the default region.
-
-               if (_envelope->size() == 2 &&
-                   _envelope->front()->value == 1.0f &&
-                   _envelope->back()->value==1.0f) {
-                       if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
-                               default_env = true;
-                       }
+       bool default_env = false;
+       
+       // If there are only two points, the points are in the start of the region and the end of the region
+       // so, if they are both at 1.0f, that means the default region.
+       
+       if (_envelope->size() == 2 &&
+           _envelope->front()->value == 1.0f &&
+           _envelope->back()->value==1.0f) {
+               if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
+                       default_env = true;
                }
+       }
+       
+       if (default_env) {
+               child->add_property ("default", "yes");
+       } else {
+               child->add_child_nocopy (_envelope->get_state ());
+       }
 
-               if (default_env) {
-                       child->add_property ("default", "yes");
-               } else {
-                       child->add_child_nocopy (_envelope->get_state ());
-               }
+       child = node.add_child (X_("FadeIn"));
 
-       } else {
+       if (_default_fade_in) {
                child->add_property ("default", "yes");
+       } else {
+               child->add_child_nocopy (_fade_in->get_state ());
        }
 
-       if (full && _extra_xml) {
-               node.add_child_copy (*_extra_xml);
+       child = node.add_child (X_("FadeOut"));
+
+       if (_default_fade_out) {
+               child->add_property ("default", "yes");
+       } else {
+               child->add_child_nocopy (_fade_out->get_state ());
        }
 
        return node;
@@ -691,54 +700,6 @@ AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_
        return 0;
 }
 
-bool
-AudioRegion::set_property (const PropertyBase& prop)
-{
-       DEBUG_TRACE (DEBUG::Properties,  string_compose ("audio region %1 set property %2\n", _name.val(), prop.property_name()));
-
-       if (prop == Properties::envelope_active.property_id) {
-               bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
-               if (val != _envelope_active) {
-                       _envelope_active = val;
-                       return true;
-               }
-       } else if (prop == Properties::default_fade_in.property_id) {
-               bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
-               if (val != _default_fade_in) {
-                       _default_fade_in = val;
-                       return true;
-               }
-       } else if (prop == Properties::default_fade_out.property_id) {
-               bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
-               if (val != _default_fade_out) {
-                       _default_fade_out = val;
-                       return true;
-               }
-       } else if (prop == Properties::fade_in_active.property_id) {
-               bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
-               if (val != _fade_in_active) {
-                       _fade_in_active = val;
-                       return true;
-               }
-       } else if (prop == Properties::fade_out_active.property_id) {
-               bool val = dynamic_cast<const PropertyTemplate<bool>*>(&prop)->val();
-               if (val != _fade_out_active) {
-                       _fade_out_active = val;
-                       return true;
-               }
-       } else if (prop == Properties::scale_amplitude.property_id) {
-               gain_t val = dynamic_cast<const PropertyTemplate<gain_t>*>(&prop)->val();
-               if (val != _scale_amplitude) {
-                       _scale_amplitude = val;
-                       return true;
-               }
-       } else {
-               return Region::set_property (prop);
-       }
-       
-       return false;
-}
-
 int
 AudioRegion::set_state (const XMLNode& node, int version)
 {
@@ -996,15 +957,18 @@ AudioRegion::recompute_at_end ()
        _envelope->set_max_xval (_length);
         _envelope->thaw ();
 
+        if (_left_of_split) {
+                set_default_fade_out ();
+                _left_of_split = false;
+        } else if (_fade_out->back()->when > _length) {
+                _fade_out->extend_to (_length);
+                send_change (PropertyChange (Properties::fade_out));
+        }
+        
        if (_fade_in->back()->when > _length) {
                _fade_in->extend_to (_length);
                send_change (PropertyChange (Properties::fade_in));
        }
-
-       if (_fade_out->back()->when > _length) {
-               _fade_out->extend_to (_length);
-               send_change (PropertyChange (Properties::fade_out));
-       }
 }
 
 void
@@ -1014,7 +978,10 @@ AudioRegion::recompute_at_start ()
 
        _envelope->truncate_start (_length);
 
-       if (_fade_in->back()->when > _length) {
+        if (_right_of_split) {
+                set_default_fade_in ();
+                _right_of_split = false;
+        } else if (_fade_in->back()->when > _length) {
                _fade_in->extend_to (_length);
                send_change (PropertyChange (Properties::fade_in));
        }
@@ -1163,74 +1130,66 @@ AudioRegion::set_scale_amplitude (gain_t g)
        send_change (PropertyChange (Properties::scale_amplitude));
 }
 
-void
-AudioRegion::normalize_to (float target_dB)
+/** @return the maximum (linear) amplitude of the region */
+double
+AudioRegion::maximum_amplitude () const
 {
-       const framecnt_t blocksize = 64 * 1024;
-       Sample buf[blocksize];
-       framepos_t fpos;
-       framepos_t fend;
-       framecnt_t to_read;
+       framepos_t fpos = _start;
+       framepos_t const fend = _start + _length;
        double maxamp = 0;
-       gain_t target = dB_to_coefficient (target_dB);
-
-       if (target == 1.0f) {
-               /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
-                  that we may have clipped.
-               */
-               target -= FLT_EPSILON;
-       }
-
-       fpos = _start;
-       fend = _start + _length;
-
-       /* first pass: find max amplitude */
 
+       framecnt_t const blocksize = 64 * 1024;
+       Sample buf[blocksize];
+       
        while (fpos < fend) {
 
                uint32_t n;
 
-               to_read = min (fend - fpos, blocksize);
+               framecnt_t const to_read = min (fend - fpos, blocksize);
 
                for (n = 0; n < n_channels(); ++n) {
 
                        /* read it in */
 
                        if (read_raw_internal (buf, fpos, to_read, 0) != to_read) {
-                               return;
+                               return 0;
                        }
 
                        maxamp = compute_peak (buf, to_read, maxamp);
                }
 
                fpos += to_read;
-       };
+       }
+
+       return maxamp;
+}
+
+/** Normalize using a given maximum amplitude and target, so that region
+ *  _scale_amplitude becomes target / max_amplitude.
+ */
+void
+AudioRegion::normalize (float max_amplitude, float target_dB)
+{
+       gain_t target = dB_to_coefficient (target_dB);
+
+       if (target == 1.0f) {
+               /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
+                  that we may have clipped.
+               */
+               target -= FLT_EPSILON;
+       }
 
-       if (maxamp == 0.0f) {
+       if (max_amplitude == 0.0f) {
                /* don't even try */
                return;
        }
 
-       if (maxamp == target) {
+       if (max_amplitude == target) {
                /* we can't do anything useful */
                return;
        }
 
-       /* compute scale factor */
-
-       _scale_amplitude = target/maxamp;
-
-       /* tell the diskstream we're in */
-
-       boost::shared_ptr<Playlist> pl (playlist());
-
-       if (pl) {
-               pl->ContentsChanged();
-       }
-
-       /* tell everybody else */
-
-       send_change (PropertyChange (Properties::scale_amplitude));
+       set_scale_amplitude (target / max_amplitude);
 }
 
 void
@@ -1327,6 +1286,63 @@ AudioRegion::audio_source (uint32_t n) const
        return boost::dynamic_pointer_cast<AudioSource>(source(n));
 }
 
+int 
+AudioRegion::adjust_transients (nframes64_t delta)
+{
+       for (AnalysisFeatureList::iterator x = _transients.begin(); x != _transients.end(); ++x) {
+               (*x) = (*x) + delta;
+       }
+       
+       send_change (PropertyChange (Properties::valid_transients));
+       
+       return 0;  
+} 
+
+int
+AudioRegion::update_transient (nframes64_t old_position, nframes64_t new_position)
+{
+       for (AnalysisFeatureList::iterator x = _transients.begin(); x != _transients.end(); ++x) {
+               if ((*x) == old_position) {
+                       (*x) = new_position;
+                       send_change (PropertyChange (Properties::valid_transients));
+                       
+                       break;
+               }
+       }
+       
+       return 0;
+}
+
+void
+AudioRegion::add_transient (nframes64_t where)
+{
+       _transients.push_back(where);
+       _valid_transients = true;
+       
+       send_change (PropertyChange (Properties::valid_transients));
+}
+
+void
+AudioRegion::remove_transient (nframes64_t where)
+{
+       _transients.remove(where);
+       _valid_transients = true;
+       
+       send_change (PropertyChange (Properties::valid_transients));
+}
+
+int
+AudioRegion::set_transients (AnalysisFeatureList& results)
+{
+       _transients.clear();
+       _transients = results;
+       _valid_transients = true;
+       
+       send_change (PropertyChange (Properties::valid_transients));
+       
+       return 0;
+}
+
 int
 AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
 {
@@ -1387,14 +1403,22 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
 
        /* no existing/complete transient info */
 
+        static bool analyse_dialog_shown = false; /* global per instance of Ardour */
+
        if (!Config->get_auto_analyse_audio()) {
-               pl->session().Dialog (_("\
-You have requested an operation that requires audio analysis.\n\n\
+                if (!analyse_dialog_shown) {
+                        pl->session().Dialog (_("\
+You have requested an operation that requires audio analysis.\n\n       \
 You currently have \"auto-analyse-audio\" disabled, which means\n\
 that transient data must be generated every time it is required.\n\n\
 If you are doing work that will require transient data on a\n\
 regular basis, you should probably enable \"auto-analyse-audio\"\n\
-then quit ardour and restart."));
++then quit ardour and restart.\n\n\
++This dialog will not display again.  But you may notice a slight delay\n\
++in this and future transient-detection operations.\n\
++"));
+                        analyse_dialog_shown = true;
+                }
        }
 
        TransientDetector t (pl->session().frame_rate());
@@ -1458,7 +1482,7 @@ then quit ardour and restart."));
  */
 
 std::list<std::pair<frameoffset_t, framecnt_t> >
-AudioRegion::find_silence (Sample threshold, framecnt_t min_length) const
+AudioRegion::find_silence (Sample threshold, framecnt_t min_length, InterThreadInfo& itt) const
 {
        framecnt_t const block_size = 64 * 1024;
        Sample loudest[block_size];
@@ -1473,7 +1497,7 @@ AudioRegion::find_silence (Sample threshold, framecnt_t min_length) const
        frameoffset_t silence_start = 0;
        bool silence;
 
-       while (pos < end) {
+       while (pos < end && !itt.cancel) {
 
                /* fill `loudest' with the loudest absolute sample at each instant, across all channels */
                memset (loudest, 0, sizeof (Sample) * block_size);
@@ -1502,6 +1526,7 @@ AudioRegion::find_silence (Sample threshold, framecnt_t min_length) const
                }
 
                pos += block_size;
+                itt.progress = (end-pos)/(double)_length;
        }
 
        if (in_silence && end - 1 - silence_start >= min_length) {
@@ -1509,6 +1534,8 @@ AudioRegion::find_silence (Sample threshold, framecnt_t min_length) const
                silent_periods.push_back (std::make_pair (silence_start, end));
        }
 
+        itt.done = true;
+
        return silent_periods;
 }