EnvelopeFreeEdit.cpp (10402B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 EnvelopeFreeEdit.cpp - Envelope Edit View 5 Copyright (C) 2016 Mark McCurry 6 7 This program is free software; you can redistribute it and/or 8 modify it under the terms of the GNU General Public License 9 as published by the Free Software Foundation; either version 2 10 of the License, or (at your option) any later version. 11 */ 12 #include "EnvelopeFreeEdit.h" 13 #include "../Misc/Util.h" 14 #include <FL/Fl.H> 15 #include <FL/fl_draw.H> 16 #include <cstdlib> 17 #include <cassert> 18 #include <rtosc/rtosc.h> 19 20 using namespace zyn; 21 22 EnvelopeFreeEdit::EnvelopeFreeEdit(int x,int y, int w, int h, const char *label) 23 :Fl_Box(x,y,w,h,label), Fl_Osc_Widget(this) 24 { 25 pair=NULL; 26 currentpoint=-1; 27 cpx=0; 28 lastpoint=-1; 29 } 30 31 void EnvelopeFreeEdit::init(void) 32 { 33 oscRegister("Penvpoints"); 34 oscRegister("Penvdt"); 35 oscRegister("Penvval"); 36 oscRegister("Penvsustain"); 37 38 //register for non-bulk types 39 for(int i=0; i<MAX_ENVELOPE_POINTS; ++i) { 40 osc->createLink(loc+string("Penvdt") + to_s(i), this); 41 osc->createLink(loc+string("Penvval") + to_s(i), this); 42 } 43 } 44 45 void EnvelopeFreeEdit::OSC_raw(const char *msg) 46 { 47 const char *args = rtosc_argument_string(msg); 48 if(strstr(msg,"Penvpoints") && !strcmp(args, "i")) { 49 Penvpoints = rtosc_argument(msg, 0).i; 50 } else if(strstr(msg,"Penvdt") && !strcmp(args, "b")) { 51 rtosc_blob_t b = rtosc_argument(msg, 0).b; 52 assert(b.len == MAX_ENVELOPE_POINTS); 53 memcpy(Penvdt, b.data, MAX_ENVELOPE_POINTS); 54 } else if(strstr(msg,"Penvval") && !strcmp(args, "b")) { 55 rtosc_blob_t b = rtosc_argument(msg, 0).b; 56 assert(b.len == MAX_ENVELOPE_POINTS); 57 memcpy(Penvval, b.data, MAX_ENVELOPE_POINTS); 58 } else if(strstr(msg, "Penvval") && !strcmp(args, "i")) { 59 const char *str = strstr(msg, "Penvval"); 60 int id = atoi(str+7); 61 assert(0 <= id && id < MAX_ENVELOPE_POINTS); 62 Penvval[id] = rtosc_argument(msg, 0).i; 63 } else if(strstr(msg, "Penvdt") && !strcmp(args, "i")) { 64 const char *str = strstr(msg, "Penvdt"); 65 int id = atoi(str+6); 66 assert(0 <= id && id < MAX_ENVELOPE_POINTS); 67 Penvdt[id] = rtosc_argument(msg, 0).i; 68 } else if(strstr(msg,"Penvsustain") && !strcmp(args, "i")) { 69 Penvsustain = rtosc_argument(msg, 0).i; 70 } 71 redraw(); 72 do_callback(); 73 } 74 75 void EnvelopeFreeEdit::setpair(Fl_Box *pair_) 76 { 77 pair=pair_; 78 } 79 80 int EnvelopeFreeEdit::getpointx(int n) const 81 { 82 const int lx=w()-10; 83 int npoints=Penvpoints; 84 85 float sum=0; 86 for(int i=1; i<npoints; ++i) 87 sum+=getdt(i)+1; 88 89 float sumbefore=0;//the sum of all points before the computed point 90 for(int i=1; i<=n; ++i) 91 sumbefore+=getdt(i)+1; 92 93 return (int) (sumbefore/(float) sum*lx); 94 } 95 96 int EnvelopeFreeEdit::getpointy(int n) const 97 { 98 const int ly=h()-10; 99 100 return (1.0-Penvval[n]/127.0)*ly; 101 } 102 103 static inline int distance_fn(int dx, int dy) { 104 return dx*dx+dy*dy; 105 } 106 107 int EnvelopeFreeEdit::getnearest(int x,int y) const 108 { 109 x-=5;y-=5; 110 111 int nearestpoint=0; 112 int nearest_distance_sq=distance_fn(x-getpointx(0), y-getpointy(0)); 113 for(int i=1; i<Penvpoints; ++i){ 114 int distance_sq=distance_fn(x-getpointx(i), y-getpointy(i)); 115 if (distance_sq<nearest_distance_sq) { 116 nearestpoint=i; 117 nearest_distance_sq=distance_sq; 118 } 119 } 120 121 return nearestpoint; 122 } 123 124 static float dt(char val) 125 { 126 return (powf(2.0f, val / 127.0f * 12.0f) - 1.0f) * 10.0f; //milliseconds 127 } 128 129 float EnvelopeFreeEdit::getdt(int i) const 130 { 131 return dt(Penvdt[i]); 132 } 133 134 static bool ctrldown, altdown; 135 136 void EnvelopeFreeEdit::draw(void) 137 { 138 int ox=x(),oy=y(),lx=w(),ly=h(); 139 //if (env->Pfreemode==0) 140 // env->converttofree(); 141 const int npoints=Penvpoints; 142 143 if (active_r()) fl_color(FL_BLACK); 144 else fl_color(90,90,90); 145 if (!active_r()) currentpoint=-1; 146 147 fl_rectf(ox,oy,lx,ly); 148 149 //Margins 150 ox+=5;oy+=5;lx-=10;ly-=10; 151 152 //draw the lines 153 fl_color(FL_GRAY); 154 155 const int midline = oy+ly*(1-64.0/127); 156 fl_line_style(FL_SOLID); 157 fl_line(ox+2,midline,ox+lx-2,midline); 158 159 //draws the envelope points and lines 160 Fl_Color alb=FL_WHITE; 161 if (!active_r()) alb=fl_rgb_color(180,180,180); 162 fl_color(alb); 163 int oldxx=0,xx=0,oldyy=0,yy=getpointy(0); 164 fl_rectf(ox-3,oy+yy-3,6,6); 165 for (int i=1; i<npoints; ++i){ 166 oldxx=xx;oldyy=yy; 167 xx=getpointx(i);yy=getpointy(i); 168 if (i==currentpoint || (ctrldown && i==lastpoint)) 169 fl_color(FL_RED); 170 else 171 fl_color(alb); 172 fl_line(ox+oldxx,oy+oldyy,ox+xx,oy+yy); 173 fl_rectf(ox+xx-3,oy+yy-3,6,6); 174 } 175 176 //draw the last moved point point (if exists) 177 if (lastpoint>=0){ 178 fl_color(FL_CYAN); 179 fl_rectf(ox+getpointx(lastpoint)-5,oy+getpointy(lastpoint)-5,10,10); 180 } 181 182 //draw the sustain position 183 if(Penvsustain>0){ 184 fl_color(FL_YELLOW); 185 xx=getpointx(Penvsustain); 186 fl_line(ox+xx,oy+0,ox+xx,oy+ly); 187 } 188 189 //Show the envelope duration and the current line duration 190 fl_font(FL_HELVETICA|FL_BOLD,10); 191 float time=0.0; 192 if (currentpoint<=0 && (!ctrldown||lastpoint <= 0)){ 193 fl_color(alb); 194 for(int i=1; i<npoints; ++i) 195 time+=getdt(i); 196 } else { 197 fl_color(FL_RED); 198 time=getdt(lastpoint); 199 } 200 char tmpstr[20]; 201 if (!altdown || ctrldown) { 202 if (time<1000.0) 203 snprintf((char *)&tmpstr,20,"%.1fms",time); 204 else 205 snprintf((char *)&tmpstr,20,"%.2fs",time/1000.0); 206 fl_draw(tmpstr,ox+lx-20,oy+ly-10,20,10,FL_ALIGN_RIGHT,NULL,0); 207 } 208 if (!altdown || !ctrldown) { 209 if (lastpoint>=0){ 210 snprintf((char *)&tmpstr,20,"%d", Penvval[lastpoint]); 211 fl_draw(tmpstr,ox+lx-20,oy+ly-23,20,10,FL_ALIGN_RIGHT,NULL,0); 212 } 213 } 214 } 215 216 int EnvelopeFreeEdit::handle(int event) 217 { 218 const int x_=Fl::event_x()-x(); 219 const int y_=Fl::event_y()-y(); 220 static Fl_Widget *old_focus; 221 int key, old_mod_state; 222 223 switch(event) { 224 case FL_ENTER: 225 old_focus=Fl::focus(); 226 Fl::focus(this); 227 // Otherwise the underlying window seems to regrab focus, 228 // and I can't see the KEYDOWN action. 229 return 1; 230 case FL_LEAVE: 231 Fl::focus(old_focus); 232 break; 233 case FL_KEYDOWN: 234 case FL_KEYUP: 235 key = Fl::event_key(); 236 if (key==FL_Alt_L || key==FL_Alt_R) { 237 altdown = (event==FL_KEYDOWN); 238 redraw(); 239 if (pair!=NULL) pair->redraw(); 240 } 241 if (key==FL_Control_L || key==FL_Control_R){ 242 ctrldown = (event==FL_KEYDOWN); 243 redraw(); 244 if (pair!=NULL) pair->redraw(); 245 } 246 break; 247 case FL_PUSH: 248 currentpoint=getnearest(x_,y_); 249 cpx=x_; 250 cpy=y_; 251 cpdt=Penvdt[currentpoint]; 252 cpval=Penvval[currentpoint]; 253 lastpoint=currentpoint; 254 redraw(); 255 if (pair) 256 pair->redraw(); 257 return 1; 258 case FL_RELEASE: 259 currentpoint=-1; 260 redraw(); 261 if (pair) 262 pair->redraw(); 263 return 1; 264 case FL_MOUSEWHEEL: 265 if (Fl::event_buttons()) 266 return 1; 267 if (lastpoint>=0) { 268 int delta = Fl::event_dy() * (Fl::event_shift() ? 4 : 1); 269 if (!ctrldown) { 270 int ny = Penvval[lastpoint] - delta; 271 ny = ny < 0 ? 0 : ny > 127 ? 127 : ny; 272 Penvval[lastpoint] = ny; 273 oscWrite(to_s("Penvval")+to_s(lastpoint), "i", ny); 274 oscWrite("Penvval",""); 275 } else if (lastpoint > 0) { 276 int newdt = Penvdt[lastpoint] - delta; 277 newdt = newdt < 0 ? 0 : newdt > 127 ? 127 : newdt; 278 Penvdt[lastpoint] = newdt; 279 oscWrite(to_s("Penvdt")+to_s(lastpoint), "i", newdt); 280 oscWrite("Penvdt",""); 281 } 282 redraw(); 283 if (pair!=NULL) pair->redraw(); 284 return 1; 285 } 286 // fall through 287 case FL_DRAG: 288 if (currentpoint>=0){ 289 old_mod_state = mod_state; 290 mod_state = ctrldown << 1 | altdown; 291 if (old_mod_state != mod_state) { 292 cpx=x_; 293 cpy=y_; 294 cpdt=Penvdt[currentpoint]; 295 cpval=Penvval[currentpoint]; 296 old_mod_state = mod_state; 297 } 298 299 if (!altdown || !ctrldown) { 300 const int dy=(int)((cpy-y_)/3.0); 301 const int newval=limit(cpval+dy, 0, 127); 302 303 Penvval[currentpoint]=newval; 304 oscWrite(to_s("Penvval")+to_s(currentpoint), "i", newval); 305 oscWrite("Penvval",""); 306 } 307 308 if (!altdown || ctrldown) { 309 const int dx=(int)((x_-cpx)*0.1); 310 const int newdt=limit(cpdt+dx,0,127); 311 312 if(currentpoint!=0) 313 Penvdt[currentpoint]=newdt; 314 else 315 Penvdt[currentpoint]=0; 316 oscWrite(to_s("Penvdt")+to_s(currentpoint), "i", newdt); 317 oscWrite("Penvdt",""); 318 } 319 320 redraw(); 321 322 if(pair) 323 pair->redraw(); 324 return 1; 325 } 326 } 327 // Needed to propagate undo/redo keys. 328 return 0; 329 } 330 331 void EnvelopeFreeEdit::update(void) 332 { 333 oscWrite("Penvpoints"); 334 oscWrite("Penvdt"); 335 oscWrite("Penvval"); 336 oscWrite("Penvsustain"); 337 } 338 339 void EnvelopeFreeEdit::rebase(std::string new_base) 340 { 341 osc->renameLink(loc+"Penvpoints", new_base+"Penvpoints", this); 342 osc->renameLink(loc+"Penvdt", new_base+"Penvdt", this); 343 osc->renameLink(loc+"Penvval", new_base+"Penvval", this); 344 osc->renameLink(loc+"Penvsustain", new_base+"Penvsustain", this); 345 for(int i=0; i<MAX_ENVELOPE_POINTS; ++i) { 346 string dt = string("Penvdt") + to_s(i); 347 string val = string("Penvval") + to_s(i); 348 osc->renameLink(loc+dt, new_base+dt, this); 349 osc->renameLink(loc+val, new_base+val, this); 350 } 351 loc = new_base; 352 update(); 353 }