Re-work fade operation to be closer to Mixbus; things
below the top region's fades are implicitly faded in the opposite sense; restore short crossfades option. git-svn-id: svn://localhost/ardour2/branches/3.0@12022 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
95f5c3cc80
commit
2072261bb0
@ -149,6 +149,17 @@ struct ReadSorter {
|
||||
}
|
||||
};
|
||||
|
||||
/** A segment of region that needs to be read */
|
||||
struct Segment {
|
||||
Segment (boost::shared_ptr<AudioRegion> r, Evoral::Range<framepos_t> a) : region (r), range (a) {}
|
||||
|
||||
boost::shared_ptr<AudioRegion> region; ///< the region
|
||||
Evoral::Range<framepos_t> range; ///< range of the region to read, in session frames
|
||||
};
|
||||
|
||||
/** @param start Start position in session frames.
|
||||
* @param cnt Number of frames to read.
|
||||
*/
|
||||
ARDOUR::framecnt_t
|
||||
AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
|
||||
framecnt_t cnt, unsigned chan_n)
|
||||
@ -184,16 +195,18 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
|
||||
all->sort (ReadSorter ());
|
||||
|
||||
/* This will be a list of the bits of our read range that we have
|
||||
read completely (ie for which no more regions need to be read).
|
||||
handled completely (ie for which no more regions need to be read).
|
||||
It is a list of ranges in session frames.
|
||||
*/
|
||||
Evoral::RangeList<framepos_t> done;
|
||||
|
||||
/* This will be a list of the bits of regions that we need to read */
|
||||
list<Segment> to_do;
|
||||
|
||||
/* Now go through the `all' list filling in `to_do' and `done' */
|
||||
for (RegionList::iterator i = all->begin(); i != all->end(); ++i) {
|
||||
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*i);
|
||||
|
||||
/* Trim region range to the bit we are reading */
|
||||
|
||||
/* Work out which bits of this region need to be read;
|
||||
first, trim to the range we are reading...
|
||||
*/
|
||||
@ -202,19 +215,17 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
|
||||
region_range.to = min (region_range.to, start + cnt - 1);
|
||||
|
||||
/* ... and then remove the bits that are already done */
|
||||
Evoral::RangeList<framepos_t> to_do = Evoral::subtract (region_range, done);
|
||||
Evoral::RangeList<framepos_t> region_to_do = Evoral::subtract (region_range, done);
|
||||
|
||||
/* Read those bits, adding their bodies (the parts between end-of-fade-in
|
||||
and start-of-fade-out) to the `done' list.
|
||||
*/
|
||||
|
||||
Evoral::RangeList<framepos_t>::List t = to_do.get ();
|
||||
Evoral::RangeList<framepos_t>::List t = region_to_do.get ();
|
||||
|
||||
for (Evoral::RangeList<framepos_t>::List::iterator i = t.begin(); i != t.end(); ++i) {
|
||||
Evoral::Range<framepos_t> d = *i;
|
||||
|
||||
/* Read the whole range, possibly including fades */
|
||||
ar->read_at (buf + d.from - start, mixdown_buffer, gain_buffer, d.from, d.to - d.from + 1, chan_n);
|
||||
for (Evoral::RangeList<framepos_t>::List::iterator j = t.begin(); j != t.end(); ++j) {
|
||||
Evoral::Range<framepos_t> d = *j;
|
||||
to_do.push_back (Segment (ar, d));
|
||||
|
||||
if (ar->opaque ()) {
|
||||
/* Cut this range down to just the body and mark it done */
|
||||
@ -228,6 +239,11 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr
|
||||
}
|
||||
}
|
||||
|
||||
/* Now go backwards through the to_do list doing the actual reads */
|
||||
for (list<Segment>::reverse_iterator i = to_do.rbegin(); i != to_do.rend(); ++i) {
|
||||
i->region->read_at (buf + i->range.from - start, mixdown_buffer, gain_buffer, i->range.from, i->range.to - i->range.from + 1, chan_n);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
@ -385,6 +385,10 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
|
||||
The caller has verified that we cover the desired section.
|
||||
*/
|
||||
|
||||
/* See doc/region_read.svg for a drawing which might help to explain
|
||||
what is going on.
|
||||
*/
|
||||
|
||||
assert (cnt >= 0);
|
||||
|
||||
if (n_channels() == 0) {
|
||||
@ -478,7 +482,7 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
|
||||
|
||||
/* READ DATA FROM THE SOURCE INTO mixdown_buffer.
|
||||
We can never read directly into buf, since it may contain data
|
||||
from a transparent region `above' this one in the stack; we
|
||||
from a transparent region `below' this one in the stack; we
|
||||
must always mix.
|
||||
*/
|
||||
|
||||
@ -532,6 +536,12 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
|
||||
if (fade_in_limit != 0) {
|
||||
_fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit);
|
||||
|
||||
/* Fade the current data out */
|
||||
for (framecnt_t n = 0; n < fade_in_limit; ++n) {
|
||||
buf[n] *= 1 - gain_buffer[n];
|
||||
}
|
||||
|
||||
/* Mix our newly-read data in, with the fade */
|
||||
for (framecnt_t n = 0; n < fade_in_limit; ++n) {
|
||||
buf[n] += mixdown_buffer[n] * gain_buffer[n];
|
||||
}
|
||||
@ -540,7 +550,13 @@ AudioRegion::_read_at (const SourceList& srcs, framecnt_t limit,
|
||||
if (fade_out_limit != 0) {
|
||||
framecnt_t const curve_offset = fade_interval_start - (limit - _fade_out->back()->when);
|
||||
_fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit);
|
||||
|
||||
|
||||
/* Fade the current data in */
|
||||
for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
|
||||
buf[m] *= 1 - gain_buffer[n];
|
||||
}
|
||||
|
||||
/* Mix our newly-read data in, with the fade */
|
||||
for (framecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
|
||||
buf[m] += mixdown_buffer[m] * gain_buffer[n];
|
||||
}
|
||||
|
@ -74,8 +74,8 @@ PlaylistReadTest::overlappingReadTest ()
|
||||
{
|
||||
/* Overlapping read; ar0 and ar1 are both 1024 frames long, ar0 starts at 0,
|
||||
ar1 starts at 128. We test a read from 0 to 256, which should consist
|
||||
of the start of ar0, with its fade in, followed by ar1's fade in (mixed with ar0)
|
||||
and some more of ar1.
|
||||
of the start of ar0, with its fade in, followed by ar1's fade in (mixed with ar0
|
||||
faded out with the inverse gain), and some more of ar1.
|
||||
*/
|
||||
|
||||
boost::shared_ptr<AudioRegion> ar0 = boost::dynamic_pointer_cast<AudioRegion> (_region[0]);
|
||||
@ -102,9 +102,11 @@ PlaylistReadTest::overlappingReadTest ()
|
||||
/* ar0's fade in */
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
/* Note: this specific float casting is necessary so that the rounding
|
||||
is done here the same as it is done in AudioPlaylist.
|
||||
is done here the same as it is done in AudioPlaylist; the gain factor
|
||||
must be computed using double precision, with the result then cast
|
||||
to float.
|
||||
*/
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * float (i / 63.0)), _buf[i], 1e-16);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * float (i / (double) 63)), _buf[i], 1e-16);
|
||||
}
|
||||
|
||||
/* bit of ar0 */
|
||||
@ -112,10 +114,12 @@ PlaylistReadTest::overlappingReadTest ()
|
||||
CPPUNIT_ASSERT_EQUAL (i, int (_buf[i]));
|
||||
}
|
||||
|
||||
/* ar1's fade in */
|
||||
/* ar1's fade in with faded-out ar0 */
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
/* Similar carry-on to above with float rounding */
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (i + 128 + float (i * float (i / 63.0)), _buf[i + 128], 1e-4);
|
||||
float const from_ar0 = (128 + i) * float (1 - float (i / (double) 63));
|
||||
float const from_ar1 = i * float (i / (double) 63);
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (from_ar0 + from_ar1, _buf[i + 128], 1e-16);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,10 +147,14 @@ PlaylistReadTest::transparentReadTest ()
|
||||
|
||||
_apl->read (_buf, _mbuf, _gbuf, 0, 1024, 0);
|
||||
|
||||
/* ar0 and ar1 fade-ins, mixed */
|
||||
/* ar0 and ar1 fade-ins; ar1 is on top, so its fade in will implicitly
|
||||
fade in ar0
|
||||
*/
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
float const fade = i / 63.0;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * fade) * 2, _buf[i], 1e-16);
|
||||
float const fade = i / (double) 63;
|
||||
float const ar0 = i * fade * (1 - fade);
|
||||
float const ar1 = i * fade;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (ar0 + ar1, _buf[i], 1e-16);
|
||||
}
|
||||
|
||||
/* ar0 and ar1 bodies, mixed */
|
||||
@ -154,10 +162,12 @@ PlaylistReadTest::transparentReadTest ()
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * 2), _buf[i], 1e-16);
|
||||
}
|
||||
|
||||
/* ar0 and ar1 fade-outs, mixed */
|
||||
/* ar0 and ar1 fade-outs, mixed (with implicit fade-in of ar0) */
|
||||
for (int i = (1024 - 64); i < 1024; ++i) {
|
||||
float const fade = (1023 - i) / 63.0;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (float (i * fade) * 2, _buf[i], 1e-16);
|
||||
float const fade = (1023 - i) / (double) 63;
|
||||
float const ar0 = i * fade * (1 - fade);
|
||||
float const ar1 = i * fade;
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (ar0 + ar1, _buf[i], 1e-16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ class PlaylistReadTest : public TestNeedingPlaylistAndRegions
|
||||
{
|
||||
CPPUNIT_TEST_SUITE (PlaylistReadTest);
|
||||
CPPUNIT_TEST (singleReadTest);
|
||||
CPPUNIT_TEST (overlappingReadTest);
|
||||
CPPUNIT_TEST (transparentReadTest);
|
||||
CPPUNIT_TEST_SUITE_END ();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user