diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 92a7b8b284..c2de836097 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -50,6 +50,7 @@ namespace Properties { LIBARDOUR_API extern PBD::PropertyDescriptor default_fade_out; LIBARDOUR_API extern PBD::PropertyDescriptor fade_in_active; LIBARDOUR_API extern PBD::PropertyDescriptor fade_out_active; + LIBARDOUR_API extern PBD::PropertyDescriptor fade_before_fx; LIBARDOUR_API extern PBD::PropertyDescriptor scale_amplitude; LIBARDOUR_API extern PBD::PropertyDescriptor > fade_in; LIBARDOUR_API extern PBD::PropertyDescriptor > inverse_fade_in; @@ -100,6 +101,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable bool envelope_active () const { return _envelope_active; } bool fade_in_active () const { return _fade_in_active; } bool fade_out_active () const { return _fade_out_active; } + bool fade_before_fx () const { return _fade_before_fx; } std::shared_ptr fade_in() { return _fade_in.val (); } std::shared_ptr inverse_fade_in() { return _inverse_fade_in.val (); } @@ -162,6 +164,8 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable void set_envelope_active (bool yn); void set_default_envelope (); + void set_fade_before_fx (bool yn); + int separate_by_channel (std::vector >&) const; bool remove_plugin (std::shared_ptr); @@ -220,6 +224,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable PBD::Property _default_fade_out; PBD::Property _fade_in_active; PBD::Property _fade_out_active; + PBD::Property _fade_before_fx; /** linear gain to apply to the whole region */ PBD::Property _scale_amplitude; diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 5b81448a72..ab7bf64766 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -82,6 +82,7 @@ namespace ARDOUR { PBD::PropertyDescriptor default_fade_out; PBD::PropertyDescriptor fade_in_active; PBD::PropertyDescriptor fade_out_active; + PBD::PropertyDescriptor fade_before_fx; PBD::PropertyDescriptor scale_amplitude; PBD::PropertyDescriptor > fade_in; PBD::PropertyDescriptor > inverse_fade_in; @@ -176,6 +177,8 @@ AudioRegion::make_property_quarks () DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-active = %1\n", Properties::fade_in_active.property_id)); Properties::fade_out_active.property_id = g_quark_from_static_string (X_("fade-out-active")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-active = %1\n", Properties::fade_out_active.property_id)); + Properties::fade_before_fx.property_id = g_quark_from_static_string (X_("fade-before-fx")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-before-fx = %1\n", Properties::fade_before_fx.property_id)); Properties::scale_amplitude.property_id = g_quark_from_static_string (X_("scale-amplitude")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for scale-amplitude = %1\n", Properties::scale_amplitude.property_id)); Properties::fade_in.property_id = g_quark_from_static_string (X_("FadeIn")); @@ -200,6 +203,7 @@ AudioRegion::register_properties () add_property (_default_fade_out); add_property (_fade_in_active); add_property (_fade_out_active); + add_property (_fade_before_fx); add_property (_scale_amplitude); add_property (_fade_in); add_property (_inverse_fade_in); @@ -214,6 +218,7 @@ AudioRegion::register_properties () , _default_fade_out (Properties::default_fade_out, true) \ , _fade_in_active (Properties::fade_in_active, true) \ , _fade_out_active (Properties::fade_out_active, true) \ + , _fade_before_fx (Properties::fade_before_fx, false) \ , _scale_amplitude (Properties::scale_amplitude, 1.0) \ , _fade_in (Properties::fade_in, std::shared_ptr (new AutomationList (Evoral::Parameter (FadeInAutomation), tdp))) \ , _inverse_fade_in (Properties::inverse_fade_in, std::shared_ptr (new AutomationList (Evoral::Parameter (FadeInAutomation), tdp))) \ @@ -226,6 +231,7 @@ AudioRegion::register_properties () , _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) \ + , _fade_before_fx (Properties::fade_before_fx, other->_fade_before_fx) \ , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) \ , _fade_in (Properties::fade_in, std::shared_ptr (new AutomationList (*other->_fade_in.val()))) \ , _inverse_fade_in (Properties::fade_in, std::shared_ptr (new AutomationList (*other->_inverse_fade_in.val()))) \ @@ -499,6 +505,22 @@ AudioRegion::set_envelope_active (bool yn) } } +void +AudioRegion::set_fade_before_fx (bool yn) +{ + if (fade_before_fx() != yn) { + _fade_before_fx = yn; + send_change (PropertyChange (Properties::fade_before_fx)); + if (!has_region_fx ()) { + return; + } + if (!_invalidated.exchange (true)) { + send_change (PropertyChange (Properties::region_fx)); // trigger DiskReader overwrite + } + RegionFxChanged (); /* EMIT SIGNAL */ + } +} + /** @param buf Buffer to put peak data in. * @param npeaks Number of peaks to read (ie the number of PeakDatas in buf) * @param offset Start position, as an offset from the start of this region's source. @@ -585,6 +607,11 @@ AudioRegion::read_at (Sample* buf, return 0; } + std::shared_ptr pl (playlist()); + if (!pl){ + return 0; + } + /* WORK OUT WHERE TO GET DATA FROM */ samplecnt_t to_read; @@ -605,29 +632,28 @@ AudioRegion::read_at (Sample* buf, return 0; /* read nothing */ } - std::shared_ptr pl (playlist()); - if (!pl){ - return 0; - } - /* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */ + /* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ + * + * This information is also used for inverse fades to fade out + * layered regions below this one. + */ + bool const use_region_fades = _session.config.get_use_region_fades(); /* Amount (length) of fade in that we are dealing with in this read */ samplecnt_t fade_in_limit = 0; - /* Offset from buf / mixdown_buffer of the start - of any fade out that we are dealing with - */ + /* Offset from buf / mixdown_buffer of the start of any fade out that we are dealing with */ sampleoffset_t fade_out_offset = 0; /* Amount (length) of fade out that we are dealing with in this read */ samplecnt_t fade_out_limit = 0; + /* offset for fade-out curve data */ samplecnt_t fade_interval_start = 0; /* Fade in */ - - if (_fade_in_active && _session.config.get_use_region_fades()) { + if (_fade_in_active && use_region_fades) { samplecnt_t fade_in_length = _fade_in->when(false).samples(); @@ -639,15 +665,13 @@ AudioRegion::read_at (Sample* buf, } /* Fade out */ - - if (_fade_out_active && _session.config.get_use_region_fades()) { + if (_fade_out_active && use_region_fades) { /* see if some part of this read is within the fade out */ - - /* ................. >| REGION + /* ................. >| REGION * _length * - * { } FADE + * { } FADE * fade_out_length * ^ * _length - fade_out_length @@ -655,18 +679,19 @@ AudioRegion::read_at (Sample* buf, * |--------------| * ^internal_offset * ^internal_offset + to_read - * - * we need the intersection of [internal_offset,internal_offset+to_read] with - * [_length - fade_out_length, _length] - * + */ + /* we need the intersection of + * [internal_offset, internal_offset + to_read] + * with + * [_length - fade_out_length, _length] */ - fade_interval_start = max (internal_offset, lsamples - _fade_out->when(false).samples()); - samplecnt_t fade_interval_end = min(internal_offset + to_read, lsamples); + fade_interval_start = max (internal_offset, lsamples - _fade_out->when(false).samples()); + samplecnt_t fade_interval_end = min (internal_offset + to_read, lsamples); if (fade_interval_end > fade_interval_start) { /* (part of the) the fade out is in this buffer */ - fade_out_limit = fade_interval_end - fade_interval_start; + fade_out_limit = fade_interval_end - fade_interval_start; fade_out_offset = fade_interval_start - internal_offset; } } @@ -760,6 +785,49 @@ AudioRegion::read_at (Sample* buf, apply_gain_to_buffer (mixdown_buffer, n_read, _scale_amplitude); } + /* Apply Region Fades before processing. */ + + /* Fade in. Precomputed data from above may not apply here. + * latent FX may have increasded to_read -> n_read, + * or internal_offset. + */ + if (_fade_before_fx && use_region_fades && _fade_in_active) { + samplecnt_t fade_in_length = _fade_in->when(false).samples(); + if (offset < fade_in_length) { + samplecnt_t fade_in_limit = min (n_read, fade_in_length - offset); + + //fade_in_limit = min (fade_in_limit, n_read); + assert (fade_in_limit <= n_read); + _fade_in->curve().get_vector (timepos_t (offset), timepos_t (offset + fade_in_limit), gain_buffer, fade_in_limit); + for (samplecnt_t n = 0; n < fade_in_limit; ++n) { + mixdown_buffer[n] *= gain_buffer[n]; + } + } + } + + /* Fade out. Precomputed data from above may not apply here. + * If there are latent FX: internal_offset != offset + */ + if (_fade_before_fx && use_region_fades && _fade_out_active) { + samplecnt_t fade_interval_start = max (offset, lsamples - _fade_out->when(false).samples()); + samplecnt_t fade_interval_end = min (offset + n_read, lsamples); + + if (fade_interval_end > fade_interval_start) { + /* (part of the) the fade out is in this buffer */ + samplecnt_t fade_out_limit = fade_interval_end - fade_interval_start; + sampleoffset_t fade_out_offset = fade_interval_start - offset; + + assert (fade_out_offset + fade_out_limit <= n_read); + + /* apply fade out */ + samplecnt_t const curve_offset = fade_interval_start - _fade_out->when(false).distance (len_as_tpos ()).samples(); + _fade_out->curve().get_vector (timepos_t (curve_offset), timepos_t (curve_offset + fade_out_limit), gain_buffer, fade_out_limit); + for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { + mixdown_buffer[m] *= gain_buffer[n]; + } + } + } + /* for mono regions no cache is required, unless there are * regionFX, which use the _readcache BufferSet. */ @@ -838,9 +906,13 @@ AudioRegion::read_at (Sample* buf, _fade_in->curve().get_vector (timepos_t (internal_offset), timepos_t (internal_offset + fade_in_limit), gain_buffer, fade_in_limit); } - /* Mix our newly-read data in, with the fade */ - for (samplecnt_t n = 0; n < fade_in_limit; ++n) { - buf[n] += mixdown_buffer[n] * gain_buffer[n]; + if (!_fade_before_fx) { + /* Mix our newly-read data in, with the fade */ + for (samplecnt_t n = 0; n < fade_in_limit; ++n) { + buf[n] += mixdown_buffer[n] * gain_buffer[n]; + } + } else { + fade_in_limit = 0; } } @@ -879,11 +951,13 @@ AudioRegion::read_at (Sample* buf, _fade_out->curve().get_vector (timepos_t (curve_offset), timepos_t (curve_offset + fade_out_limit), gain_buffer, fade_out_limit); } - /* Mix our newly-read data with whatever was already there, - with the fade out applied to our data. - */ - for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { - buf[m] += mixdown_buffer[m] * gain_buffer[n]; + if (!_fade_before_fx) { + /* Mix our newly-read data with whatever was already there, with the fade out applied to our data. */ + for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) { + buf[m] += mixdown_buffer[m] * gain_buffer[n]; + } + } else { + fade_out_limit = 0; } } diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index c7768296c6..c0e558c763 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -1692,7 +1692,9 @@ LuaBindings::common (lua_State* L) .addFunction ("envelope_active", &AudioRegion::envelope_active) .addFunction ("fade_in_active", &AudioRegion::fade_in_active) .addFunction ("fade_out_active", &AudioRegion::fade_out_active) + .addFunction ("fade_before_fx", &AudioRegion::fade_before_fx) .addFunction ("set_envelope_active", &AudioRegion::set_envelope_active) + .addFunction ("set_fade_before_fx", &AudioRegion::set_fade_before_fx) .addFunction ("set_fade_in_active", &AudioRegion::set_fade_in_active) .addFunction ("set_fade_in_shape", &AudioRegion::set_fade_in_shape) .addFunction ("set_fade_in_length", &AudioRegion::set_fade_in_length)