zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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 }