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:
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)));