|
- /*
- ZynAddSubFX - a software synthesizer
-
- EnvelopeFreeEdit.cpp - Envelope Edit View
- Copyright (C) 2016 Mark McCurry
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- */
- #include "EnvelopeFreeEdit.h"
- #include "../Misc/Util.h"
- #include <FL/Fl.H>
- #include <FL/fl_draw.H>
- #include <cstdlib>
- #include <cassert>
- #include <rtosc/rtosc.h>
-
- using namespace zyncarla;
-
- EnvelopeFreeEdit::EnvelopeFreeEdit(int x,int y, int w, int h, const char *label)
- :Fl_Box(x,y,w,h,label), Fl_Osc_Widget(this)
- {
- pair=NULL;
- currentpoint=-1;
- cpx=0;
- lastpoint=-1;
- }
-
- void EnvelopeFreeEdit::init(void)
- {
- oscRegister("Penvpoints");
- oscRegister("Penvdt");
- oscRegister("Penvval");
- oscRegister("Penvsustain");
-
- //register for non-bulk types
- for(int i=0; i<MAX_ENVELOPE_POINTS; ++i) {
- osc->createLink(loc+string("Penvdt") + to_s(i), this);
- osc->createLink(loc+string("Penvval") + to_s(i), this);
- }
- }
-
- void EnvelopeFreeEdit::OSC_raw(const char *msg)
- {
- const char *args = rtosc_argument_string(msg);
- if(strstr(msg,"Penvpoints") && !strcmp(args, "i")) {
- Penvpoints = rtosc_argument(msg, 0).i;
- } else if(strstr(msg,"Penvdt") && !strcmp(args, "b")) {
- rtosc_blob_t b = rtosc_argument(msg, 0).b;
- assert(b.len == MAX_ENVELOPE_POINTS);
- memcpy(Penvdt, b.data, MAX_ENVELOPE_POINTS);
- } else if(strstr(msg,"Penvval") && !strcmp(args, "b")) {
- rtosc_blob_t b = rtosc_argument(msg, 0).b;
- assert(b.len == MAX_ENVELOPE_POINTS);
- memcpy(Penvval, b.data, MAX_ENVELOPE_POINTS);
- } else if(strstr(msg, "Penvval") && !strcmp(args, "c")) {
- const char *str = strstr(msg, "Penvval");
- int id = atoi(str+7);
- assert(0 <= id && id < MAX_ENVELOPE_POINTS);
- Penvval[id] = rtosc_argument(msg, 0).i;
- } else if(strstr(msg, "Penvdt") && !strcmp(args, "c")) {
- const char *str = strstr(msg, "Penvdt");
- int id = atoi(str+6);
- assert(0 <= id && id < MAX_ENVELOPE_POINTS);
- Penvdt[id] = rtosc_argument(msg, 0).i;
- } else if(strstr(msg,"Penvsustain") && !strcmp(args, "i")) {
- Penvsustain = rtosc_argument(msg, 0).i;
- }
- redraw();
- do_callback();
- }
-
- void EnvelopeFreeEdit::setpair(Fl_Box *pair_)
- {
- pair=pair_;
- }
-
- int EnvelopeFreeEdit::getpointx(int n) const
- {
- const int lx=w()-10;
- int npoints=Penvpoints;
-
- float sum=0;
- for(int i=1; i<npoints; ++i)
- sum+=getdt(i)+1;
-
- float sumbefore=0;//the sum of all points before the computed point
- for(int i=1; i<=n; ++i)
- sumbefore+=getdt(i)+1;
-
- return (int) (sumbefore/(float) sum*lx);
- }
-
- int EnvelopeFreeEdit::getpointy(int n) const
- {
- const int ly=h()-10;
-
- return (1.0-Penvval[n]/127.0)*ly;
- }
-
- static inline int distance_fn(int dx, int dy) {
- return dx*dx+dy*dy;
- }
-
- int EnvelopeFreeEdit::getnearest(int x,int y) const
- {
- x-=5;y-=5;
-
- int nearestpoint=0;
- int nearest_distance_sq=distance_fn(x-getpointx(0), y-getpointy(0));
- for(int i=1; i<Penvpoints; ++i){
- int distance_sq=distance_fn(x-getpointx(i), y-getpointy(i));
- if (distance_sq<nearest_distance_sq) {
- nearestpoint=i;
- nearest_distance_sq=distance_sq;
- }
- }
-
- return nearestpoint;
- }
-
- static float dt(char val)
- {
- return (powf(2.0f, val / 127.0f * 12.0f) - 1.0f) * 10.0f; //miliseconds
- }
-
- float EnvelopeFreeEdit::getdt(int i) const
- {
- return dt(Penvdt[i]);
- }
-
- static bool ctrldown, altdown;
-
- void EnvelopeFreeEdit::draw(void)
- {
- int ox=x(),oy=y(),lx=w(),ly=h();
- //if (env->Pfreemode==0)
- // env->converttofree();
- const int npoints=Penvpoints;
-
- if (active_r()) fl_color(FL_BLACK);
- else fl_color(90,90,90);
- if (!active_r()) currentpoint=-1;
-
- fl_rectf(ox,oy,lx,ly);
-
- //Margins
- ox+=5;oy+=5;lx-=10;ly-=10;
-
- //draw the lines
- fl_color(FL_GRAY);
-
- const int midline = oy+ly*(1-64.0/127);
- fl_line_style(FL_SOLID);
- fl_line(ox+2,midline,ox+lx-2,midline);
-
- //draws the evelope points and lines
- Fl_Color alb=FL_WHITE;
- if (!active_r()) alb=fl_rgb_color(180,180,180);
- fl_color(alb);
- int oldxx=0,xx=0,oldyy=0,yy=getpointy(0);
- fl_rectf(ox-3,oy+yy-3,6,6);
- for (int i=1; i<npoints; ++i){
- oldxx=xx;oldyy=yy;
- xx=getpointx(i);yy=getpointy(i);
- if (i==currentpoint || (ctrldown && i==lastpoint))
- fl_color(FL_RED);
- else
- fl_color(alb);
- fl_line(ox+oldxx,oy+oldyy,ox+xx,oy+yy);
- fl_rectf(ox+xx-3,oy+yy-3,6,6);
- }
-
- //draw the last moved point point (if exists)
- if (lastpoint>=0){
- fl_color(FL_CYAN);
- fl_rectf(ox+getpointx(lastpoint)-5,oy+getpointy(lastpoint)-5,10,10);
- }
-
- //draw the sustain position
- if(Penvsustain>0){
- fl_color(FL_YELLOW);
- xx=getpointx(Penvsustain);
- fl_line(ox+xx,oy+0,ox+xx,oy+ly);
- }
-
- //Show the envelope duration and the current line duration
- fl_font(FL_HELVETICA|FL_BOLD,10);
- float time=0.0;
- if (currentpoint<=0 && (!ctrldown||lastpoint <= 0)){
- fl_color(alb);
- for(int i=1; i<npoints; ++i)
- time+=getdt(i);
- } else {
- fl_color(FL_RED);
- time=getdt(lastpoint);
- }
- char tmpstr[20];
- if (!altdown || ctrldown) {
- if (time<1000.0)
- snprintf((char *)&tmpstr,20,"%.1fms",time);
- else
- snprintf((char *)&tmpstr,20,"%.2fs",time/1000.0);
- fl_draw(tmpstr,ox+lx-20,oy+ly-10,20,10,FL_ALIGN_RIGHT,NULL,0);
- }
- if (!altdown || !ctrldown) {
- if (lastpoint>=0){
- snprintf((char *)&tmpstr,20,"%d", Penvval[lastpoint]);
- fl_draw(tmpstr,ox+lx-20,oy+ly-23,20,10,FL_ALIGN_RIGHT,NULL,0);
- }
- }
- }
-
- int EnvelopeFreeEdit::handle(int event)
- {
- const int x_=Fl::event_x()-x();
- const int y_=Fl::event_y()-y();
- static Fl_Widget *old_focus;
- int key, old_mod_state;
-
- switch(event) {
- case FL_ENTER:
- old_focus=Fl::focus();
- Fl::focus(this);
- // Otherwise the underlying window seems to regrab focus,
- // and I can't see the KEYDOWN action.
- return 1;
- case FL_LEAVE:
- Fl::focus(old_focus);
- break;
- case FL_KEYDOWN:
- case FL_KEYUP:
- key = Fl::event_key();
- if (key==FL_Alt_L || key==FL_Alt_R) {
- altdown = (event==FL_KEYDOWN);
- redraw();
- if (pair!=NULL) pair->redraw();
- }
- if (key==FL_Control_L || key==FL_Control_R){
- ctrldown = (event==FL_KEYDOWN);
- redraw();
- if (pair!=NULL) pair->redraw();
- }
- break;
- case FL_PUSH:
- currentpoint=getnearest(x_,y_);
- cpx=x_;
- cpy=y_;
- cpdt=Penvdt[currentpoint];
- cpval=Penvval[currentpoint];
- lastpoint=currentpoint;
- redraw();
- if (pair)
- pair->redraw();
- return 1;
- case FL_RELEASE:
- currentpoint=-1;
- redraw();
- if (pair)
- pair->redraw();
- return 1;
- case FL_MOUSEWHEEL:
- if (Fl::event_buttons())
- return 1;
- if (lastpoint>=0) {
- int delta = Fl::event_dy() * (Fl::event_shift() ? 4 : 1);
- if (!ctrldown) {
- int ny = Penvval[lastpoint] - delta;
- ny = ny < 0 ? 0 : ny > 127 ? 127 : ny;
- Penvval[lastpoint] = ny;
- oscWrite(to_s("Penvval")+to_s(lastpoint), "c", ny);
- oscWrite("Penvval","");
- } else if (lastpoint > 0) {
- int newdt = Penvdt[lastpoint] - delta;
- newdt = newdt < 0 ? 0 : newdt > 127 ? 127 : newdt;
- Penvdt[lastpoint] = newdt;
- oscWrite(to_s("Penvdt")+to_s(lastpoint), "c", newdt);
- oscWrite("Penvdt","");
- }
- redraw();
- if (pair!=NULL) pair->redraw();
- return 1;
- }
- case FL_DRAG:
- if (currentpoint>=0){
- old_mod_state = mod_state;
- mod_state = ctrldown << 1 | altdown;
- if (old_mod_state != mod_state) {
- cpx=x_;
- cpy=y_;
- cpdt=Penvdt[currentpoint];
- cpval=Penvval[currentpoint];
- old_mod_state = mod_state;
- }
-
- if (!altdown || !ctrldown) {
- const int dy=(int)((cpy-y_)/3.0);
- const int newval=limit(cpval+dy, 0, 127);
-
- Penvval[currentpoint]=newval;
- oscWrite(to_s("Penvval")+to_s(currentpoint), "c", newval);
- oscWrite("Penvval","");
- }
-
- if (!altdown || ctrldown) {
- const int dx=(int)((x_-cpx)*0.1);
- const int newdt=limit(cpdt+dx,0,127);
-
- if(currentpoint!=0)
- Penvdt[currentpoint]=newdt;
- else
- Penvdt[currentpoint]=0;
- oscWrite(to_s("Penvdt")+to_s(currentpoint), "c", newdt);
- oscWrite("Penvdt","");
- }
-
- redraw();
-
- if(pair)
- pair->redraw();
- return 1;
- }
- }
- // Needed to propagate undo/redo keys.
- return 0;
- }
-
- void EnvelopeFreeEdit::update(void)
- {
- oscWrite("Penvpoints");
- oscWrite("Penvdt");
- oscWrite("Penvval");
- oscWrite("Penvsustain");
- }
-
- void EnvelopeFreeEdit::rebase(std::string new_base)
- {
- osc->renameLink(loc+"Penvpoints", new_base+"Penvpoints", this);
- osc->renameLink(loc+"Penvdt", new_base+"Penvdt", this);
- osc->renameLink(loc+"Penvval", new_base+"Penvval", this);
- osc->renameLink(loc+"Penvsustain", new_base+"Penvsustain", this);
- for(int i=0; i<MAX_ENVELOPE_POINTS; ++i) {
- string dt = string("Penvdt") + to_s(i);
- string val = string("Penvval") + to_s(i);
- osc->renameLink(loc+dt, new_base+dt, this);
- osc->renameLink(loc+val, new_base+val, this);
- }
- loc = new_base;
- update();
- }
|