#include <glibmm/thread.h>
-#include <pbd/basename.h>
-#include <pbd/xml++.h>
-#include <pbd/stacktrace.h>
-#include <pbd/enumwriter.h>
-#include <pbd/convert.h>
-
-#include <ardour/audioregion.h>
-#include <ardour/session.h>
-#include <ardour/gain.h>
-#include <ardour/dB.h>
-#include <ardour/playlist.h>
-#include <ardour/audiofilesource.h>
-#include <ardour/region_factory.h>
-#include <ardour/runtime_functions.h>
-#include <ardour/transient_detector.h>
+#include "pbd/basename.h"
+#include "pbd/xml++.h"
+#include "pbd/stacktrace.h"
+#include "pbd/enumwriter.h"
+#include "pbd/convert.h"
+
+#include "evoral/Curve.hpp"
+
+#include "ardour/audioregion.h"
+#include "ardour/session.h"
+#include "ardour/gain.h"
+#include "ardour/dB.h"
+#include "ardour/playlist.h"
+#include "ardour/audiofilesource.h"
+#include "ardour/region_factory.h"
+#include "ardour/runtime_functions.h"
+#include "ardour/transient_detector.h"
#include "i18n.h"
#include <locale.h>
set_default_envelope ();
listen_to_my_curves ();
- listen_to_my_sources ();
+ connect_to_analysis_changed ();
}
-/* constructor for use by derived types only */
+/** Constructor for use by derived types only */
AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name)
: Region (s, start, length, name, DataType::AUDIO)
, _automatable(s)
, _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
{
init ();
+ assert (_sources.size() == _master_sources.size());
}
/** Basic AudioRegion constructor (one channel) */
}
init ();
+ assert (_sources.size() == _master_sources.size());
}
/* Basic AudioRegion constructor (one channel) */
}
init ();
+ assert (_sources.size() == _master_sources.size());
}
-/* Basic AudioRegion constructor (many channels) */
+/** Basic AudioRegion constructor (many channels) */
AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
: Region (srcs, start, length, name, DataType::AUDIO, layer, flags)
, _automatable(srcs[0]->session())
, _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
{
init ();
- listen_to_my_sources ();
+ connect_to_analysis_changed ();
+ assert (_sources.size() == _master_sources.size());
}
/** Create a new AudioRegion, that is part of an existing one */
AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
: Region (other, offset, length, name, layer, flags)
, _automatable(other->session())
- , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
- , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
- , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
+ , _fade_in (new AutomationList(*other->_fade_in))
+ , _fade_out (new AutomationList(*other->_fade_out))
+ , _envelope (new AutomationList(*other->_envelope, offset, offset + length))
{
- set<boost::shared_ptr<Source> > unique_srcs;
-
- for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
- _sources.push_back (*i);
-
- pair<set<boost::shared_ptr<Source> >::iterator,bool> result;
-
- result = unique_srcs.insert (*i);
-
- if (result.second) {
- boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
- if (afs) {
- afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
- }
- }
- }
+ connect_to_header_position_offset_changed ();
/* return to default fades if the existing ones are too long */
- init ();
if (_flags & LeftOfSplit) {
if (_fade_in->back()->when >= _length) {
_scale_amplitude = other->_scale_amplitude;
assert(_type == DataType::AUDIO);
- listen_to_my_sources ();
+
+ listen_to_my_curves ();
+ connect_to_analysis_changed ();
+
+ assert (_sources.size() == _master_sources.size());
}
AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
: Region (other)
- , _automatable(other->session())
- , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
- , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
- , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
+ , _automatable (other->session())
+ , _fade_in (new AutomationList (*other->_fade_in))
+ , _fade_out (new AutomationList (*other->_fade_out))
+ , _envelope (new AutomationList (*other->_envelope))
{
assert(_type == DataType::AUDIO);
_scale_amplitude = other->_scale_amplitude;
- _envelope = other->_envelope;
set_default_fades ();
-
+
listen_to_my_curves ();
- listen_to_my_sources ();
+ connect_to_analysis_changed ();
+
+ assert (_sources.size() == _master_sources.size());
+}
+
+AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const SourceList& /*srcs*/,
+ nframes_t length, const string& name, layer_t layer, Flag flags)
+ : Region (other, length, name, layer, flags)
+ , _automatable (other->session())
+ , _fade_in (new AutomationList (*other->_fade_in))
+ , _fade_out (new AutomationList (*other->_fade_out))
+ , _envelope (new AutomationList (*other->_envelope))
+{
+ /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */
+
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> ((*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 ();
+ connect_to_analysis_changed ();
+
+ assert (_sources.size() == _master_sources.size());
}
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
}
assert(_type == DataType::AUDIO);
- listen_to_my_sources ();
+ connect_to_analysis_changed ();
+
+ assert (_sources.size() == _master_sources.size());
}
AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
}
assert(_type == DataType::AUDIO);
- listen_to_my_sources ();
+ connect_to_analysis_changed ();
+ assert (_sources.size() == _master_sources.size());
}
AudioRegion::~AudioRegion ()
}
void
-AudioRegion::listen_to_my_sources ()
+AudioRegion::connect_to_analysis_changed ()
{
for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
(*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients));
}
}
+void
+AudioRegion::connect_to_header_position_offset_changed ()
+{
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+
+ if (unique_srcs.find (*i) == unique_srcs.end ()) {
+ unique_srcs.insert (*i);
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
+ if (afs) {
+ afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
+ }
+ }
+ }
+}
+
void
AudioRegion::listen_to_my_curves ()
{
}
}
-nframes64_t
-AudioRegion::read (Sample* buf, nframes64_t position, nframes64_t cnt, int channel) const
+nframes_t
+AudioRegion::read (Sample* buf, sframes_t timeline_position, nframes_t cnt, int channel) const
{
/* raw read, no fades, no gain, nada */
- return _read_at (_sources, _length, buf, 0, 0, _position + position, cnt, channel, 0, 0, true);
+ return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
}
nframes_t
-AudioRegion::read_at (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
+AudioRegion::read_with_ops (Sample* buf, sframes_t file_position, nframes_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,
+ sframes_t file_position, nframes_t cnt, uint32_t chan_n,
+ nframes_t read_frames, nframes_t skip_frames) const
{
/* regular diskstream/butler read complete with fades etc */
- return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames, false);
+ 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
+AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
+ sframes_t position, nframes_t cnt, uint32_t chan_n) const
{
- return _read_at (_master_sources, _master_sources.front()->length(), 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(_master_sources.front()->timeline_position()),
+ buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0, ReadOps (0));
}
nframes_t
-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,
- bool raw) const
+AudioRegion::_read_at (const SourceList& /*srcs*/, nframes_t limit,
+ Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
+ sframes_t position, nframes_t cnt,
+ 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() && !raw) {
return 0; /* read nothing */
mixdown_buffer += buf_offset;
}
- if (!raw) {
+ if (rops & ReadOpsCount) {
_read_data_count = 0;
}
return 0; /* "read nothing" */
}
- if (!raw) {
+ if (rops & ReadOpsCount) {
_read_data_count += src->read_data_count();
}
*/
memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
-
- /* no fades required */
-
- if (!raw) {
- goto merge;
- }
}
- /* fade in */
-
- if (!raw) {
+ if (rops & ReadOpsFades) {
- if ((_flags & FadeIn) && Config->get_use_region_fades()) {
+ /* fade in */
+
+ if ((_flags & FadeIn) && _session.config.get_use_region_fades()) {
nframes_t fade_in_length = (nframes_t) _fade_in->back()->when;
fi_limit = min (to_read, fade_in_length - internal_offset);
+
_fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
for (nframes_t n = 0; n < fi_limit; ++n) {
/* fade out */
- if ((_flags & FadeOut) && Config->get_use_region_fades()) {
+ if ((_flags & FadeOut) && _session.config.get_use_region_fades()) {
/* see if some part of this read is within the fade out */
/* ................. >| REGION
- limit
+ limit
{ } FADE
fade_out_length
^
- limit - fade_out_length
+ limit - fade_out_length
|--------------|
^internal_offset
^internal_offset + to_read
}
}
+ }
- /* Regular gain curves */
-
- if (envelope_active()) {
- _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
+ /* Regular gain curves and scaling */
+
+ if ((rops & ReadOpsOwnAutomation) && envelope_active()) {
+ _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
- if (_scale_amplitude != 1.0f) {
- for (nframes_t n = 0; n < to_read; ++n) {
- mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
- }
- } else {
- for (nframes_t n = 0; n < to_read; ++n) {
- mixdown_buffer[n] *= gain_buffer[n];
- }
+ if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
+ for (nframes_t n = 0; n < to_read; ++n) {
+ mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
+ }
+ } else {
+ for (nframes_t n = 0; n < to_read; ++n) {
+ mixdown_buffer[n] *= gain_buffer[n];
}
- } else if (_scale_amplitude != 1.0f) {
- apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
}
+ } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
+
+ // XXX this should be using what in 2.0 would have been:
+ // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
+
+ for (nframes_t n = 0; n < to_read; ++n) {
+ mixdown_buffer[n] *= _scale_amplitude;
+ }
+ }
- merge:
+ if (!opaque()) {
- if (!opaque()) {
-
- /* gack. the things we do for users.
- */
-
- buf += buf_offset;
+ /* gack. the things we do for users.
+ */
+
+ buf += buf_offset;
- for (nframes_t n = 0; n < to_read; ++n) {
- buf[n] += mixdown_buffer[n];
- }
- }
- }
+ for (nframes_t n = 0; n < to_read; ++n) {
+ buf[n] += mixdown_buffer[n];
+ }
+ }
return to_read;
}
_flags = Flag (_flags & ~Region::RightOfSplit);
}
+ /* leave this flag setting in place, no matter what */
+
+ if ((old_flags & DoNotSendPropertyChanges)) {
+ _flags = Flag (_flags | DoNotSendPropertyChanges);
+ }
+
+ /* find out if any flags changed that we signal about */
+
if ((old_flags ^ _flags) & Muted) {
what_changed = Change (what_changed|MuteChanged);
}
void
AudioRegion::set_default_fade_in ()
{
+ _fade_in_disabled = 0;
set_fade_in (Linear, 64);
}
void
AudioRegion::set_default_fade_out ()
{
+ _fade_out_disabled = 0;
set_fade_out (Linear, 64);
}
void
AudioRegion::set_default_fades ()
{
- _fade_in_disabled = 0;
- _fade_out_disabled = 0;
set_default_fade_in ();
set_default_fade_out ();
}
}
int
-AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<AudioRegion> >& v) const
+AudioRegion::separate_by_channel (Session& /*session*/, vector<boost::shared_ptr<Region> >& v) const
{
SourceList srcs;
string new_name;
- int n;
+ int n = 0;
if (_sources.size() < 2) {
return 0;
}
- n = 0;
-
for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-
srcs.clear ();
srcs.push_back (*i);
new_name += ('0' + n + 1);
}
- /* create a copy with just one source. prevent if from being thought of as "whole file" even if
- it covers the entire source file(s).
+ /* create a copy with just one source. prevent if from being thought of as
+ "whole file" even if it covers the entire source file(s).
*/
Flag f = Flag (_flags & ~WholeFile);
- boost::shared_ptr<Region> r = RegionFactory::create (srcs, _start, _length, new_name, _layer, f);
- boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
-
- v.push_back (ar);
+ v.push_back(RegionFactory::create (srcs, _start, _length, new_name, _layer, f));
++n;
}
}
nframes_t
-AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const
+AudioRegion::read_raw_internal (Sample* buf, sframes_t pos, nframes_t cnt, int channel) const
{
- return audio_source()->read (buf, pos, cnt);
+ return audio_source()->read (buf, pos, cnt, channel);
}
int
-AudioRegion::exportme (Session& session, ARDOUR::ExportSpecification& spec)
+AudioRegion::exportme (Session& /*session*/, ARDOUR::ExportSpecification& /*spec*/)
{
// TODO EXPORT
// const nframes_t blocksize = 4096;
/* read it in */
- if (read_raw_internal (buf, fpos, to_read) != to_read) {
+ if (read_raw_internal (buf, fpos, to_read, 0) != to_read) {
return;
}
return 0;
}
+/** Find areas of `silence' within a region.
+ *
+ * @param threshold Threshold below which signal is considered silence (as a sample value)
+ * @param min_length Minimum length of silent period to be reported.
+ * @return Silent periods; first of pair is the offset within the region, second is the length of the period
+ */
+
+std::list<std::pair<nframes_t, nframes_t> >
+AudioRegion::find_silence (Sample threshold, nframes_t min_length) const
+{
+ nframes_t const block_size = 64 * 1024;
+ Sample loudest[block_size];
+ Sample buf[block_size];
+
+ nframes_t pos = _start;
+ nframes_t const end = _start + _length - 1;
+
+ std::list<std::pair<nframes_t, nframes_t> > silent_periods;
+
+ bool in_silence = false;
+ nframes_t silence_start = 0;
+ bool silence;
+
+ while (pos < end) {
+
+ /* fill `loudest' with the loudest absolute sample at each instant, across all channels */
+ memset (loudest, 0, sizeof (Sample) * block_size);
+ for (uint32_t n = 0; n < n_channels(); ++n) {
+
+ read_raw_internal (buf, pos, block_size, n);
+ for (nframes_t i = 0; i < block_size; ++i) {
+ loudest[i] = max (loudest[i], abs (buf[i]));
+ }
+ }
+
+ /* now look for silence */
+ for (nframes_t i = 0; i < block_size; ++i) {
+ silence = abs (loudest[i]) < threshold;
+ if (silence && !in_silence) {
+ /* non-silence to silence */
+ in_silence = true;
+ silence_start = pos + i;
+ } else if (!silence && in_silence) {
+ /* silence to non-silence */
+ in_silence = false;
+ if (pos + i - 1 - silence_start >= min_length) {
+ silent_periods.push_back (std::make_pair (silence_start, pos + i - 1));
+ }
+ }
+ }
+
+ pos += block_size;
+ }
+
+ if (in_silence && end - 1 - silence_start >= min_length) {
+ /* last block was silent, so finish off the last period */
+ silent_periods.push_back (std::make_pair (silence_start, end));
+ }
+
+ return silent_periods;
+}
+
+
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)