Watchpoints: Fix missing env/LFO watch points
[zynaddsubfx-code.git] / src / Synth / WatchPoint.cpp
blobd2fbc6ebf4ce7ecfeedb96823ba4fa8c03a2f6ea
1 /*
2 ZynAddSubFX - a software synthesizer
4 WatchPoint.cpp - Synthesis State Watcher
5 Copyright (C) 2015-2015 Mark McCurry
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.
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.
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
22 #include "WatchPoint.h"
23 #include "../Misc/Util.h"
24 #include <cstring>
25 #include <rtosc/thread-link.h>
27 namespace zyn {
29 WatchPoint::WatchPoint(WatchManager *ref, const char *prefix, const char *id)
30 :active(false), samples_left(0), reference(ref)
32 identity[0] = 0;
33 if(prefix)
34 fast_strcpy(identity, prefix, sizeof(identity));
35 if(id)
36 strncat(identity, id, sizeof(identity));
39 bool WatchPoint::is_active(void)
41 //Either the watchpoint is already active or the watchpoint manager has
42 //received another activation this frame
43 if(active)
44 return true;
46 if(reference && reference->active(identity)) {
47 active = true;
48 samples_left = 1;
49 return true;
52 return false;
55 bool WatchPoint::is_empty(void)
57 //return reference->is_empty(identity);
58 return true;
61 FloatWatchPoint::FloatWatchPoint(WatchManager *ref, const char *prefix, const char *id)
62 :WatchPoint(ref, prefix, id)
65 VecWatchPoint::VecWatchPoint(WatchManager *ref, const char *prefix, const char *id)
66 :WatchPoint(ref, prefix, id)
69 WatchManager::WatchManager(thrlnk *link)
70 :write_back(link), new_active(false)
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));
84 void WatchManager::add_watch(const char *id)
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;
103 void WatchManager::del_watch(const char *id)
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);
111 void WatchManager::tick(void)
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];
126 write_back->writeArray(active_list[i], arg_types, arg_val);
127 deactivate[i] = true;
131 //Cleanup internal data
132 new_active = false;
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;
149 bool WatchManager::active(const char *id) const
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;
158 return false;
161 bool WatchManager::trigger_active(const char *id) const
163 for(int i=0; i<MAX_WATCH; ++i)
164 if(!strcmp(active_list[i], id))
165 return trigger[i];
166 return false;
169 int WatchManager::samples(const char *id) const
171 for(int i=0; i<MAX_WATCH; ++i)
172 if(!strcmp(active_list[i], id))
173 return sample_list[i];
174 return 0;
177 void WatchManager::satisfy(const char *id, float f)
179 //printf("trying to satisfy '%s'\n", id);
180 if(write_back)
181 write_back->write(id, "f", f);
182 del_watch(id);
185 void WatchManager::satisfy(const char *id, float *f, int n)
187 int selected = -1;
188 for(int i=0; i<MAX_WATCH; ++i)
189 if(!strcmp(active_list[i], id))
190 selected = i;
192 if(selected == -1)
193 return;
195 int space = MAX_SAMPLE - sample_list[selected];
197 if(space >= n || !trigger[selected])
198 space = n;
200 //special case to capture the time+level pairs that come from
201 //envelopes/LFOs
202 if(n == 2)
203 trigger[selected] = true;
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)]);
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]++;
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);
232 if(trigger[selected] && !prebuffer_done[selected]){
233 data_list[selected][sample_list[selected]] = f[i];
234 sample_list[selected]++;
237 if(prebuffer_done[selected])
238 prebuffer_done[selected] = false;
241 call_count[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]++;
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]++;
268 //prebuffer_done[k] = true;
269 //printf("\n t Trigger for %s happen at sample %d \n",active_list[k],sample_list[k] );