RegionFX: implement pre/post region-fade FX

This commit is contained in:
Robin Gareus 2024-08-13 19:43:13 +02:00
parent 8f5d6295b3
commit 4bcf1d31c6
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
3 changed files with 111 additions and 30 deletions

View file

@ -50,6 +50,7 @@ namespace Properties {
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> default_fade_out;
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> fade_in_active;
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> fade_out_active;
LIBARDOUR_API extern PBD::PropertyDescriptor<bool> fade_before_fx;
LIBARDOUR_API extern PBD::PropertyDescriptor<float> scale_amplitude;
LIBARDOUR_API extern PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > fade_in;
LIBARDOUR_API extern PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > 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<AutomationList> fade_in() { return _fade_in.val (); }
std::shared_ptr<AutomationList> 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<std::shared_ptr<Region> >&) const;
bool remove_plugin (std::shared_ptr<RegionFxPlugin>);
@ -220,6 +224,7 @@ class LIBARDOUR_API AudioRegion : public Region, public AudioReadable
PBD::Property<bool> _default_fade_out;
PBD::Property<bool> _fade_in_active;
PBD::Property<bool> _fade_out_active;
PBD::Property<bool> _fade_before_fx;
/** linear gain to apply to the whole region */
PBD::Property<gain_t> _scale_amplitude;

View file

@ -82,6 +82,7 @@ namespace ARDOUR {
PBD::PropertyDescriptor<bool> default_fade_out;
PBD::PropertyDescriptor<bool> fade_in_active;
PBD::PropertyDescriptor<bool> fade_out_active;
PBD::PropertyDescriptor<bool> fade_before_fx;
PBD::PropertyDescriptor<float> scale_amplitude;
PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > fade_in;
PBD::PropertyDescriptor<std::shared_ptr<AutomationList> > 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<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation), tdp))) \
, _inverse_fade_in (Properties::inverse_fade_in, std::shared_ptr<AutomationList> (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<AutomationList> (new AutomationList (*other->_fade_in.val()))) \
, _inverse_fade_in (Properties::fade_in, std::shared_ptr<AutomationList> (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<Playlist> 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<Playlist> 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;
}
}

View file

@ -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)