Skip to content

Commit

Permalink
Fix a Sus=0 special case with quadratic ditigal decay (#1511)
Browse files Browse the repository at this point in the history
In a special case of the digital envelope decay type 1
and sustain exactly 0, you would oscillate away from zero rather than getting
there. There's a math reason for this but it is undesirable so
fix this special case with a limit if.

Closes #1509
  • Loading branch information
baconpaul authored Jan 25, 2020
1 parent d4e5017 commit 6b0dbb3
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 12 deletions.
6 changes: 6 additions & 0 deletions src/common/dsp/AdsrEnvelope.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,19 @@ class AdsrEnvelope : public ModulationSource
envelope_rate_linear(lc[d].f) * (adsr->d.temposync ? storage->temposyncratio : 1.f);

float l_lo, l_hi;

switch (lc[d_s].i)
{
case 1:
{
float sx = sqrt(phase);
l_lo = phase - 2 * sx * rate + rate * rate;
l_hi = phase + 2 * sx * rate + rate * rate;
// That + rate * rate in both means at low sustain ( < 1e-3 or so) you end up with
// lo and hi both pushing us up off sustain. Unfortunatley we ned to handle that case
// specially by pushing lo down
if( lc[s].f < 1e-3 && phase < 1e-4 )
l_lo = 0;
}
break;
case 2:
Expand Down
43 changes: 31 additions & 12 deletions src/headless/UnitTestsMOD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )
{
p->set_value_f01( p->value_to_normalized( limit_range( vn, p->val_min.f, p->val_max.f ) ) );
};


auto svni = [](Parameter *p, int vn)
{
p->val.i = vn;
};

auto inverseEnvtime = [](float desiredTime)
{
// 2^x = desired time
Expand All @@ -59,9 +64,9 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )
svn(&(adsrstorage->s), s);
svn(&(adsrstorage->r), inverseEnvtime(r));

svn(&(adsrstorage->a_s), a_s);
svn(&(adsrstorage->d_s), d_s);
svn(&(adsrstorage->r_s), r_s);
svni(&(adsrstorage->a_s), a_s);
svni(&(adsrstorage->d_s), d_s);
svni(&(adsrstorage->r_s), r_s);

adsrstorage->mode.val.b = isAnalog;

Expand Down Expand Up @@ -116,8 +121,8 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )
{
int ldir = 0;
if( v > 0.999999f ) ldir = dir; // sometimes we get a double '1'
if( fabs( v - pv ) < 5e-6 && fabs( v ) < 1e-5) ldir = 0; // bouncing off of 0 is annoying
else if( fabs( v - pv ) < 5e-7 ) ldir = 0;
if( turns.size() > 1 && fabs( v - pv ) < 5e-6 && fabs( v ) < 1e-5) ldir = 0; // bouncing off of 0 is annoying
else if( fabs( v - pv ) < 1e-7 ) ldir = 0;
else if( v > pv ) ldir = 1;
else ldir = -1;

Expand All @@ -144,8 +149,7 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )

auto simple = runAdsr( a, d, s, r, a_s, d_s, r_s, isAnalog, a + d + sustime, totaltime );
auto sturns = detectTurnarounds(simple);
if( false )
std::cout << "ADSR: " << a << " " << d << " " << s << " " << r << " switches: " << a_s << " " << d_s << " " << r_s << std::endl;
INFO( "ADSR: " << a << " " << d << " " << s << " " << r << " switches: " << a_s << " " << d_s << " " << r_s );
if( s == 0 )
{
if( sturns.size() != 3 )
Expand All @@ -169,6 +173,7 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )
{
for( auto s : simple )
std::cout << s.first << " " << s.second << std::endl;
std::cout << "TURNS" << std::endl;
for( auto s : sturns )
std::cout << s.first << " " << s.second << std::endl;
}
Expand Down Expand Up @@ -216,7 +221,22 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )
}
}
}


SECTION( "Quadrtic Digital hits Zero" )
{
auto res = runAdsr( 0.1, 0.1, 0.0, 0.1,
0, 1, 0,
false,
0.4, 0.5 );
for( auto p : res )
{
if( p.first > 0.22 )
{
REQUIRE( p.second == 0.f );
}
}
}

SECTION( "Test the Analog Envelope" )
{
// OK so we can't check the same thing here since the turns aren't as tight in analog mode
Expand Down Expand Up @@ -429,11 +449,11 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )

for( int rc=0;rc<100; ++rc )
{
float a = rand() * 1.0 / RAND_MAX + 0.01;
float a = rand() * 1.0 / RAND_MAX + 0.03;
float d = rand() * 1.0 / RAND_MAX + 0.01;
float s = 0.7 * rand() * 1.0 / RAND_MAX + 0.1; // we have tested the s=0 case above
float r = rand() * 1.0 / RAND_MAX + 0.01;
INFO( "Testing with ADSR=" << a << " " << d << " " << s << " " << r );
INFO( "Testing " << rc << " with ADSR=" << a << " " << d << " " << s << " " << r );
compareSrgRepl( a, d, s, r );

}
Expand Down Expand Up @@ -488,7 +508,6 @@ TEST_CASE( "ADSR Envelope Behaviour", "[mod]" )
testSusPush( s1, s2 );
}
}

}

TEST_CASE( "Non-MPE pitch bend", "[mod]" )
Expand Down

0 comments on commit 6b0dbb3

Please sign in to comment.