zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit a3ff226b1c68ba9e28104390edf4ca1f1d0d6c0b
parent 9d7dca8bcfd18e5c72539b481e1ed7d778d3cca4
Author: fundamental <mark.d.mccurry@gmail.com>
Date:   Wed, 31 Jul 2019 08:31:08 -0400

Merge Fixes for watchpoint trigger by michiboo

Add Sine wave test with multiple buffer sizes
Add precondition that prebuffer must fill before triggering
Fix multi-note triggering (only watch the first processed note)
Fix minor code style concerns in WatchPoint.cpp and TriggerTest.cpp
Confirm correct behavior when testing with add synth sine waves and sawtooth
waves

Diffstat:
Msrc/Synth/SUBnote.cpp | 8++------
Msrc/Synth/WatchPoint.cpp | 145++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/Synth/WatchPoint.h | 8+++++++-
Msrc/Tests/TriggerTest.h | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
4 files changed, 177 insertions(+), 83 deletions(-)

diff --git a/src/Synth/SUBnote.cpp b/src/Synth/SUBnote.cpp @@ -519,9 +519,6 @@ void SUBnote::chanOutput(float *out, bpfilter *bp, int buffer_size) for(int i = 0; i < synth.buffersize; ++i) out[i] += tmpsmp[i] * rolloff; - - if(n == 0) - watchOut(tmpsmp,buffer_size); } } @@ -551,7 +548,7 @@ int SUBnote::noteout(float *outl, float *outr) memcpy(outr, outl, synth.bufferbytes); } - watchOut1(outl,synth.buffersize); + watchOut(outl,synth.buffersize); if(firsttick) { int n = 10; if(n > synth.buffersize) @@ -580,13 +577,12 @@ int SUBnote::noteout(float *outl, float *outr) outl[i] *= newamplitude * panning; outr[i] *= newamplitude * (1.0f - panning); } - oldamplitude = newamplitude; computecurrentparameters(); // Apply legato-specific sound signal modifications legato.apply(*this, outl, outr); - + watchOut1(outl,synth.buffersize); // Check if the note needs to be computed more if(AmpEnvelope->finished() != 0) { for(int i = 0; i < synth.buffersize; ++i) { //fade-out diff --git a/src/Synth/WatchPoint.cpp b/src/Synth/WatchPoint.cpp @@ -52,6 +52,12 @@ bool WatchPoint::is_active(void) return false; } +bool WatchPoint::is_empty(void) +{ + //return reference->is_empty(identity); + return true; +} + FloatWatchPoint::FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id) :WatchPoint(ref, prefix, id) {} @@ -65,26 +71,29 @@ WatchManager::WatchManager(thrlnk *link) { memset(active_list, 0, sizeof(active_list)); memset(sample_list, 0, sizeof(sample_list)); + memset(prebuffer_sample, 0, sizeof(prebuffer_sample)); memset(data_list, 0, sizeof(data_list)); memset(deactivate, 0, sizeof(deactivate)); memset(prebuffer, 0, sizeof(prebuffer)); memset(trigger, 0, sizeof(trigger)); + memset(prebuffer_done, 0, sizeof(prebuffer_done)); + memset(call_count,0,sizeof(call_count)); -} +} void WatchManager::add_watch(const char *id) -{ +{ //Don't add duplicate watchs for(int i=0; i<MAX_WATCH; ++i) if(!strcmp(active_list[i], id)) return; - //Apply to a free slot for(int i=0; i<MAX_WATCH; ++i) { if(!active_list[i][0]) { fast_strcpy(active_list[i], id, MAX_WATCH_PATH); new_active = true; sample_list[i] = 0; + call_count[i] = 0; //printf("\n added watchpoint ID %s\n",id); break; } @@ -100,20 +109,20 @@ void WatchManager::del_watch(const char *id) } void WatchManager::tick(void) -{ +{ //Try to send out any vector stuff for(int i=0; i<MAX_WATCH; ++i) { int framesize = 2; + call_count[i] = 0; if(strstr(active_list[i], "noteout") != NULL) - framesize = MAX_SAMPLE-1; - if(sample_list[i] >= framesize-1) { + framesize = MAX_SAMPLE; + if(sample_list[i] >= framesize && call_count[i]==0) { char arg_types[MAX_SAMPLE+1] = {0}; rtosc_arg_t arg_val[MAX_SAMPLE]; for(int j=0; j<sample_list[i]; ++j) { arg_types[j] = 'f'; arg_val[j].f = data_list[i][j]; } - write_back->writeArray(active_list[i], arg_types, arg_val); deactivate[i] = true; } @@ -125,17 +134,16 @@ void WatchManager::tick(void) //Clear deleted slots for(int i=0; i<MAX_WATCH; ++i) { if(deactivate[i]) { - //printf("\ndelete id : %s\n",active_list[i]); memset(active_list[i], 0, MAX_SAMPLE); sample_list[i] = 0; memset(data_list[i], 0, sizeof(float)*MAX_SAMPLE); - memset(prebuffer[i], 0, sizeof(float)*MAX_SAMPLE); + memset(prebuffer[i], 0, sizeof(float)*(MAX_SAMPLE/2)); deactivate[i] = false; trigger[i] = false; - + prebuffer_done[i] = false; + prebuffer_sample[i] = 0; } } - } bool WatchManager::active(const char *id) const @@ -180,70 +188,95 @@ void WatchManager::satisfy(const char *id, float *f, int n) for(int i=0; i<MAX_WATCH; ++i) if(!strcmp(active_list[i], id)) selected = i; - + if(selected == -1) return; - // printf("\npath : %s \n", id); - - // if (!strcmp(id,"/part0/kit0/subpars/noteout")) - // printf("\n matched: %s\n", id); - - int space = MAX_SAMPLE - sample_list[selected]; - - - for(int i = 0; i < n; ++i){ - prebuffer[selected][i] = f[i]; + // if trigger by another + if(trigger[selected] && prebuffer_done[selected]){ + for(int j = 0; j < (MAX_SAMPLE/2); ++j){ + data_list[selected][sample_list[selected]] = prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)]; + sample_list[selected]++; + prebuffer_sample[selected]++; + } + prebuffer_done[selected] = false; } - - if(space >= n) + int space = MAX_SAMPLE - sample_list[selected]; + + if(space >= n || !trigger[selected]) space = n; - if(n == 2) trigger[selected] = true; - //FIXME buffer overflow - if(space){ - for(int i=0; i<space; ++i){ + if(space && call_count[selected]==0){ + for(int i=0; i<space; i++){ + const float prev = prebuffer[selected][(prebuffer_sample[selected]+MAX_SAMPLE/2-1)%(MAX_SAMPLE/2)]; if(!trigger[selected]){ - if(i == 0) - i++; - if (f[i-1] <= 0 && f[i] > 0){ + prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)] = f[i]; + prebuffer_sample[selected]++; + //printf("\n before trigger %s prebuffer at index %d %f \n",active_list[selected],prebuffer_sample[selected],prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)]); + } + if(!trigger[selected] && prebuffer_sample[selected] >= (MAX_SAMPLE/2)){ + if (prev <= 0 && f[i] > 0){ + //printf("\n trigger at %s prebuffer at index %f %d f[i] %f \n",active_list[selected],prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)-2],prebuffer_sample[selected],f[i]); trigger[selected] = true; - for(int k=0; k<MAX_WATCH; ++k){ - if(selected != k && !trigger[k]){ - char tmp[128]; - char tmp1[128]; - strcpy(tmp, active_list[selected]); - strcpy(tmp1, active_list[k]); - - if(strlen(active_list[k]) < strlen(active_list[selected])) - tmp[strlen(tmp)-1] =0; - else if (strlen(active_list[k]) > strlen(active_list[selected])) - tmp1[strlen(tmp1)-1] =0; - if(!strcmp(tmp1,tmp)){ - trigger[k] = true; - int space_k = MAX_SAMPLE - sample_list[k]; - - if(space_k >= n) - space_k = n; - - for(int j = i; j < space_k ; ++j){ - data_list[k][sample_list[k]] = prebuffer[k][j]; - sample_list[k]++; - } - } - } + for(int j = 0; j < (MAX_SAMPLE/2); ++j){ + data_list[selected][sample_list[selected]] = prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)]; + sample_list[selected]++; + prebuffer_sample[selected]++; } + prebuffer_done[selected] = true; + space = MAX_SAMPLE - sample_list[selected]; + if(n >= i+space) + space = i+space; + else + space = n; + trigger_other(selected); } } - if(trigger[selected]){ + if(trigger[selected] && !prebuffer_done[selected]){ data_list[selected][sample_list[selected]] = f[i]; sample_list[selected]++; } + + if(prebuffer_done[selected]) + prebuffer_done[selected] = false; } + } + call_count[selected]++; +} +void WatchManager::trigger_other(int selected){ + for(int k=0; k<MAX_WATCH; ++k){ + if(selected != k && !trigger[k]){ + char tmp[128]; + char tmp1[128]; + strcpy(tmp, active_list[selected]); + strcpy(tmp1, active_list[k]); + if(strlen(active_list[k]) < strlen(active_list[selected])) + tmp[strlen(tmp)-1] =0; + else if (strlen(active_list[k]) > strlen(active_list[selected])) + tmp1[strlen(tmp1)-1] =0; + //printf("\n compare tmp1 %s with tmp %s \n",tmp1,tmp); + if(!strcmp(tmp1,tmp)){ + trigger[k] = true; + //printf("\n putting prebuffer size of %d into %s watchpoint \n",prebuffer_sample[k]%(MAX_SAMPLE/2),active_list[k]); + //printf("\n value of first buffer %f \n",prebuffer[k][prebuffer_sample[k]%(MAX_SAMPLE/2)]); + // for(int j = prebuffer_sample[k]%(MAX_SAMPLE/2); j < (MAX_SAMPLE/2); ++j){ + // data_list[k][sample_list[k]] = prebuffer[k][j]; + // sample_list[k]++; + // } + // for(int j = 0; j < prebuffer_sample[selected]%(MAX_SAMPLE/2); ++j){ + // data_list[k][sample_list[k]] = prebuffer[k][j]; + // sample_list[k]++; + // } + //prebuffer_done[k] = true; + //printf("\n t Trigger for %s happen at sample %d \n",active_list[k],sample_list[k] ); + } + } } } + + } diff --git a/src/Synth/WatchPoint.h b/src/Synth/WatchPoint.h @@ -28,6 +28,7 @@ struct WatchPoint WatchPoint(WatchManager *ref, const char *prefix, const char *id); bool is_active(void); + bool is_empty(void); }; #define MAX_WATCH 16 @@ -40,10 +41,14 @@ struct WatchManager bool new_active; char active_list[MAX_WATCH][MAX_WATCH_PATH]; float data_list[MAX_WATCH][MAX_SAMPLE]; - float prebuffer[MAX_WATCH][MAX_SAMPLE]; + float prebuffer[MAX_WATCH][MAX_SAMPLE/2]; int sample_list[MAX_WATCH]; + int prebuffer_sample[MAX_WATCH]; bool deactivate[MAX_WATCH]; bool trigger[MAX_WATCH]; + bool prebuffer_done[MAX_WATCH]; + int call_count[MAX_WATCH]; + char countID_list[MAX_WATCH][MAX_WATCH_PATH]; //External API WatchManager(thrlnk *link=0); @@ -51,6 +56,7 @@ struct WatchManager void del_watch(const char *); void tick(void); bool trigger_active(const char *) const; + void trigger_other(int); //Watch Point Query API bool active(const char *) const; diff --git a/src/Tests/TriggerTest.h b/src/Tests/TriggerTest.h @@ -69,7 +69,7 @@ class TriggerTest:public CxxTest::TestSuite //prepare the default settings SUBnoteParameters *defaultPreset = new SUBnoteParameters(time); - sprng(0x7eefdead); + sprng(3543); controller = new Controller(*synth, time); @@ -106,6 +106,58 @@ class TriggerTest:public CxxTest::TestSuite // printf("%d->%f\n", i, w->prebuffer[1][i]); } + void testSine(void) { + //Generate a sine table + float data[1024] = {0}; + for(int i=0; i<1024; ++i) + data[i] = -sin(2*M_PI*(i/1024.0)); + + //Preconditions + // + //- No pending messages + //- No active watch points + // + TS_ASSERT(!tr->hasNext()); + TS_ASSERT_EQUALS(string(""), w->active_list[0]); + TS_ASSERT_EQUALS(0, w->sample_list[0]); + TS_ASSERT(!w->trigger_active("data")); + + + w->add_watch("noteout"); + for(int i=0; i<1024; ++i) { + w->satisfy("noteout", &data[i], 1); + w->tick(); + } + const char *msg1 = tr->read(); + float buf1[128] = {0}; + TS_ASSERT(msg1); + TS_ASSERT_EQUALS(128, rtosc_narguments(msg1)); + + printf("msg1 = %s\n", msg1); + printf("msg1 = <%s>\n", rtosc_argument_string(msg1)); + printf("nargs = %d\n", rtosc_narguments(msg1)); + for(int i=0; i<127; ++i) + buf1[i] = rtosc_argument(msg1, i).f; + + w->add_watch("noteout2"); + for(int i=0; i<1024/97; ++i) { + w->satisfy("noteout2", &data[i*97], 97); + w->tick(); + } + const char *msg2 = tr->read(); + TS_ASSERT(msg2); + TS_ASSERT_EQUALS(128, rtosc_narguments(msg2)); + float buf2[128] = {0}; + printf("nargs = %d\n", rtosc_narguments(msg2)); + for(int i=0; i<127; ++i) + buf2[i] = rtosc_argument(msg2, i).f; + for(int i=0; i<127; ++i){ + TS_ASSERT_EQUALS(buf1[i], buf2[i]); + TS_ASSERT_EQUALS(buf1[i],data[450+i]); + TS_ASSERT_EQUALS(buf2[i],data[450+i]); + } + } + void testCombinedTrigger() { //Generate a note note->noteout(outL, outR); @@ -142,44 +194,49 @@ class TriggerTest:public CxxTest::TestSuite //Run the system //noteout1 should trigger on this buffer note->noteout(outL, outR); + w->tick(); dump_samples("Step 1 pre-buffer"); - TS_ASSERT(w->trigger_active("noteout")); - TS_ASSERT(w->trigger_active("noteout1")); + TS_ASSERT(!w->trigger_active("noteout")); //not active as prebuffer is not filled + TS_ASSERT(!w->trigger_active("noteout1")); TS_ASSERT(!tr->hasNext()); - TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 32);//only 32 have been - TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 32);//processed so far + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 0); // Is 0 as prebuffer not filled + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 0); //Both should continue to accumulate samples note->noteout(outL, outR); w->tick(); dump_samples("Step 2 pre-buffer\n"); - TS_ASSERT(w->trigger_active("noteout1")); - TS_ASSERT(w->trigger_active("noteout")); + TS_ASSERT(!w->trigger_active("noteout1")); // not active as prebuffer is not filled + TS_ASSERT(!w->trigger_active("noteout")); TS_ASSERT(!tr->hasNext()); - TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 64);//only 64 have been - TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 64);//processed so far + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 0); // Is 0 as prebuffer not filled + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 0); //Continue accum samples note->noteout(outL, outR); w->tick(); dump_samples("Step 3 pre-buffer\n"); - TS_ASSERT(w->trigger_active("noteout1")); - TS_ASSERT(w->trigger_active("noteout")); + TS_ASSERT(!w->trigger_active("noteout1")); + TS_ASSERT(!w->trigger_active("noteout")); TS_ASSERT(!tr->hasNext()); - TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 96);//only 96 have been - TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 96);//processed so far - + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 0); // Is 0 as prebuffer not filled + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 0); + //Finish accumulating samples note->noteout(outL, outR); w->tick(); dump_samples("Step 4 pre-buffer\n"); - TS_ASSERT(!w->trigger_active("noteout1")); - TS_ASSERT(!w->trigger_active("noteout")); - TS_ASSERT(tr->hasNext()); - - + TS_ASSERT(w->trigger_active("noteout1")); // trigger activate and filling post buffer + TS_ASSERT(w->trigger_active("noteout")); + TS_ASSERT(!tr->hasNext()); // post buffer not reach 128 + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[1], 128); // prebuffer + postbuffer filled in + TS_ASSERT_LESS_THAN_EQUALS(w->sample_list[0], 128); + note->noteout(outL, outR); + w->tick(); + note->noteout(outL, outR); + w->tick(); #define f32 "ffffffffffffffffffffffffffffffff" #define f128 f32 f32 f32 f32 @@ -189,7 +246,9 @@ class TriggerTest:public CxxTest::TestSuite TS_ASSERT_EQUALS(string("noteout"), msg1); TS_ASSERT_EQUALS(string(f128), rtosc_argument_string(msg1)); TS_ASSERT_EQUALS(128, strlen(rtosc_argument_string(msg1))); - TS_ASSERT(tr->hasNext()); + TS_ASSERT(!tr->hasNext()); + note->noteout(outL, outR); + w->tick(); const char *msg2 = tr->read(); TS_ASSERT_EQUALS(string("noteout1"), msg2); TS_ASSERT_EQUALS(128, strlen(rtosc_argument_string(msg2)));