1 /*
2   ZynAddSubFX - a software synthesizer
3 
4   WatchPoint.cpp - Synthesis State Watcher
5   Copyright (C) 2015-2015 Mark McCurry
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of version 2 of the GNU General Public License
9   as published by the Free Software Foundation.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License (version 2 or later) for more details.
15 
16   You should have received a copy of the GNU General Public License (version 2)
17   along with this program; if not, write to the Free Software Foundation,
18   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 
20 */
21 
22 #include "WatchPoint.h"
23 #include "../Misc/Util.h"
24 #include <cstring>
25 #include <rtosc/thread-link.h>
26 
27 namespace zyn {
28 
WatchPoint(WatchManager * ref,const char * prefix,const char * id)29 WatchPoint::WatchPoint(WatchManager *ref, const char *prefix, const char *id)
30     :active(false), samples_left(0), reference(ref)
31 {
32     identity[0] = 0;
33     if(prefix)
34         fast_strcpy(identity, prefix, sizeof(identity));
35     if(id)
36         strncat(identity, id, sizeof(identity));
37 }
38 
is_active(void)39 bool WatchPoint::is_active(void)
40 {
41     //Either the watchpoint is already active or the watchpoint manager has
42     //received another activation this frame
43     if(active)
44         return true;
45 
46     if(reference && reference->active(identity)) {
47         active       = true;
48         samples_left = 1;
49         return true;
50     }
51 
52     return false;
53 }
54 
is_empty(void)55 bool WatchPoint::is_empty(void)
56 {
57     //return reference->is_empty(identity);
58     return true;
59 }
60 
FloatWatchPoint(WatchManager * ref,const char * prefix,const char * id)61 FloatWatchPoint::FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id)
62     :WatchPoint(ref, prefix, id)
63 {}
64 
VecWatchPoint(WatchManager * ref,const char * prefix,const char * id)65 VecWatchPoint::VecWatchPoint(WatchManager *ref, const char *prefix, const char *id)
66     :WatchPoint(ref, prefix, id)
67 {}
68 
WatchManager(thrlnk * link)69 WatchManager::WatchManager(thrlnk *link)
70     :write_back(link), new_active(false)
71 {
72     memset(active_list, 0, sizeof(active_list));
73     memset(sample_list, 0, sizeof(sample_list));
74     memset(prebuffer_sample, 0, sizeof(prebuffer_sample));
75     memset(data_list,   0, sizeof(data_list));
76     memset(deactivate,  0, sizeof(deactivate));
77     memset(prebuffer,  0, sizeof(prebuffer));
78     memset(trigger,  0, sizeof(trigger));
79     memset(prebuffer_done,  0, sizeof(prebuffer_done));
80     memset(call_count,0,sizeof(call_count));
81 
82 }
83 
add_watch(const char * id)84 void WatchManager::add_watch(const char *id)
85 {
86     //Don't add duplicate watchs
87     for(int i=0; i<MAX_WATCH; ++i)
88         if(!strcmp(active_list[i], id))
89             return;
90     //Apply to a free slot
91     for(int i=0; i<MAX_WATCH; ++i) {
92         if(!active_list[i][0]) {
93             fast_strcpy(active_list[i], id, MAX_WATCH_PATH);
94             new_active = true;
95             sample_list[i] = 0;
96             call_count[i] = 0;
97             //printf("\n added watchpoint ID %s\n",id);
98             break;
99         }
100     }
101 }
102 
del_watch(const char * id)103 void WatchManager::del_watch(const char *id)
104 {
105     //Queue up the delete
106     for(int i=0; i<MAX_WATCH; ++i)
107         if(!strcmp(active_list[i], id))
108             return (void) (deactivate[i] = true);
109 }
110 
tick(void)111 void WatchManager::tick(void)
112 {
113     //Try to send out any vector stuff
114     for(int i=0; i<MAX_WATCH; ++i) {
115         int framesize = 2;
116         call_count[i] = 0;
117         if(strstr(active_list[i], "noteout") != NULL)
118             framesize = MAX_SAMPLE-1;
119         if(sample_list[i] >= framesize && call_count[i]==0) {
120             char        arg_types[MAX_SAMPLE+1] = {};
121             rtosc_arg_t arg_val[MAX_SAMPLE];
122             for(int j=0; j<sample_list[i]; ++j) {
123                 arg_types[j] = 'f';
124                 arg_val[j].f = data_list[i][j];
125             }
126             write_back->writeArray(active_list[i], arg_types, arg_val);
127             deactivate[i] = true;
128         }
129     }
130 
131     //Cleanup internal data
132     new_active = false;
133 
134     //Clear deleted slots
135     for(int i=0; i<MAX_WATCH; ++i) {
136         if(deactivate[i]) {
137             memset(active_list[i], 0, MAX_SAMPLE);
138             sample_list[i] = 0;
139             memset(data_list[i], 0, sizeof(float)*MAX_SAMPLE);
140             memset(prebuffer[i], 0, sizeof(float)*(MAX_SAMPLE/2));
141             deactivate[i]  = false;
142             trigger[i] = false;
143             prebuffer_done[i] = false;
144             prebuffer_sample[i] = 0;
145         }
146     }
147 }
148 
active(const char * id) const149 bool WatchManager::active(const char *id) const
150 {
151     assert(this);
152     assert(id);
153     if(new_active || true)
154         for(int i=0; i<MAX_WATCH; ++i)
155             if(!strcmp(active_list[i], id))
156                 return true;
157 
158     return false;
159 }
160 
trigger_active(const char * id) const161 bool WatchManager::trigger_active(const char *id) const
162 {
163     for(int i=0; i<MAX_WATCH; ++i)
164         if(!strcmp(active_list[i], id))
165             return trigger[i];
166     return false;
167 }
168 
samples(const char * id) const169 int WatchManager::samples(const char *id) const
170 {
171     for(int i=0; i<MAX_WATCH; ++i)
172         if(!strcmp(active_list[i], id))
173             return sample_list[i];
174     return 0;
175 }
176 
satisfy(const char * id,float f)177 void WatchManager::satisfy(const char *id, float f)
178 {
179     //printf("trying to satisfy '%s'\n", id);
180     if(write_back)
181         write_back->write(id, "f", f);
182     del_watch(id);
183 }
184 
satisfy(const char * id,float * f,int n)185 void WatchManager::satisfy(const char *id, float *f, int n)
186 {
187     int selected = -1;
188     for(int i=0; i<MAX_WATCH; ++i)
189         if(!strcmp(active_list[i], id))
190             selected = i;
191 
192     if(selected == -1)
193         return;
194 
195     int space = MAX_SAMPLE - sample_list[selected];
196 
197     if(space >= n || !trigger[selected])
198         space = n;
199 
200     //special case to capture the time+level pairs that come from
201     //envelopes/LFOs
202     if(n == 2)
203         trigger[selected] = true;
204 
205     if(space && (call_count[selected]==0 || n == 2)){
206         for(int i=0; i<space; i++){
207             const float prev = prebuffer[selected][(prebuffer_sample[selected]+MAX_SAMPLE/2-1)%(MAX_SAMPLE/2)];
208             if(!trigger[selected]){
209                 prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)] = f[i];
210                 prebuffer_sample[selected]++;
211                 //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)]);
212             }
213             if(!trigger[selected] && prebuffer_sample[selected] >= (MAX_SAMPLE/2)){
214                 if (prev <= 0 && f[i] > 0){
215                     //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]);
216                     trigger[selected] = true;
217                     for(int j = 0; j < (MAX_SAMPLE/2); ++j){
218                         data_list[selected][sample_list[selected]] = prebuffer[selected][prebuffer_sample[selected]%(MAX_SAMPLE/2)];
219                         sample_list[selected]++;
220                         prebuffer_sample[selected]++;
221                     }
222                     prebuffer_done[selected] = true;
223                     space = MAX_SAMPLE - sample_list[selected];
224                     if(n >= i+space)
225                         space = i+space;
226                     else
227                         space = n;
228                     trigger_other(selected);
229                 }
230             }
231 
232             if(trigger[selected] && !prebuffer_done[selected]){
233                 data_list[selected][sample_list[selected]] = f[i];
234                 sample_list[selected]++;
235             }
236 
237             if(prebuffer_done[selected])
238                 prebuffer_done[selected] = false;
239         }
240     }
241     call_count[selected]++;
242 }
243 
trigger_other(int selected)244 void WatchManager::trigger_other(int selected){
245     for(int k=0; k<MAX_WATCH; ++k){
246         if(selected != k && !trigger[k] && prebuffer_sample[k]>(MAX_SAMPLE/2) ){
247             char tmp[128];
248             char tmp1[128];
249             strcpy(tmp, active_list[selected]);
250             strcpy(tmp1, active_list[k]);
251             if(strlen(active_list[k]) < strlen(active_list[selected]))
252                 tmp[strlen(tmp)-1] =0;
253             else if (strlen(active_list[k]) > strlen(active_list[selected]))
254                 tmp1[strlen(tmp1)-1] =0;
255             //printf("\n compare tmp1 %s with tmp %s \n",tmp1,tmp);
256             if(!strcmp(tmp1,tmp)){
257                 trigger[k] = true;
258                 // printf("\n putting prebuffer size of %d into %s watchpoint \n",prebuffer_sample[k]%(MAX_SAMPLE/2),active_list[k]);
259                 // printf("\n value of first buffer %f \n",prebuffer[k][prebuffer_sample[k]%(MAX_SAMPLE/2)]);
260                 for(int j = prebuffer_sample[k]%(MAX_SAMPLE/2); j < (MAX_SAMPLE/2); ++j){
261                     data_list[k][sample_list[k]] = prebuffer[k][j];
262                     sample_list[k]++;
263                 }
264                 for(int j = 0; j < prebuffer_sample[selected]%(MAX_SAMPLE/2); ++j){
265                     data_list[k][sample_list[k]] = prebuffer[k][j];
266                     sample_list[k]++;
267                 }
268                 //prebuffer_done[k] = true;
269                 //printf("\n t Trigger for %s happen at sample %d \n",active_list[k],sample_list[k] );
270             }
271         }
272     }
273 }
274 
275 
276 }
277