diff --git a/src/common/dsp/AdsrEnvelope.h b/src/common/dsp/AdsrEnvelope.h index e07ce2fe48a..d17ab45da0d 100644 --- a/src/common/dsp/AdsrEnvelope.h +++ b/src/common/dsp/AdsrEnvelope.h @@ -314,6 +314,8 @@ class AdsrEnvelope : public ModulationSource } } + int getEnvState() { return envstate; } + private: ADSRStorage* adsr = nullptr; SurgeVoiceState* state = nullptr; diff --git a/src/headless/UnitTests.cpp b/src/headless/UnitTests.cpp index 3be2d7e5224..8a67bea3f51 100644 --- a/src/headless/UnitTests.cpp +++ b/src/headless/UnitTests.cpp @@ -467,6 +467,92 @@ TEST_CASE( "lipol_ps class", "[dsp]" ) } +void copyScenedataSubset(SurgeStorage *storage, int scene, int start, int end) { + int s = storage->getPatch().scene_start[scene]; + for(int i=start; igetPatch().scenedata[scene][i-s].i = + storage->getPatch().param_ptr[i]->val.i; + } +} + +void setupStorageRanges(Parameter *start, Parameter *endIncluding, + int &storage_id_start, int &storage_id_end) { + int min_id = 100000, max_id = -1; + Parameter *oap = start; + while( oap <= endIncluding ) + { + if( oap->id >= 0 ) + { + if( oap->id > max_id ) max_id = oap->id; + if( oap->id < min_id ) min_id = oap->id; + } + oap++; + } + + storage_id_start = min_id; + storage_id_end = max_id + 1; +} + +TEST_CASE( "ADSR Envelope Behaviour", "[mod]" ) +{ + + std::shared_ptr surge( Surge::Headless::createSurge(44100) ); + REQUIRE( surge.get() ); + + auto* adsrstorage = &(surge->storage.getPatch().scene[0].adsr[0]); + std::shared_ptr adsr( new AdsrEnvelope() ); + adsr->init( &(surge->storage), adsrstorage, surge->storage.getPatch().scenedata[0], nullptr ); + REQUIRE( adsr.get() ); + + int ids, ide; + setupStorageRanges(&(adsrstorage->a), &(adsrstorage->mode), ids, ide ); + REQUIRE( ide > ids ); + REQUIRE( ide >= 0 ); + REQUIRE( ids >= 0 ); + + /* + ** OK so lets set up a pretty simple setup + */ + + auto svn = [](Parameter *p, float vn) + { + p->set_value_f01( p->value_to_normalized( vn ) ); + }; + + auto inverseEnvtime = [](float desiredTime) + { + // 2^x = desired time + auto x = log(desiredTime)/log(2.0); + return x; + }; + + svn(&(adsrstorage->a), inverseEnvtime(0.2)); + svn(&(adsrstorage->d), inverseEnvtime(0.2)); + svn(&(adsrstorage->s), 0.0); + svn(&(adsrstorage->r), inverseEnvtime(0.1)); + + svn(&(adsrstorage->a_s), 0); + svn(&(adsrstorage->d_s), 0); + svn(&(adsrstorage->r_s), 0); + + svn(&(adsrstorage->mode),0); // digital + + copyScenedataSubset(&(surge->storage), 0, ids, ide); + adsr->attack(); + for( int i=0; i<1000; ++i ) + { + auto t = 1.0 * (i+1) * BLOCK_SIZE * dsamplerate_inv; + if( i == 600 ) + adsr->release(); + + adsr->process_block(); + + if( i % 25 == 0 ) std::cout << i << " " << t << " " << adsr->output << " " << adsr->getEnvState() + << std::endl; + } +} + int runAllTests(int argc, char **argv)