zynaddsubfx

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

commit 4c93218559ec43945b4d708f75939669fb9fc086
parent 38a80b06c8d3d5b97d4bc87ea612ece2f4e60692
Author: fundamental <mark.d.mccurry@gmail.com>
Date:   Sat, 11 Jul 2009 10:32:33 -0400

Merge branch 'portamento'

Conflicts:
	src/Tests/make.sh

Added Proportional Portamento to 'master'

Diffstat:
MChangeLog | 3+++
Msrc/Params/Controller.cpp | 26++++++++++++++++++++++++--
Msrc/Params/Controller.h | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Asrc/Tests/ControllerTest.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Tests/EchoTest.h | 15++++++---------
Msrc/Tests/make.sh | 4++--
Msrc/UI/PartUI.fl | 50+++++++++++++++++++++++++++++++++++++-------------
7 files changed, 204 insertions(+), 45 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -873,3 +873,6 @@ 10 Iul 2009 (Paul Nasca) - Update copyright info + +11 Jul 2009 (Mark McCurry) + - Added Proportinal Portamento diff --git a/src/Params/Controller.cpp b/src/Params/Controller.cpp @@ -52,6 +52,9 @@ void Controller::defaults() portamento.portamento=0; portamento.used=0; + portamento.proportional=0; + portamento.propRate=80; + portamento.propDepth=90; portamento.receive=1; portamento.time=64; portamento.updowntimestretch=64; @@ -179,8 +182,6 @@ void Controller::setportamento(int value) if (portamento.receive!=0) portamento.portamento=((value<64) ? 0 : 1 ); }; -// I added a third argument to pass legato status, -// when legatoflag is true it means "there's a legato in progress". int Controller::initportamento(REALTYPE oldfreq,REALTYPE newfreq,bool legatoflag) { portamento.x=0.0; @@ -192,6 +193,18 @@ int Controller::initportamento(REALTYPE oldfreq,REALTYPE newfreq,bool legatoflag }; REALTYPE portamentotime=pow(100.0,portamento.time/127.0)/50.0;//portamento time in seconds + + if (portamento.proportional) { + //If there is a min(float,float) and a max(float,float) then they + //could be used here + //Linear functors could also make this nicer + if(oldfreq > newfreq) //2 is the center of propRate + portamentotime *= pow(oldfreq/newfreq/(portamento.propRate/127.0*3+.05), + (portamento.propDepth/127.0*1.6+.2)); + else //1 is the center of propDepth + portamentotime *= pow(newfreq/oldfreq/(portamento.propRate/127.0*3+.05), + (portamento.propDepth/127.0*1.6+.2)); + } if ((portamento.updowntimestretch>=64)&&(newfreq<oldfreq)) { if (portamento.updowntimestretch==127) return(0); @@ -202,6 +215,8 @@ int Controller::initportamento(REALTYPE oldfreq,REALTYPE newfreq,bool legatoflag portamentotime*=pow(0.1,(64.0-portamento.updowntimestretch)/64.0); }; + //printf("%f->%f : Time %f\n",oldfreq,newfreq,portamentotime); + portamento.dx=SOUND_BUFFER_SIZE/(portamentotime*SAMPLE_RATE); portamento.origfreqrap=oldfreq/newfreq; @@ -303,6 +318,9 @@ void Controller::add2XML(XMLwrapper *xml) xml->addpar("portamento_pitchthreshtype",portamento.pitchthreshtype); xml->addpar("portamento_portamento",portamento.portamento); xml->addpar("portamento_updowntimestretch",portamento.updowntimestretch); + xml->addpar("portamento_proportional",portamento.proportional); + xml->addpar("portamento_proprate",portamento.propRate); + xml->addpar("portamento_propdepth",portamento.propDepth); xml->addpar("resonance_center_depth",resonancecenter.depth); xml->addpar("resonance_bandwidth_depth",resonancebandwidth.depth); @@ -329,6 +347,10 @@ void Controller::getfromXML(XMLwrapper *xml) portamento.pitchthreshtype=xml->getpar127("portamento_pitchthreshtype",portamento.pitchthreshtype); portamento.portamento=xml->getpar127("portamento_portamento",portamento.portamento); portamento.updowntimestretch=xml->getpar127("portamento_updowntimestretch",portamento.updowntimestretch); + portamento.proportional=xml->getpar127("portamento_proportional",portamento.proportional); + portamento.propRate=xml->getpar127("portamento_proprate",portamento.propRate); + portamento.propDepth=xml->getpar127("portamento_propdepth",portamento.propDepth); + resonancecenter.depth=xml->getpar127("resonance_center_depth",resonancecenter.depth); resonancebandwidth.depth=xml->getpar127("resonance_bandwidth_depth",resonancebandwidth.depth); diff --git a/src/Params/Controller.h b/src/Params/Controller.h @@ -51,6 +51,8 @@ public: void setfmamp(int value); void setvolume(int value); void setsustain(int value); + /**Enable or disable portamento + * @param value 0-127 MIDI value (greater than 64 enables)*/ void setportamento(int value); void setresonancecenter(int value); void setresonancebw(int value); @@ -59,8 +61,16 @@ public: void setparameternumber(unsigned int type,int value);//used for RPN and NRPN's int getnrpn(int *parhi, int *parlo, int *valhi, int *vallo); - int initportamento(REALTYPE oldfreq,REALTYPE newfreq,bool legatoflag);//returns 1 if the portamento's conditions are true, else return 0 - void updateportamento(); //update portamento values + /** + * Initialize a portamento + * + * @param oldfreq Starting frequency of the portamento (Hz) + * @param newfreq Ending frequency of the portamento (Hz) + * @param legatoflag true when legato is in progress, false otherwise + * @returns 1 if properly initialized, 0 otherwise*/ + int initportamento(REALTYPE oldfreq,REALTYPE newfreq,bool legatoflag); + /**Update portamento's freqrap to next value based upon dx*/ + void updateportamento(); // Controllers values struct {//Pitch Wheel @@ -129,29 +139,59 @@ public: //parameters int data; unsigned char portamento; - - unsigned char receive,time; - /**pitchthresh is the threshold of enabling protamento \todo see if this should be an int*/ + /**Whether the portamento midi events are received or not*/ + unsigned char receive; + /** The time that it takes for the portamento to complete + * + * Translates in an expontal fashion to 0 Seconds to 1.93 Seconds + * of completion time*/ + unsigned char time; + /**If the portamento is proportinal to the distance spanned + * + * 0 - constant time(default) + * 1 - proportional*/ + unsigned char proportional; + /**Rate of proportinal portamento*/ + unsigned char propRate; + /**Depth of proportinal portamento*/ + unsigned char propDepth; + /**pitchthresh is the threshold of enabling protamento*/ unsigned char pitchthresh; - /**pitchthreshtype -> enable the portamento only below(0)/above(1) the threshold*/ + /**enable the portamento only below(0)/above(1) the threshold*/ unsigned char pitchthreshtype; /**this value represent how the portamento time is reduced - * 0 - for down portamento, 1..63 - the up portamento's time is smaller than the down portamento - * 64 - the portamento time is always the same - * 64-126 - the down portamento's time is smaller than the up portamento - * 127 - for upper portamento - * 'up portanemto' means when the frequency is rising (eg: the portamento is from 200Hz to 300 Hz) - * 'down portanemto' means when the frequency is lowering (eg: the portamento is from 300Hz to 200 Hz) + * 0 - for down portamento + * 1-63 - the up portamento's time is smaller than the down portamento + * 64 - the portamento time is always the same + * 64-126 - the down portamento's time is smaller than the up portamento + * 127 - for upper portamento + * 'up portamento' means when the frequency is rising + * (eg: the portamento is from 200Hz to 300 Hz) + * 'down portamento' means when the frequency is lowering + * (eg: the portamento is from 300Hz to 200 Hz) */ unsigned char updowntimestretch; - - REALTYPE freqrap;/**<this value is used to compute the actual portamento*/ - int noteusing;/**this is used by the Part for knowing which note uses the portamento*/ - int used;/**<if a the portamento is used by a note \todo see if this can be a bool*/ - //internal data - REALTYPE x,dx;//x is from 0.0 (start portamento) to 1.0 (finished portamento), dx is x increment - REALTYPE origfreqrap;// this is used for computing oldfreq value from x + /**this value is used to compute the actual portamento + * + * This is a multiplyer to change the frequency of the newer + * frequency to fit the profile of the portamento. + * This will be linear with respect to x.*/ + REALTYPE freqrap; + /**this is used by the Part for knowing which note uses the portamento*/ + int noteusing; + /**if a the portamento is used by a note + * \todo see if this can be a bool*/ + int used; + + //Internal data + + /**x is from 0.0 (start portamento) to 1.0 (finished portamento)*/ + REALTYPE x; + /**dx is the increment to x when updateportamento is called*/ + REALTYPE dx; + /** this is used for computing oldfreq value from x*/ + REALTYPE origfreqrap; } portamento; struct {//Resonance Center Frequency diff --git a/src/Tests/ControllerTest.h b/src/Tests/ControllerTest.h @@ -0,0 +1,73 @@ +/* + ZynAddSubFX - a software synthesizer + + ControllerTest.h - CxxTest for Params/Controller + Copyright (C) 2009-2009 Mark McCurry + Author: Mark McCurry + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include <cxxtest/TestSuite.h> +#include <iostream> +#include "../Params/Controller.h" + +class ControllerTest : public CxxTest::TestSuite +{ +public: + void setUp() { + testCtl=new Controller(); + } + + void tearDown() { + delete testCtl; + } + + + void testPortamentoRange() { + //Initialize portamento + testCtl->setportamento(127); + testCtl->portamento.time=127; + testCtl->initportamento(40.0,400.0,false); + //Bounds Check + while(testCtl->portamento.used){ + TS_ASSERT((0.0<=testCtl->portamento.x)&& + (testCtl->portamento.x<=1.0)); + TS_ASSERT((0.1<=testCtl->portamento.freqrap)&& + (testCtl->portamento.freqrap<=1.0)); + testCtl->updateportamento(); + } + TS_ASSERT((0.0<=testCtl->portamento.x)&& + (testCtl->portamento.x<=1.0)); + TS_ASSERT((0.1<=testCtl->portamento.freqrap)&& + (testCtl->portamento.freqrap<=1.0)); + } + + void testPortamentoValue() { + testCtl->setportamento(127); + testCtl->portamento.time=127; + testCtl->initportamento(40.0,400.0,false); + int i; + for(i=0;i<10;++i){ + testCtl->updateportamento(); + } + //Assert that the numbers are the same as they were at release + TS_ASSERT_DELTA(testCtl->portamento.x,0.0290249,0.000001) + TS_ASSERT_DELTA(testCtl->portamento.freqrap,0.126122,0.000001) + } + +private: + Controller *testCtl; + +}; diff --git a/src/Tests/EchoTest.h b/src/Tests/EchoTest.h @@ -24,9 +24,6 @@ #include "../Effects/Echo.h" #include "../globals.h" -int SOUND_BUFFER_SIZE=256; -int SAMPLE_RATE=1024; - class EchoTest : public CxxTest::TestSuite { public: @@ -59,10 +56,10 @@ public: //Make sure that the output will be zero at start //(given a zero input) testFX->out(inL,inR); - for (int i=0;i<SOUND_BUFFER_SIZE;++i) - TS_ASSERT_EQUALS(outL[i],0.0); - for (int i=0;i<SOUND_BUFFER_SIZE;++i) - TS_ASSERT_EQUALS(outR[i],0.0); + for (int i=0;i<SOUND_BUFFER_SIZE;++i){ + TS_ASSERT_DELTA(outL[i],0.0,0.0001); + TS_ASSERT_DELTA(outR[i],0.0,0.0001); + } } void testClear() { @@ -72,7 +69,7 @@ public: *(inL+i)=1.0; for (int i=0;i<SOUND_BUFFER_SIZE;++i) *(inR+i)=1.0; - for (int i=0;i<50;++i) + for (int i=0;i<500;++i) testFX->out(inL,inR); for (int i=0;i<SOUND_BUFFER_SIZE;++i) { TS_ASSERT_DIFFERS(outL[i],0.0); @@ -97,7 +94,7 @@ public: *(inR+i)=1.0; char FEEDBACK=5; testFX->changepar(FEEDBACK,127); - for (int i=0;i<4;++i) + for (int i=0;i<100;++i) testFX->out(inL,inR); for (int i=0;i<SOUND_BUFFER_SIZE;++i) { TS_ASSERT_DIFFERS(outL[i],0.0); diff --git a/src/Tests/make.sh b/src/Tests/make.sh @@ -1,5 +1,5 @@ cd ../ make cd Tests -cxxtestgen.py --error-printer -o runner.cpp SampleTest.h EchoTest.h -g++ -g -o runner runner.cpp ../Samples/AuSample.o ../Samples/Sample.o ../Effects/Echo.o ../Effects/Effect.o ../Controls/Control.o ../Controls/DelayCtl.o +cxxtestgen.py --error-printer -o runner.cpp SampleTest.h EchoTest.h ControllerTest.h +g++ -g -o runner runner.cpp ../Samples/AuSample.o ../Samples/Sample.o ../Effects/Echo.o ../Effects/Effect.o ../Controls/Control.o ../Controls/DelayCtl.o ../Params/Controller.o ../Misc/XMLwrapper.o ../Misc/Config.o ../Misc/Util.o -lmxml -lm -lz -lpthread diff --git a/src/UI/PartUI.fl b/src/UI/PartUI.fl @@ -42,9 +42,9 @@ decl {\#include "../Misc/Master.h"} {public decl {\#include "../Misc/Part.h"} {public } -class PartSysEffSend {open : {public Fl_Group} +class PartSysEffSend {: {public Fl_Group} } { - Function {make_window()} {open private + Function {make_window()} {private } { Fl_Window syseffsend { private xywh {584 83 90 35} type Double hide @@ -52,7 +52,7 @@ class PartSysEffSend {open : {public Fl_Group} } { Fl_Dial {} { label 01 - callback {master->setPsysefxvol(npart,neff,(int) o->value());} selected + callback {master->setPsysefxvol(npart,neff,(int) o->value());} xywh {0 0 25 25} box ROUND_UP_BOX labelfont 1 labelsize 10 align 130 maximum 127 step 1 code0 {o->size(25,25);} code1 {o->value(master->Psysefxvol[neff][npart]);} @@ -274,9 +274,9 @@ end();} {} decl {PartUI_ *partui;} {} } -class PartUI {open : {public Fl_Group,PartUI_} +class PartUI {: {public Fl_Group,PartUI_} } { - Function {make_window()} {open private + Function {make_window()} {private } { Fl_Window partgroup { private xywh {424 178 385 180} type Double hide @@ -457,7 +457,7 @@ if (part->Penabled==0) partgroupui->deactivate(); } Fl_Window ctlwindow { label Controllers - private xywh {198 472 460 130} type Double hide + private xywh {542 212 500 130} type Double hide } { Fl_Check_Button {} { label Expr @@ -540,8 +540,8 @@ if (part->ctl.sustain.receive==0) { xywh {5 105 210 20} box THIN_UP_BOX } Fl_Group {} { - label Portamento open - xywh {280 15 120 85} box ENGRAVED_FRAME labelfont 1 labelsize 10 + label Portamento + xywh {280 15 160 90} box ENGRAVED_FRAME labelfont 1 labelsize 10 } { Fl_Check_Button {} { label Rcv @@ -565,7 +565,7 @@ if (part->ctl.sustain.receive==0) { Fl_Check_Button {} { label {th.type} callback {part->ctl.portamento.pitchthreshtype=(int) o->value();} - tooltip {Threshold type (min/max)} xywh {370 70 15 15} down_box DOWN_BOX labelsize 10 align 2 + tooltip {Threshold type (min/max)} xywh {365 70 15 15} down_box DOWN_BOX labelsize 10 align 2 code0 {o->value(part->ctl.portamento.pitchthreshtype);} } Fl_Box {} { @@ -581,22 +581,46 @@ part->ctl.portamento.updowntimestretch=x;} code0 {o->value(part->ctl.portamento.updowntimestretch);} class WidgetPDial } + Fl_Dial propta { + label {Prp.Rate} + callback {part->ctl.portamento.propRate=(int) o->value();} + tooltip {Distance required to double change from nonpropotinal portamento time} xywh {405 20 25 25} labelsize 9 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.propRate);} + class WidgetPDial + } + Fl_Dial proptb { + label {Prp.Dpth} + callback {part->ctl.portamento.propDepth=(int) o->value();} selected + tooltip {The difference from nonproportinal portamento} xywh {405 60 25 25} labelsize 9 maximum 127 step 1 + code0 {o->value(part->ctl.portamento.propDepth);} + class WidgetPDial + } + Fl_Check_Button {} { + label {Proprt.} + callback {part->ctl.portamento.proportional=(int) o->value(); +if(o->value()){propta->activate();proptb->activate();} +else {propta->deactivate();proptb->deactivate();}} + tooltip {Enable Proportinal Portamento (over fixed Portamento)} xywh {285 40 50 15} box THIN_UP_BOX down_box DOWN_BOX labelsize 9 + code0 {o->value(part->ctl.portamento.proportional);} + code1 {if(o->value()){propta->activate();proptb->activate();}} + code2 {else {propta->deactivate();proptb->deactivate();}} + } } Fl_Group {} { - label Resonance open - xywh {400 15 45 85} box ENGRAVED_BOX labelfont 1 labelsize 10 + label Resonance + xywh {440 15 50 90} box ENGRAVED_BOX labelfont 1 labelsize 10 } { Fl_Dial {} { label BWdpth callback {part->ctl.resonancebandwidth.depth=(int) o->value();} - tooltip {BandWidth controller depth} xywh {410 60 25 25} labelsize 10 maximum 127 step 1 + tooltip {BandWidth controller depth} xywh {450 60 25 25} labelsize 10 maximum 127 step 1 code0 {o->value(part->ctl.resonancebandwidth.depth);} class WidgetPDial } Fl_Dial {} { label CFdpth callback {part->ctl.resonancecenter.depth=(int) o->value();} - tooltip {Center Frequency controller Depth} xywh {410 20 25 25} labelsize 10 maximum 127 step 1 + tooltip {Center Frequency controller Depth} xywh {450 20 25 25} labelsize 10 maximum 127 step 1 code0 {o->value(part->ctl.resonancecenter.depth);} class WidgetPDial }