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.

340 lines
9.7KB

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