Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
10KB

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