X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudioregion.cc;h=6163897b2015c24ce9434fd23706efc5ff676649;hb=5a3a2fad4fd1c217a5771caa64efe92a0b305275;hp=4a82303cdb50b1f9b4860d1912a10e93f18ed59c;hpb=cb534fd536125fc0a1654f51d077424a58fda06e;p=ardour.git diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 4a82303cdb..6163897b20 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include "i18n.h" #include @@ -83,6 +85,7 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n set_default_envelope (); listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) @@ -108,17 +111,18 @@ AudioRegion::AudioRegion (boost::shared_ptr src, nframes_t start, n set_default_envelope (); listen_to_my_curves (); + listen_to_my_sources (); } -AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) +AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags) : Region (start, length, name, layer, flags), _fade_in (0.0, 2.0, 1.0, false), _fade_out (0.0, 2.0, 1.0, false), _envelope (0.0, 2.0, 1.0, false) { /* basic AudioRegion constructor */ - - for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) { + + for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) { sources.push_back (*i); master_sources.push_back (*i); (*i)->GoingAway.connect (mem_fun (*this, &AudioRegion::source_deleted)); @@ -135,6 +139,7 @@ AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, c set_default_envelope (); listen_to_my_curves (); + listen_to_my_sources (); } @@ -200,13 +205,15 @@ AudioRegion::AudioRegion (boost::shared_ptr other, nframes_t _scale_amplitude = other->_scale_amplitude; listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (boost::shared_ptr other) : Region (other), _fade_in (other->_fade_in), _fade_out (other->_fade_out), - _envelope (other->_envelope) + _envelope (other->_envelope) + { /* Pure copy constructor */ @@ -235,12 +242,44 @@ AudioRegion::AudioRegion (boost::shared_ptr other) } _scale_amplitude = other->_scale_amplitude; - _envelope = other->_envelope; _fade_in_disabled = 0; _fade_out_disabled = 0; - + listen_to_my_curves (); + listen_to_my_sources (); +} + +AudioRegion::AudioRegion (boost::shared_ptr other, const SourceList& srcs, + nframes_t length, const string& name, layer_t layer, Flag flags) + : Region (other, length, name, layer, flags), + _fade_in (other->_fade_in), + _fade_out (other->_fade_out), + _envelope (other->_envelope) + +{ + /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */ + + set > unique_srcs; + + for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) { + sources.push_back (*i); + master_sources.push_back (*i); + (*i)->GoingAway.connect (mem_fun (*this, &AudioRegion::source_deleted)); + + boost::shared_ptr afs = boost::dynamic_pointer_cast ((*i)); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + } + + _scale_amplitude = other->_scale_amplitude; + + _fade_in_disabled = 0; + _fade_out_disabled = 0; + + listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& node) @@ -265,6 +304,7 @@ AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& nod } listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) @@ -305,6 +345,7 @@ AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node) } listen_to_my_curves (); + listen_to_my_sources (); } AudioRegion::~AudioRegion () @@ -315,12 +356,23 @@ AudioRegion::~AudioRegion () for (SourceList::const_iterator i = sources.begin(); i != sources.end(); ++i) { (*i)->remove_playlist (pl); } + for (SourceList::const_iterator i = master_sources.begin(); i != master_sources.end(); ++i) { + (*i)->remove_playlist (pl); + } } notify_callbacks (); GoingAway (); /* EMIT SIGNAL */ } +void +AudioRegion::listen_to_my_sources () +{ + for (SourceList::const_iterator i = sources.begin(); i != sources.end(); ++i) { + (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients)); + } +} + void AudioRegion::listen_to_my_curves () { @@ -330,7 +382,7 @@ AudioRegion::listen_to_my_curves () } bool -AudioRegion::verify_length (nframes_t len) +AudioRegion::verify_length (nframes_t& len) { boost::shared_ptr afs = boost::dynamic_pointer_cast(source()); @@ -338,16 +390,19 @@ AudioRegion::verify_length (nframes_t len) return true; } + nframes_t maxlen = 0; + for (uint32_t n=0; n < sources.size(); ++n) { - if (_start > sources[n]->length() - len) { - return false; - } + maxlen = max (maxlen, sources[n]->length() - _start); } + + len = min (len, maxlen); + return true; } bool -AudioRegion::verify_start_and_length (nframes_t new_start, nframes_t new_length) +AudioRegion::verify_start_and_length (nframes_t new_start, nframes_t& new_length) { boost::shared_ptr afs = boost::dynamic_pointer_cast(source()); @@ -355,13 +410,17 @@ AudioRegion::verify_start_and_length (nframes_t new_start, nframes_t new_length) return true; } + nframes_t maxlen = 0; + for (uint32_t n=0; n < sources.size(); ++n) { - if (new_length > sources[n]->length() - new_start) { - return false; - } + maxlen = max (maxlen, sources[n]->length() - new_start); } + + new_length = min (new_length, maxlen); + return true; } + bool AudioRegion::verify_start (nframes_t pos) { @@ -431,31 +490,54 @@ AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nfra } } +nframes64_t +AudioRegion::read (Sample* buf, nframes64_t timeline_position, nframes64_t cnt, int channel) const +{ + /* raw read, no fades, no gain, nada */ + return _read_at (sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0)); +} + +nframes64_t +AudioRegion::read_with_ops (Sample* buf, nframes64_t file_position, nframes64_t cnt, int channel, ReadOps rops) const +{ + return _read_at (sources, _length, buf, 0, 0, file_position, cnt, channel, 0, 0, rops); +} + nframes_t -AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, +AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t file_position, nframes_t cnt, uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const { - return _read_at (sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames); + /* regular diskstream/butler read complete with fades etc */ + return _read_at (sources, _length, buf, mixdown_buffer, gain_buffer, file_position, cnt, + chan_n, read_frames, skip_frames, ReadOps (~0)); } nframes_t AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, nframes_t cnt, uint32_t chan_n) const { - return _read_at (master_sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0); + /* do not read gain/scaling/fades and do not count this disk i/o in statistics */ + + return _read_at (master_sources, master_sources.front()->length(), buf, mixdown_buffer, + gain_buffer, position, cnt, chan_n, 0, 0, ReadOps (0)); } nframes_t -AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buffer, float *gain_buffer, +AudioRegion::_read_at (const SourceList& srcs, nframes_t limit, + Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, nframes_t cnt, - uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const + uint32_t chan_n, + nframes_t read_frames, + nframes_t skip_frames, + ReadOps rops) const { nframes_t internal_offset; nframes_t buf_offset; nframes_t to_read; + bool raw = (rops == ReadOpsNone); - if (muted()) { + if (muted() && !raw) { return 0; /* read nothing */ } @@ -470,31 +552,34 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff buf_offset = 0; } - if (internal_offset >= _length) { + if (internal_offset >= limit) { return 0; /* read nothing */ } - if ((to_read = min (cnt, _length - internal_offset)) == 0) { + if ((to_read = min (cnt, limit - internal_offset)) == 0) { return 0; /* read nothing */ } - if (opaque()) { + if (opaque() || raw) { /* overwrite whatever is there */ mixdown_buffer = buf + buf_offset; } else { mixdown_buffer += buf_offset; } - _read_data_count = 0; + if (rops & ReadOpsCount) { + _read_data_count = 0; + } if (chan_n < n_channels()) { if (srcs[chan_n]->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) { - return 0; /* "read nothing" */ } - _read_data_count += srcs[chan_n]->read_data_count(); + if (rops & ReadOpsCount) { + _read_data_count += srcs[chan_n]->read_data_count(); + } } else { @@ -503,83 +588,82 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff */ memset (mixdown_buffer, 0, sizeof (Sample) * cnt); - - /* no fades required */ - - goto merge; } - /* fade in */ - - if (_flags & FadeIn) { - - nframes_t fade_in_length = (nframes_t) _fade_in.back()->when; - - /* see if this read is within the fade in */ - - if (internal_offset < fade_in_length) { - - nframes_t limit; - - limit = min (to_read, fade_in_length - internal_offset); - - _fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit); + if (rops & ReadOpsFades) { + + /* fade in */ - for (nframes_t n = 0; n < limit; ++n) { - mixdown_buffer[n] *= gain_buffer[n]; + if ((_flags & FadeIn) && Config->get_use_region_fades()) { + + nframes_t fade_in_length = (nframes_t) _fade_in.back()->when; + + /* see if this read is within the fade in */ + + if (internal_offset < fade_in_length) { + + nframes_t fi_limit; + + fi_limit = min (to_read, fade_in_length - internal_offset); + + _fade_in.get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit); + + for (nframes_t n = 0; n < fi_limit; ++n) { + mixdown_buffer[n] *= gain_buffer[n]; + } } } - } - - /* fade out */ - - if (_flags & FadeOut) { - - /* see if some part of this read is within the fade out */ + + /* fade out */ + + if ((_flags & FadeOut) && Config->get_use_region_fades()) { + /* see if some part of this read is within the fade out */ + /* ................. >| REGION - _length + limit { } FADE fade_out_length ^ - _length - fade_out_length + limit - fade_out_length |--------------| ^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 + [limit - fade_out_length, limit] + */ - nframes_t fade_out_length = (nframes_t) _fade_out.back()->when; - nframes_t fade_interval_start = max(internal_offset, _length-fade_out_length); - nframes_t fade_interval_end = min(internal_offset + to_read, _length); + nframes_t fade_out_length = (nframes_t) _fade_out.back()->when; + nframes_t fade_interval_start = max(internal_offset, limit-fade_out_length); + nframes_t fade_interval_end = min(internal_offset + to_read, limit); - if (fade_interval_end > fade_interval_start) { - /* (part of the) the fade out is in this buffer */ - - nframes_t limit = fade_interval_end - fade_interval_start; - nframes_t curve_offset = fade_interval_start - (_length-fade_out_length); - nframes_t fade_offset = fade_interval_start - internal_offset; - - _fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit); - - for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) { - mixdown_buffer[m] *= gain_buffer[n]; - } - } + if (fade_interval_end > fade_interval_start) { + /* (part of the) the fade out is in this buffer */ + nframes_t fo_limit = fade_interval_end - fade_interval_start; + nframes_t curve_offset = fade_interval_start - (limit-fade_out_length); + nframes_t fade_offset = fade_interval_start - internal_offset; + + _fade_out.get_vector (curve_offset,curve_offset+fo_limit, gain_buffer, fo_limit); + + for (nframes_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) { + mixdown_buffer[m] *= gain_buffer[n]; + } + } + + } } - - /* Regular gain curves */ - - if (envelope_active()) { + + /* Regular gain curves and scaling */ + + if ((rops & ReadOpsOwnAutomation) && envelope_active()) { _envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read); - if (_scale_amplitude != 1.0f) { + if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) { for (nframes_t n = 0; n < to_read; ++n) { mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude; } @@ -588,24 +672,22 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff mixdown_buffer[n] *= gain_buffer[n]; } } - } else if (_scale_amplitude != 1.0f) { + } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) { Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude); } - - merge: - + if (!opaque()) { - + /* gack. the things we do for users. */ - - buf += buf_offset; + buf += buf_offset; + for (nframes_t n = 0; n < to_read; ++n) { buf[n] += mixdown_buffer[n]; } } - + return to_read; } @@ -704,7 +786,7 @@ AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool sen Region::set_live_state (node, what_changed, false); uint32_t old_flags = _flags; - + if ((prop = node.property ("flags")) != 0) { _flags = Flag (string_2_enum (prop->value(), _flags)); @@ -755,17 +837,44 @@ AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool sen _fade_in.clear (); - if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0 || _fade_in.set_state (*child)) { + if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) { set_default_fade_in (); - } + } else { + XMLNode* grandchild = child->child ("AutomationList"); + if (grandchild) { + _fade_in.set_state (*grandchild); + } + } + + if ((prop = child->property ("active")) != 0) { + if (string_is_affirmative (prop->value())) { + set_fade_in_active (true); + } else { + set_fade_in_active (true); + } + } } else if (child->name() == "FadeOut") { _fade_out.clear (); - if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0 || _fade_out.set_state (*child)) { + if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) { set_default_fade_out (); - } + } else { + XMLNode* grandchild = child->child ("AutomationList"); + if (grandchild) { + _fade_out.set_state (*grandchild); + } + } + + if ((prop = child->property ("active")) != 0) { + if (string_is_affirmative (prop->value())) { + set_fade_out_active (true); + } else { + set_fade_out_active (false); + } + } + } } @@ -920,6 +1029,10 @@ AudioRegion::set_fade_out (FadeShape shape, nframes_t len) void AudioRegion::set_fade_in_length (nframes_t len) { + if (len > _length) { + len = _length - 1; + } + bool changed = _fade_in.extend_to (len); if (changed) { @@ -931,13 +1044,16 @@ AudioRegion::set_fade_in_length (nframes_t len) void AudioRegion::set_fade_out_length (nframes_t len) { + if (len > _length) { + len = _length - 1; + } + bool changed = _fade_out.extend_to (len); if (changed) { _flags = Flag (_flags & ~DefaultFadeOut); + send_change (FadeOutChanged); } - - send_change (FadeOutChanged); } void @@ -1123,7 +1239,7 @@ AudioRegion::master_source_names () } void -AudioRegion::set_master_sources (SourceList& srcs) +AudioRegion::set_master_sources (const SourceList& srcs) { master_sources = srcs; } @@ -1400,6 +1516,15 @@ AudioRegion::speed_mismatch (float sr) const void AudioRegion::source_offset_changed () { + /* XXX this fixes a crash that should not occur. It does occur + becauses regions are not being deleted when a session + is unloaded. That bug must be fixed. + */ + + if (sources.empty()) { + return; + } + boost::shared_ptr afs = boost::dynamic_pointer_cast(sources.front()); if (afs && afs->destructive()) { @@ -1427,20 +1552,160 @@ AudioRegion::set_playlist (boost::weak_ptr wpl) (*i)->remove_playlist (_playlist); (*i)->add_playlist (pl); } + for (SourceList::const_iterator i = master_sources.begin(); i != master_sources.end(); ++i) { + (*i)->remove_playlist (_playlist); + (*i)->add_playlist (pl); + } } else { for (SourceList::const_iterator i = sources.begin(); i != sources.end(); ++i) { (*i)->add_playlist (pl); } + for (SourceList::const_iterator i = master_sources.begin(); i != master_sources.end(); ++i) { + (*i)->add_playlist (pl); + } } } else { if (old_playlist) { for (SourceList::const_iterator i = sources.begin(); i != sources.end(); ++i) { (*i)->remove_playlist (old_playlist); } + for (SourceList::const_iterator i = master_sources.begin(); i != master_sources.end(); ++i) { + (*i)->remove_playlist (old_playlist); + } } } } +int +AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new) +{ + boost::shared_ptr pl = playlist(); + + if (!pl) { + return -1; + } + + if (valid_transients && !force_new) { + results = _transients; + return 0; + } + + SourceList::iterator s; + + for (s = sources.begin() ; s != sources.end(); ++s) { + if (!(*s)->has_been_analysed()) { + cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n"; + break; + } + } + + if (s == sources.end()) { + /* all sources are analyzed, merge data from each one */ + + for (s = sources.begin() ; s != sources.end(); ++s) { + + /* find the set of transients within the bounds of this region */ + + AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(), + (*s)->transients.end(), + _start); + + AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(), + (*s)->transients.end(), + _start + _length); + + /* and add them */ + + results.insert (results.end(), low, high); + } + + TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0); + + /* translate all transients to current position */ + + for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) { + (*x) -= _start; + (*x) += _position; + } + + _transients = results; + valid_transients = true; + + return 0; + } + + /* no existing/complete transient info */ + + static bool analyse_dialog_shown = false; + if (!Config->get_auto_analyse_audio()) { + 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.\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; //only show this dialog once + } + } + + TransientDetector t (pl->session().frame_rate()); + bool existing_results = !results.empty(); + + _transients.clear (); + valid_transients = false; + + for (uint32_t i = 0; i < n_channels(); ++i) { + + AnalysisFeatureList these_results; + + t.reset (); + + if (t.run ("", this, i, these_results)) { + return -1; + } + + /* translate all transients to give absolute position */ + + for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) { + (*i) += _position; + } + + /* merge */ + + _transients.insert (_transients.end(), these_results.begin(), these_results.end()); + } + + if (!results.empty()) { + if (existing_results) { + + /* merge our transients into the existing ones, then clean up + those. + */ + + results.insert (results.end(), _transients.begin(), _transients.end()); + TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0); + } + + /* make sure ours are clean too */ + + TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0); + + } else { + + TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0); + results = _transients; + } + + valid_transients = true; + + return 0; +} + extern "C" { 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)