| @@ -402,6 +402,13 @@ extern "C" { | |||||
| #undef rChangeCb | #undef rChangeCb | ||||
| #define rChangeCb | #define rChangeCb | ||||
| #include "zynaddsubfx/Synth/ModFilter.cpp" | |||||
| #undef rObject | |||||
| #undef rStdString | |||||
| #undef rStdStringCb | |||||
| #undef rChangeCb | |||||
| #define rChangeCb | |||||
| #include "zynaddsubfx/Synth/OscilGen.cpp" | #include "zynaddsubfx/Synth/OscilGen.cpp" | ||||
| #undef PC | #undef PC | ||||
| #undef DIFF | #undef DIFF | ||||
| @@ -279,7 +279,7 @@ void NotePool::enforceKeyLimit(int limit) | |||||
| void NotePool::releasePlayingNotes(void) | void NotePool::releasePlayingNotes(void) | ||||
| { | { | ||||
| for(auto &d:activeDesc()) { | for(auto &d:activeDesc()) { | ||||
| if(d.playing()) { | |||||
| if(d.playing() || d.sustained()) { | |||||
| d.setStatus(KEY_RELEASED); | d.setStatus(KEY_RELEASED); | ||||
| for(auto s:activeNotes(d)) | for(auto s:activeNotes(d)) | ||||
| s.note->releasekey(); | s.note->releasekey(); | ||||
| @@ -323,9 +323,11 @@ void AnalogFilter::setstages(int stages_) | |||||
| { | { | ||||
| if(stages_ >= MAX_FILTER_STAGES) | if(stages_ >= MAX_FILTER_STAGES) | ||||
| stages_ = MAX_FILTER_STAGES - 1; | stages_ = MAX_FILTER_STAGES - 1; | ||||
| stages = stages_; | |||||
| cleanup(); | |||||
| computefiltercoefs(); | |||||
| if(stages_ != stages) { | |||||
| stages = stages_; | |||||
| cleanup(); | |||||
| computefiltercoefs(); | |||||
| } | |||||
| } | } | ||||
| inline void AnalogBiquadFilterA(const float coeff[5], float &src, float work[4]) | inline void AnalogBiquadFilterA(const float coeff[5], float &src, float work[4]) | ||||
| @@ -39,7 +39,7 @@ Filter::Filter(unsigned int srate, int bufsize) | |||||
| alias(); | alias(); | ||||
| } | } | ||||
| Filter *Filter::generate(Allocator &memory, FilterParams *pars, | |||||
| Filter *Filter::generate(Allocator &memory, const FilterParams *pars, | |||||
| unsigned int srate, int bufsize) | unsigned int srate, int bufsize) | ||||
| { | { | ||||
| assert(srate != 0); | assert(srate != 0); | ||||
| @@ -29,7 +29,7 @@ class Filter | |||||
| { | { | ||||
| public: | public: | ||||
| static float getrealfreq(float freqpitch); | static float getrealfreq(float freqpitch); | ||||
| static Filter *generate(class Allocator &memory, class FilterParams *pars, | |||||
| static Filter *generate(Allocator &memory, const FilterParams *pars, | |||||
| unsigned int srate, int bufsize); | unsigned int srate, int bufsize); | ||||
| Filter(unsigned int srate, int bufsize); | Filter(unsigned int srate, int bufsize); | ||||
| @@ -28,8 +28,8 @@ | |||||
| #include "AnalogFilter.h" | #include "AnalogFilter.h" | ||||
| #include "../Params/FilterParams.h" | #include "../Params/FilterParams.h" | ||||
| FormantFilter::FormantFilter(FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize) | |||||
| : Filter(srate, bufsize), memory(*alloc) | |||||
| FormantFilter::FormantFilter(const FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize) | |||||
| :Filter(srate, bufsize), memory(*alloc) | |||||
| { | { | ||||
| numformants = pars->Pnumformants; | numformants = pars->Pnumformants; | ||||
| for(int i = 0; i < numformants; ++i) | for(int i = 0; i < numformants; ++i) | ||||
| @@ -27,11 +27,10 @@ | |||||
| #include "Filter.h" | #include "Filter.h" | ||||
| class Allocator; | |||||
| class FormantFilter:public Filter | class FormantFilter:public Filter | ||||
| { | { | ||||
| public: | public: | ||||
| FormantFilter(class FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize); | |||||
| FormantFilter(const FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize); | |||||
| ~FormantFilter(); | ~FormantFilter(); | ||||
| void filterout(float *smp); | void filterout(float *smp); | ||||
| void setfreq(float frequency); | void setfreq(float frequency); | ||||
| @@ -85,7 +85,7 @@ void SVFilter::setfreq(float frequency) | |||||
| bool nyquistthresh = (abovenq ^ oldabovenq); | bool nyquistthresh = (abovenq ^ oldabovenq); | ||||
| //if the frequency is changed fast, it needs interpolation | //if the frequency is changed fast, it needs interpolation | ||||
| if((rap > 3.0f) || nyquistthresh) { //(now, filter and coeficients backup) | |||||
| if((rap > 3.0f) || nyquistthresh) { //(now, filter and coefficients backup) | |||||
| if(!firsttime) | if(!firsttime) | ||||
| needsinterpolation = true; | needsinterpolation = true; | ||||
| ipar = par; | ipar = par; | ||||
| @@ -212,6 +212,7 @@ int Bank::loadfromslot(unsigned int ninstrument, Part *part) | |||||
| */ | */ | ||||
| int Bank::loadbank(string bankdirname) | int Bank::loadbank(string bankdirname) | ||||
| { | { | ||||
| normalizedirsuffix(bankdirname); | |||||
| DIR *dir = opendir(bankdirname.c_str()); | DIR *dir = opendir(bankdirname.c_str()); | ||||
| clearbank(); | clearbank(); | ||||
| @@ -285,9 +286,8 @@ int Bank::newbank(string newbankdirname) | |||||
| string bankdir; | string bankdir; | ||||
| bankdir = config->cfg.bankRootDirList[0]; | bankdir = config->cfg.bankRootDirList[0]; | ||||
| if(((bankdir[bankdir.size() - 1]) != '/') | |||||
| && ((bankdir[bankdir.size() - 1]) != '\\')) | |||||
| bankdir += "/"; | |||||
| expanddirname(bankdir); | |||||
| normalizedirsuffix(bankdir); | |||||
| bankdir += newbankdirname; | bankdir += newbankdirname; | ||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||
| @@ -404,6 +404,8 @@ void Bank::setLsb(uint8_t lsb) | |||||
| void Bank::scanrootdir(string rootdir) | void Bank::scanrootdir(string rootdir) | ||||
| { | { | ||||
| expanddirname(rootdir); | |||||
| DIR *dir = opendir(rootdir.c_str()); | DIR *dir = opendir(rootdir.c_str()); | ||||
| if(dir == NULL) | if(dir == NULL) | ||||
| return; | return; | ||||
| @@ -498,3 +500,23 @@ void Bank::deletefrombank(int pos) | |||||
| Bank::ins_t::ins_t() | Bank::ins_t::ins_t() | ||||
| :name(""), filename("") | :name(""), filename("") | ||||
| {} | {} | ||||
| void Bank::expanddirname(std::string &dirname) { | |||||
| if (dirname.empty()) | |||||
| return; | |||||
| // if the directory name starts with a ~ and the $HOME variable is | |||||
| // defined in the environment, replace ~ by the content of $HOME | |||||
| if (dirname.at(0) == '~') { | |||||
| char *home_dirname = getenv("HOME"); | |||||
| if (home_dirname != NULL) { | |||||
| dirname = std::string(home_dirname) + dirname.substr(1); | |||||
| } | |||||
| } | |||||
| } | |||||
| void Bank::normalizedirsuffix(string &dirname) const { | |||||
| if(((dirname[dirname.size() - 1]) != '/') | |||||
| && ((dirname[dirname.size() - 1]) != '\\')) | |||||
| dirname += "/"; | |||||
| } | |||||
| @@ -100,7 +100,14 @@ class Bank | |||||
| std::string dirname; | std::string dirname; | ||||
| void scanrootdir(std::string rootdir); //scans a root dir for banks | void scanrootdir(std::string rootdir); //scans a root dir for banks | ||||
| /** Expends ~ prefix in dirname, if any */ | |||||
| void expanddirname(std::string &dirname); | |||||
| /** Ensure that the directory name is suffixed by a | |||||
| * directory separator */ | |||||
| void normalizedirsuffix(std::string &dirname) const; | |||||
| Config* const config; | Config* const config; | ||||
| public: | public: | ||||
| @@ -23,6 +23,7 @@ | |||||
| #include <cmath> | #include <cmath> | ||||
| #include <cstring> | #include <cstring> | ||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <cassert> | |||||
| #include <rtosc/ports.h> | #include <rtosc/ports.h> | ||||
| #include <rtosc/port-sugar.h> | #include <rtosc/port-sugar.h> | ||||
| @@ -72,6 +73,118 @@ const rtosc::Ports Microtonal::ports = { | |||||
| Microtonal &m = *(Microtonal*)d.obj; | Microtonal &m = *(Microtonal*)d.obj; | ||||
| d.reply(d.loc, "i", m.getoctavesize()); | d.reply(d.loc, "i", m.getoctavesize()); | ||||
| }}, | }}, | ||||
| {"mapping::s", rDoc("Get user editable tunings"), 0, [](const char *msg, RtData &d) | |||||
| { | |||||
| char buf[100*MAX_OCTAVE_SIZE] = {0}; | |||||
| char tmpbuf[100] = {0}; | |||||
| Microtonal &m = *(Microtonal*)d.obj; | |||||
| if(rtosc_narguments(msg) == 1) { | |||||
| m.texttomapping(rtosc_argument(msg,0).s); | |||||
| } else { | |||||
| for (int i=0;i<m.Pmapsize;i++){ | |||||
| if (i!=0) | |||||
| strncat(buf, "\n", sizeof(buf)-1); | |||||
| if (m.Pmapping[i]==-1) | |||||
| snprintf(tmpbuf,100,"x"); | |||||
| else | |||||
| snprintf(tmpbuf,100,"%d",m.Pmapping[i]); | |||||
| strncat(buf, tmpbuf, sizeof(buf)-1); | |||||
| }; | |||||
| d.reply(d.loc, "s", buf); | |||||
| } | |||||
| }}, | |||||
| {"tunings::s", rDoc("Get user editable tunings"), 0, [](const char *msg, RtData &d) | |||||
| { | |||||
| char buf[100*MAX_OCTAVE_SIZE] = {0}; | |||||
| char tmpbuf[100] = {0}; | |||||
| Microtonal &m = *(Microtonal*)d.obj; | |||||
| if(rtosc_narguments(msg) == 1) { | |||||
| int err = m.texttotunings(rtosc_argument(msg,0).s); | |||||
| if (err>=0) | |||||
| d.reply("/alert", "s", | |||||
| "Parse Error: The input may contain only numbers (like 232.59)\n" | |||||
| "or divisions (like 121/64)."); | |||||
| if (err==-2) | |||||
| d.reply("/alert", "s", "Parse Error: The input is empty."); | |||||
| } else { | |||||
| for (int i=0;i<m.getoctavesize();i++){ | |||||
| if (i!=0) | |||||
| strncat(buf, "\n", sizeof(buf)-1); | |||||
| m.tuningtoline(i,tmpbuf,100); | |||||
| strncat(buf, tmpbuf, sizeof(buf)-1); | |||||
| }; | |||||
| d.reply(d.loc, "s", buf); | |||||
| } | |||||
| }}, | |||||
| #define COPY(x) self.x = other->x; | |||||
| {"paste:b", rProp(internal) rDoc("Clone Input Microtonal Object"), 0, | |||||
| [](const char *msg, RtData &d) | |||||
| { | |||||
| rtosc_blob_t b = rtosc_argument(msg, 0).b; | |||||
| assert(b.len == sizeof(void*)); | |||||
| Microtonal *other = *(Microtonal**)b.len; | |||||
| Microtonal &self = *(Microtonal*)d.obj; | |||||
| //oh how I wish there was some darn reflection for this... | |||||
| COPY(Pinvertupdown); | |||||
| COPY(Pinvertupdowncenter); | |||||
| COPY(Penabled); | |||||
| COPY(PAnote); | |||||
| COPY(PAfreq); | |||||
| COPY(Pscaleshift); | |||||
| COPY(Pfirstkey); | |||||
| COPY(Plastkey); | |||||
| COPY(Pmiddlenote); | |||||
| COPY(Pmapsize); | |||||
| COPY(Pmappingenabled); | |||||
| for(int i=0; i<self.octavesize; ++i) | |||||
| self.octave[i] = other->octave[i]; | |||||
| COPY(Pglobalfinedetune); | |||||
| memcpy(self.Pname, other->Pname, sizeof(self.Pname)); | |||||
| memcpy(self.Pcomment, other->Pcomment, sizeof(self.Pcomment)); | |||||
| COPY(octavesize); | |||||
| for(int i=0; i<self.octavesize; ++i) | |||||
| self.octave[i] = other->octave[i]; | |||||
| d.reply("/free", "sb", "Microtonal", b.len, b.data); | |||||
| }}, | |||||
| {"paste_scl:b", rProp(internal) rDoc("Clone Input scl Object"), 0, | |||||
| [](const char *msg, RtData &d) | |||||
| { | |||||
| rtosc_blob_t b = rtosc_argument(msg, 0).b; | |||||
| assert(b.len == sizeof(void*)); | |||||
| SclInfo *other = *(SclInfo**)b.data; | |||||
| Microtonal &self = *(Microtonal*)d.obj; | |||||
| memcpy(self.Pname, other->Pname, sizeof(self.Pname)); | |||||
| memcpy(self.Pcomment, other->Pcomment, sizeof(self.Pcomment)); | |||||
| COPY(octavesize); | |||||
| for(int i=0; i<self.octavesize; ++i) | |||||
| self.octave[i] = other->octave[i]; | |||||
| d.reply("/free", "sb", "SclInfo", b.len, b.data); | |||||
| }}, | |||||
| {"paste_kbm:b", rProp(internal) rDoc("Clone Input kbm Object"), 0, | |||||
| [](const char *msg, RtData &d) | |||||
| { | |||||
| rtosc_blob_t b = rtosc_argument(msg, 0).b; | |||||
| assert(b.len == sizeof(void*)); | |||||
| KbmInfo *other = *(KbmInfo**)b.data; | |||||
| Microtonal &self = *(Microtonal*)d.obj; | |||||
| COPY(Pmapsize); | |||||
| COPY(Pfirstkey); | |||||
| COPY(Plastkey); | |||||
| COPY(Pmiddlenote); | |||||
| COPY(PAnote); | |||||
| COPY(PAfreq); | |||||
| COPY(Pmappingenabled); | |||||
| for(int i=0; i<128; ++i) | |||||
| self.Pmapping[i] = other->Pmapping[i]; | |||||
| d.reply("/free", "sb", "KbmInfo", b.len, b.data); | |||||
| }}, | |||||
| #undef COPY | |||||
| }; | }; | ||||
| @@ -101,13 +214,10 @@ void Microtonal::defaults() | |||||
| Pmapping[i] = i; | Pmapping[i] = i; | ||||
| for(int i = 0; i < MAX_OCTAVE_SIZE; ++i) { | for(int i = 0; i < MAX_OCTAVE_SIZE; ++i) { | ||||
| octave[i].tuning = tmpoctave[i].tuning = powf( | |||||
| 2, | |||||
| (i % octavesize | |||||
| + 1) / 12.0f); | |||||
| octave[i].type = tmpoctave[i].type = 1; | |||||
| octave[i].x1 = tmpoctave[i].x1 = (i % octavesize + 1) * 100; | |||||
| octave[i].x2 = tmpoctave[i].x2 = 0; | |||||
| octave[i].tuning = powf(2, (i % octavesize + 1) / 12.0f); | |||||
| octave[i].type = 1; | |||||
| octave[i].x1 = (i % octavesize + 1) * 100; | |||||
| octave[i].x2 = 0; | |||||
| } | } | ||||
| octave[11].type = 2; | octave[11].type = 2; | ||||
| octave[11].x1 = 2; | octave[11].x1 = 2; | ||||
| @@ -298,7 +408,7 @@ bool Microtonal::operator!=(const Microtonal µ) const | |||||
| /* | /* | ||||
| * Convert a line to tunings; returns -1 if it ok | * Convert a line to tunings; returns -1 if it ok | ||||
| */ | */ | ||||
| int Microtonal::linetotunings(unsigned int nline, const char *line) | |||||
| int Microtonal::linetotunings(OctaveTuning &octave, const char *line) | |||||
| { | { | ||||
| int x1 = -1, x2 = -1, type = -1; | int x1 = -1, x2 = -1, type = -1; | ||||
| float x = -1.0f, tmp, tuning = 1.0f; | float x = -1.0f, tmp, tuning = 1.0f; | ||||
| @@ -346,10 +456,10 @@ int Microtonal::linetotunings(unsigned int nline, const char *line) | |||||
| break; | break; | ||||
| } | } | ||||
| tmpoctave[nline].tuning = tuning; | |||||
| tmpoctave[nline].type = type; | |||||
| tmpoctave[nline].x1 = x1; | |||||
| tmpoctave[nline].x2 = x2; | |||||
| octave.tuning = tuning; | |||||
| octave.type = type; | |||||
| octave.x1 = x1; | |||||
| octave.x2 = x2; | |||||
| return -1; //ok | return -1; //ok | ||||
| } | } | ||||
| @@ -359,10 +469,11 @@ int Microtonal::linetotunings(unsigned int nline, const char *line) | |||||
| */ | */ | ||||
| int Microtonal::texttotunings(const char *text) | int Microtonal::texttotunings(const char *text) | ||||
| { | { | ||||
| unsigned int i, k = 0, nl = 0; | |||||
| char *lin; | |||||
| lin = new char[MAX_LINE_SIZE + 1]; | |||||
| unsigned int k = 0, nl = 0; | |||||
| char *lin = new char[MAX_LINE_SIZE + 1]; | |||||
| OctaveTuning tmpoctave[MAX_OCTAVE_SIZE]; | |||||
| while(k < strlen(text)) { | while(k < strlen(text)) { | ||||
| int i; | |||||
| for(i = 0; i < MAX_LINE_SIZE; ++i) { | for(i = 0; i < MAX_LINE_SIZE; ++i) { | ||||
| lin[i] = text[k++]; | lin[i] = text[k++]; | ||||
| if(lin[i] < 0x20) | if(lin[i] < 0x20) | ||||
| @@ -371,7 +482,7 @@ int Microtonal::texttotunings(const char *text) | |||||
| lin[i] = '\0'; | lin[i] = '\0'; | ||||
| if(strlen(lin) == 0) | if(strlen(lin) == 0) | ||||
| continue; | continue; | ||||
| int err = linetotunings(nl, lin); | |||||
| int err = linetotunings(tmpoctave[nl], lin); | |||||
| if(err != -1) { | if(err != -1) { | ||||
| delete [] lin; | delete [] lin; | ||||
| return nl; //Parse error | return nl; //Parse error | ||||
| @@ -384,7 +495,7 @@ int Microtonal::texttotunings(const char *text) | |||||
| if(nl == 0) | if(nl == 0) | ||||
| return -2; //the input is empty | return -2; //the input is empty | ||||
| octavesize = nl; | octavesize = nl; | ||||
| for(i = 0; i < octavesize; ++i) { | |||||
| for(int i = 0; i < octavesize; ++i) { | |||||
| octave[i].tuning = tmpoctave[i].tuning; | octave[i].tuning = tmpoctave[i].tuning; | ||||
| octave[i].type = tmpoctave[i].type; | octave[i].type = tmpoctave[i].type; | ||||
| octave[i].x1 = tmpoctave[i].x1; | octave[i].x1 = tmpoctave[i].x1; | ||||
| @@ -449,28 +560,37 @@ void Microtonal::tuningtoline(int n, char *line, int maxn) | |||||
| int Microtonal::loadline(FILE *file, char *line) | int Microtonal::loadline(FILE *file, char *line) | ||||
| { | { | ||||
| memset(line, 0, 500); | |||||
| do { | do { | ||||
| if(fgets(line, 500, file) == 0) | if(fgets(line, 500, file) == 0) | ||||
| return 1; | return 1; | ||||
| } while(line[0] == '!'); | } while(line[0] == '!'); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* | /* | ||||
| * Loads the tunnings from a scl file | * Loads the tunnings from a scl file | ||||
| */ | */ | ||||
| int Microtonal::loadscl(const char *filename) | |||||
| int Microtonal::loadscl(SclInfo &scl, const char *filename) | |||||
| { | { | ||||
| FILE *file = fopen(filename, "r"); | FILE *file = fopen(filename, "r"); | ||||
| char tmp[500]; | char tmp[500]; | ||||
| OctaveTuning tmpoctave[MAX_OCTAVE_SIZE]; | |||||
| fseek(file, 0, SEEK_SET); | fseek(file, 0, SEEK_SET); | ||||
| //loads the short description | //loads the short description | ||||
| if(loadline(file, &tmp[0]) != 0) | if(loadline(file, &tmp[0]) != 0) | ||||
| return 2; | return 2; | ||||
| for(int i = 0; i < 500; ++i) | for(int i = 0; i < 500; ++i) | ||||
| if(tmp[i] < 32) | if(tmp[i] < 32) | ||||
| tmp[i] = 0; | tmp[i] = 0; | ||||
| snprintf((char *) Pname, MICROTONAL_MAX_NAME_LEN, "%s", tmp); | |||||
| snprintf((char *) Pcomment, MICROTONAL_MAX_NAME_LEN, "%s", tmp); | |||||
| snprintf(scl.Pname, MICROTONAL_MAX_NAME_LEN, "%s", tmp); | |||||
| snprintf(scl.Pcomment, MICROTONAL_MAX_NAME_LEN, "%s", tmp); | |||||
| //loads the number of the notes | //loads the number of the notes | ||||
| if(loadline(file, &tmp[0]) != 0) | if(loadline(file, &tmp[0]) != 0) | ||||
| return 2; | return 2; | ||||
| @@ -478,29 +598,31 @@ int Microtonal::loadscl(const char *filename) | |||||
| sscanf(&tmp[0], "%d", &nnotes); | sscanf(&tmp[0], "%d", &nnotes); | ||||
| if(nnotes > MAX_OCTAVE_SIZE) | if(nnotes > MAX_OCTAVE_SIZE) | ||||
| return 2; | return 2; | ||||
| //load the tunnings | //load the tunnings | ||||
| for(int nline = 0; nline < nnotes; ++nline) { | for(int nline = 0; nline < nnotes; ++nline) { | ||||
| if(loadline(file, &tmp[0]) != 0) | if(loadline(file, &tmp[0]) != 0) | ||||
| return 2; | return 2; | ||||
| linetotunings(nline, &tmp[0]); | |||||
| linetotunings(tmpoctave[nline], tmp); | |||||
| } | } | ||||
| fclose(file); | fclose(file); | ||||
| octavesize = nnotes; | |||||
| for(int i = 0; i < octavesize; ++i) { | |||||
| octave[i].tuning = tmpoctave[i].tuning; | |||||
| octave[i].type = tmpoctave[i].type; | |||||
| octave[i].x1 = tmpoctave[i].x1; | |||||
| octave[i].x2 = tmpoctave[i].x2; | |||||
| scl.octavesize = nnotes; | |||||
| for(int i = 0; i < scl.octavesize; ++i) { | |||||
| scl.octave[i].tuning = tmpoctave[i].tuning; | |||||
| scl.octave[i].type = tmpoctave[i].type; | |||||
| scl.octave[i].x1 = tmpoctave[i].x1; | |||||
| scl.octave[i].x2 = tmpoctave[i].x2; | |||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* | /* | ||||
| * Loads the mapping from a kbm file | * Loads the mapping from a kbm file | ||||
| */ | */ | ||||
| int Microtonal::loadkbm(const char *filename) | |||||
| int Microtonal::loadkbm(KbmInfo &kbm, const char *filename) | |||||
| { | { | ||||
| FILE *file = fopen(filename, "r"); | FILE *file = fopen(filename, "r"); | ||||
| int x; | int x; | ||||
| @@ -511,32 +633,32 @@ int Microtonal::loadkbm(const char *filename) | |||||
| //loads the mapsize | //loads the mapsize | ||||
| if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | ||||
| return 2; | return 2; | ||||
| Pmapsize = limit(x, 0, 127); | |||||
| kbm.Pmapsize = limit(x, 0, 127); | |||||
| //loads first MIDI note to retune | //loads first MIDI note to retune | ||||
| if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | ||||
| return 2; | return 2; | ||||
| Pfirstkey = limit(x, 0, 127); | |||||
| kbm.Pfirstkey = limit(x, 0, 127); | |||||
| //loads last MIDI note to retune | //loads last MIDI note to retune | ||||
| if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | ||||
| return 2; | return 2; | ||||
| Plastkey = limit(x, 0, 127); | |||||
| kbm.Plastkey = limit(x, 0, 127); | |||||
| //loads last the middle note where scale fro scale degree=0 | //loads last the middle note where scale fro scale degree=0 | ||||
| if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | ||||
| return 2; | return 2; | ||||
| Pmiddlenote = limit(x, 0, 127); | |||||
| kbm.Pmiddlenote = limit(x, 0, 127); | |||||
| //loads the reference note | //loads the reference note | ||||
| if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | ||||
| return 2; | return 2; | ||||
| PAnote = limit(x,0,127); | |||||
| kbm.PAnote = limit(x,0,127); | |||||
| //loads the reference freq. | //loads the reference freq. | ||||
| if(loadline(file, tmp) != 0 || sscanf(tmp, "%f", &tmpPAfreq) == 0) | if(loadline(file, tmp) != 0 || sscanf(tmp, "%f", &tmpPAfreq) == 0) | ||||
| return 2; | return 2; | ||||
| PAfreq = tmpPAfreq; | |||||
| kbm.PAfreq = tmpPAfreq; | |||||
| //the scale degree(which is the octave) is not loaded, | //the scale degree(which is the octave) is not loaded, | ||||
| //it is obtained by the tunnings with getoctavesize() method | //it is obtained by the tunnings with getoctavesize() method | ||||
| @@ -544,20 +666,20 @@ int Microtonal::loadkbm(const char *filename) | |||||
| return 2; | return 2; | ||||
| //load the mappings | //load the mappings | ||||
| if(Pmapsize != 0) { | |||||
| for(int nline = 0; nline < Pmapsize; ++nline) { | |||||
| if(kbm.Pmapsize != 0) { | |||||
| for(int nline = 0; nline < kbm.Pmapsize; ++nline) { | |||||
| if(loadline(file, tmp) != 0) | if(loadline(file, tmp) != 0) | ||||
| return 2; | return 2; | ||||
| if(sscanf(tmp, "%d", &x) == 0) | if(sscanf(tmp, "%d", &x) == 0) | ||||
| x = -1; | x = -1; | ||||
| Pmapping[nline] = x; | |||||
| kbm.Pmapping[nline] = x; | |||||
| } | } | ||||
| Pmappingenabled = 1; | |||||
| kbm.Pmappingenabled = 1; | |||||
| } | } | ||||
| else { | else { | ||||
| Pmappingenabled = 0; | |||||
| Pmapping[0] = 0; | |||||
| Pmapsize = 1; | |||||
| kbm.Pmappingenabled = 0; | |||||
| kbm.Pmapping[0] = 0; | |||||
| kbm.Pmapsize = 1; | |||||
| } | } | ||||
| fclose(file); | fclose(file); | ||||
| @@ -24,12 +24,43 @@ | |||||
| #define MICROTONAL_H | #define MICROTONAL_H | ||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <stdint.h> | |||||
| #include "../globals.h" | #include "../globals.h" | ||||
| #define MAX_OCTAVE_SIZE 128 | #define MAX_OCTAVE_SIZE 128 | ||||
| #define MICROTONAL_MAX_NAME_LEN 120 | #define MICROTONAL_MAX_NAME_LEN 120 | ||||
| class XMLwrapper; | class XMLwrapper; | ||||
| struct KbmInfo | |||||
| { | |||||
| uint8_t Pmapsize; | |||||
| uint8_t Pfirstkey; | |||||
| uint8_t Plastkey; | |||||
| uint8_t Pmiddlenote; | |||||
| uint8_t PAnote; | |||||
| float PAfreq; | |||||
| uint8_t Pmappingenabled; | |||||
| short int Pmapping[128]; | |||||
| }; | |||||
| struct OctaveTuning { | |||||
| unsigned char type; //1 for cents or 2 for division | |||||
| // the real tuning (eg. +1.05946f for one halftone) | |||||
| // or 2.0f for one octave | |||||
| float tuning; | |||||
| //the real tunning is x1/x2 | |||||
| unsigned int x1, x2; | |||||
| }; | |||||
| struct SclInfo | |||||
| { | |||||
| char Pname[MICROTONAL_MAX_NAME_LEN]; | |||||
| char Pcomment[MICROTONAL_MAX_NAME_LEN]; | |||||
| unsigned char octavesize; | |||||
| OctaveTuning octave[MAX_OCTAVE_SIZE]; | |||||
| }; | |||||
| /**Tuning settings and microtonal capabilities*/ | /**Tuning settings and microtonal capabilities*/ | ||||
| class Microtonal | class Microtonal | ||||
| @@ -88,9 +119,9 @@ class Microtonal | |||||
| /**Convert tunning to string*/ | /**Convert tunning to string*/ | ||||
| void tuningtoline(int n, char *line, int maxn); | void tuningtoline(int n, char *line, int maxn); | ||||
| /**load the tunnings from a .scl file*/ | /**load the tunnings from a .scl file*/ | ||||
| int loadscl(const char *filename); | |||||
| static int loadscl(SclInfo &scl, const char *filename); | |||||
| /**load the mapping from .kbm file*/ | /**load the mapping from .kbm file*/ | ||||
| int loadkbm(const char *filename); | |||||
| static int loadkbm(KbmInfo &kbm, const char *filename); | |||||
| /**Load text into the internal tunings | /**Load text into the internal tunings | ||||
| * | * | ||||
| *\todo better description*/ | *\todo better description*/ | ||||
| @@ -114,23 +145,19 @@ class Microtonal | |||||
| bool operator==(const Microtonal µ) const; | bool operator==(const Microtonal µ) const; | ||||
| bool operator!=(const Microtonal µ) const; | bool operator!=(const Microtonal µ) const; | ||||
| void clone(Microtonal &m); | |||||
| static const rtosc::Ports ports; | static const rtosc::Ports ports; | ||||
| //only paste handler should access there (quasi-private) | |||||
| unsigned char octavesize; | |||||
| OctaveTuning octave[MAX_OCTAVE_SIZE]; | |||||
| private: | private: | ||||
| int linetotunings(unsigned int nline, const char *line); | |||||
| //loads a line from the text file, while ignoring the lines beggining with "!" | //loads a line from the text file, while ignoring the lines beggining with "!" | ||||
| int loadline(FILE *file, char *line); | |||||
| static int loadline(FILE *file, char *line); | |||||
| //Grab a 0..127 integer from the provided descriptor | //Grab a 0..127 integer from the provided descriptor | ||||
| unsigned char octavesize; | |||||
| struct { | |||||
| unsigned char type; //1 for cents or 2 for division | |||||
| // the real tuning (eg. +1.05946f for one halftone) | |||||
| // or 2.0f for one octave | |||||
| float tuning; | |||||
| //the real tunning is x1/x2 | |||||
| unsigned int x1, x2; | |||||
| } octave[MAX_OCTAVE_SIZE], tmpoctave[MAX_OCTAVE_SIZE]; | |||||
| static int linetotunings(struct OctaveTuning &tune, const char *line); | |||||
| const int& gzip_compression; | const int& gzip_compression; | ||||
| }; | }; | ||||
| @@ -157,6 +157,12 @@ void deallocate(const char *str, void *v) | |||||
| delete (Master*)v; | delete (Master*)v; | ||||
| else if(!strcmp(str, "fft_t")) | else if(!strcmp(str, "fft_t")) | ||||
| delete[] (fft_t*)v; | delete[] (fft_t*)v; | ||||
| else if(!strcmp(str, "KbmInfo")) | |||||
| delete (KbmInfo*)v; | |||||
| else if(!strcmp(str, "SclInfo")) | |||||
| delete (SclInfo*)v; | |||||
| else if(!strcmp(str, "Microtonal")) | |||||
| delete (Microtonal*)v; | |||||
| else | else | ||||
| fprintf(stderr, "Unknown type '%s', leaking pointer %p!!\n", str, v); | fprintf(stderr, "Unknown type '%s', leaking pointer %p!!\n", str, v); | ||||
| } | } | ||||
| @@ -556,6 +562,48 @@ public: | |||||
| parent->transmitMsg("/load-master", "b", sizeof(Master*), &m); | parent->transmitMsg("/load-master", "b", sizeof(Master*), &m); | ||||
| } | } | ||||
| void loadXsz(const char *filename, rtosc::RtData &d) | |||||
| { | |||||
| Microtonal *micro = new Microtonal(master->gzip_compression); | |||||
| int err = micro->loadXML(filename); | |||||
| if(err) { | |||||
| d.reply("/alert", "s", "Error: Could not load the xsz file."); | |||||
| delete micro; | |||||
| } else | |||||
| d.chain("/microtonal/paste", "b", sizeof(void*), µ); | |||||
| } | |||||
| void saveXsz(const char *filename, rtosc::RtData &d) | |||||
| { | |||||
| int err = 0; | |||||
| doReadOnlyOp([this,filename,&err](){ | |||||
| err = master->microtonal.saveXML(filename);}); | |||||
| if(err) | |||||
| d.reply("/alert", "s", "Error: Could not save the xsz file."); | |||||
| } | |||||
| void loadScl(const char *filename, rtosc::RtData &d) | |||||
| { | |||||
| SclInfo *scl = new SclInfo; | |||||
| int err=Microtonal::loadscl(*scl, filename); | |||||
| if(err) { | |||||
| d.reply("/alert", "s", "Error: Could not load the scl file."); | |||||
| delete scl; | |||||
| } else | |||||
| d.chain("/microtonal/paste_scl", "b", sizeof(void*), &scl); | |||||
| } | |||||
| void loadKbm(const char *filename, rtosc::RtData &d) | |||||
| { | |||||
| KbmInfo *kbm = new KbmInfo; | |||||
| int err=Microtonal::loadkbm(*kbm, filename); | |||||
| if(err) { | |||||
| d.reply("/alert", "s", "Error: Could not load the kbm file."); | |||||
| delete kbm; | |||||
| } else | |||||
| d.chain("/microtonal/paste_kbm", "b", sizeof(void*), &kbm); | |||||
| } | |||||
| void updateResources(Master *m) | void updateResources(Master *m) | ||||
| { | { | ||||
| obj_store.clear(); | obj_store.clear(); | ||||
| @@ -861,6 +909,12 @@ rtosc::Ports bankPorts = { | |||||
| rBegin; | rBegin; | ||||
| impl.setLsb(rtosc_argument(msg, 0).i); | impl.setLsb(rtosc_argument(msg, 0).i); | ||||
| rEnd}, | rEnd}, | ||||
| {"newbank:s", 0, 0, | |||||
| rBegin; | |||||
| int err = impl.newbank(rtosc_argument(msg, 0).s); | |||||
| if(err) | |||||
| d.reply("/alert", "s", "Error: Could not make a new bank (directory).."); | |||||
| rEnd}, | |||||
| }; | }; | ||||
| /****************************************************************************** | /****************************************************************************** | ||||
| @@ -959,6 +1013,27 @@ static rtosc::Ports middwareSnoopPorts = { | |||||
| xml.loadXMLfile(file); | xml.loadXMLfile(file); | ||||
| loadMidiLearn(xml, impl.midi_mapper); | loadMidiLearn(xml, impl.midi_mapper); | ||||
| rEnd}, | rEnd}, | ||||
| //scale file stuff | |||||
| {"load_xsz:s", 0, 0, | |||||
| rBegin; | |||||
| const char *file = rtosc_argument(msg, 0).s; | |||||
| impl.loadXsz(file, d); | |||||
| rEnd}, | |||||
| {"save_xsz:s", 0, 0, | |||||
| rBegin; | |||||
| const char *file = rtosc_argument(msg, 0).s; | |||||
| impl.saveXsz(file, d); | |||||
| rEnd}, | |||||
| {"load_scl:s", 0, 0, | |||||
| rBegin; | |||||
| const char *file = rtosc_argument(msg, 0).s; | |||||
| impl.loadScl(file, d); | |||||
| rEnd}, | |||||
| {"load_kbm:s", 0, 0, | |||||
| rBegin; | |||||
| const char *file = rtosc_argument(msg, 0).s; | |||||
| impl.loadKbm(file, d); | |||||
| rEnd}, | |||||
| {"save_xmz:s", 0, 0, | {"save_xmz:s", 0, 0, | ||||
| rBegin; | rBegin; | ||||
| const char *file = rtosc_argument(msg, 0).s; | const char *file = rtosc_argument(msg, 0).s; | ||||
| @@ -312,7 +312,7 @@ void Part::defaultsinstrument() | |||||
| Pdrummode = 0; | Pdrummode = 0; | ||||
| for(int n = 0; n < NUM_KIT_ITEMS; ++n) { | for(int n = 0; n < NUM_KIT_ITEMS; ++n) { | ||||
| //kit[n].Penabled = false; | |||||
| kit[n].Penabled = false; | |||||
| kit[n].Pmuted = false; | kit[n].Pmuted = false; | ||||
| kit[n].Pminkey = 0; | kit[n].Pminkey = 0; | ||||
| kit[n].Pmaxkey = 127; | kit[n].Pmaxkey = 127; | ||||
| @@ -847,9 +847,6 @@ void Part::setkititemstatus(unsigned kititem, bool Penabled_) | |||||
| delete kkit.adpars; | delete kkit.adpars; | ||||
| delete kkit.subpars; | delete kkit.subpars; | ||||
| delete kkit.padpars; | delete kkit.padpars; | ||||
| kkit.adpars = nullptr; | |||||
| kkit.subpars = nullptr; | |||||
| kkit.padpars = nullptr; | |||||
| kkit.Pname[0] = '\0'; | kkit.Pname[0] = '\0'; | ||||
| notePool.killAllNotes(); | notePool.killAllNotes(); | ||||
| @@ -98,7 +98,7 @@ XMLwrapper::XMLwrapper() | |||||
| { | { | ||||
| version.Major = 2; | version.Major = 2; | ||||
| version.Minor = 5; | version.Minor = 5; | ||||
| version.Revision = 2; | |||||
| version.Revision = 3; | |||||
| minimal = true; | minimal = true; | ||||
| @@ -237,21 +237,21 @@ void FilterParams::getfromFilterParams(FilterParams *pars) | |||||
| /* | /* | ||||
| * Parameter control | * Parameter control | ||||
| */ | */ | ||||
| float FilterParams::getfreq() | |||||
| float FilterParams::getfreq() const | |||||
| { | { | ||||
| return (Pfreq / 64.0f - 1.0f) * 5.0f; | return (Pfreq / 64.0f - 1.0f) * 5.0f; | ||||
| } | } | ||||
| float FilterParams::getq() | |||||
| float FilterParams::getq() const | |||||
| { | { | ||||
| return expf(powf((float) Pq / 127.0f, 2) * logf(1000.0f)) - 0.9f; | return expf(powf((float) Pq / 127.0f, 2) * logf(1000.0f)) - 0.9f; | ||||
| } | } | ||||
| float FilterParams::getfreqtracking(float notefreq) | |||||
| float FilterParams::getfreqtracking(float notefreq) const | |||||
| { | { | ||||
| return logf(notefreq / 440.0f) * (Pfreqtrack - 64.0f) / (64.0f * LOG_2); | return logf(notefreq / 440.0f) * (Pfreqtrack - 64.0f) / (64.0f * LOG_2); | ||||
| } | } | ||||
| float FilterParams::getgain() | |||||
| float FilterParams::getgain() const | |||||
| { | { | ||||
| return (Pgain / 64.0f - 1.0f) * 30.0f; //-30..30dB | return (Pgain / 64.0f - 1.0f) * 30.0f; //-30..30dB | ||||
| } | } | ||||
| @@ -259,7 +259,7 @@ float FilterParams::getgain() | |||||
| /* | /* | ||||
| * Get the center frequency of the formant's graph | * Get the center frequency of the formant's graph | ||||
| */ | */ | ||||
| float FilterParams::getcenterfreq() | |||||
| float FilterParams::getcenterfreq() const | |||||
| { | { | ||||
| return 10000.0f * powf(10, -(1.0f - Pcenterfreq / 127.0f) * 2.0f); | return 10000.0f * powf(10, -(1.0f - Pcenterfreq / 127.0f) * 2.0f); | ||||
| } | } | ||||
| @@ -267,7 +267,7 @@ float FilterParams::getcenterfreq() | |||||
| /* | /* | ||||
| * Get the number of octave that the formant functions applies to | * Get the number of octave that the formant functions applies to | ||||
| */ | */ | ||||
| float FilterParams::getoctavesfreq() | |||||
| float FilterParams::getoctavesfreq() const | |||||
| { | { | ||||
| return 0.25f + 10.0f * Poctavesfreq / 127.0f; | return 0.25f + 10.0f * Poctavesfreq / 127.0f; | ||||
| } | } | ||||
| @@ -275,7 +275,7 @@ float FilterParams::getoctavesfreq() | |||||
| /* | /* | ||||
| * Get the frequency from x, where x is [0..1] | * Get the frequency from x, where x is [0..1] | ||||
| */ | */ | ||||
| float FilterParams::getfreqx(float x) | |||||
| float FilterParams::getfreqx(float x) const | |||||
| { | { | ||||
| if(x > 1.0f) | if(x > 1.0f) | ||||
| x = 1.0f; | x = 1.0f; | ||||
| @@ -286,7 +286,7 @@ float FilterParams::getfreqx(float x) | |||||
| /* | /* | ||||
| * Get the x coordinate from frequency (used by the UI) | * Get the x coordinate from frequency (used by the UI) | ||||
| */ | */ | ||||
| float FilterParams::getfreqpos(float freq) | |||||
| float FilterParams::getfreqpos(float freq) const | |||||
| { | { | ||||
| return (logf(freq) - logf(getfreqx(0.0f))) / logf(2.0f) / getoctavesfreq(); | return (logf(freq) - logf(getfreqx(0.0f))) / logf(2.0f) / getoctavesfreq(); | ||||
| } | } | ||||
| @@ -294,19 +294,19 @@ float FilterParams::getfreqpos(float freq) | |||||
| /* | /* | ||||
| * Transforms a parameter to the real value | * Transforms a parameter to the real value | ||||
| */ | */ | ||||
| float FilterParams::getformantfreq(unsigned char freq) | |||||
| float FilterParams::getformantfreq(unsigned char freq) const | |||||
| { | { | ||||
| float result = getfreqx(freq / 127.0f); | float result = getfreqx(freq / 127.0f); | ||||
| return result; | return result; | ||||
| } | } | ||||
| float FilterParams::getformantamp(unsigned char amp) | |||||
| float FilterParams::getformantamp(unsigned char amp) const | |||||
| { | { | ||||
| float result = powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); | float result = powf(0.1f, (1.0f - amp / 127.0f) * 4.0f); | ||||
| return result; | return result; | ||||
| } | } | ||||
| float FilterParams::getformantq(unsigned char q) | |||||
| float FilterParams::getformantq(unsigned char q) const | |||||
| { | { | ||||
| //temp | //temp | ||||
| float result = powf(25.0f, (q - 32.0f) / 64.0f); | float result = powf(25.0f, (q - 32.0f) / 64.0f); | ||||
| @@ -48,10 +48,10 @@ class FilterParams:public PresetsArray | |||||
| void getfromFilterParams(FilterParams *pars); | void getfromFilterParams(FilterParams *pars); | ||||
| float getfreq(); | |||||
| float getq(); | |||||
| float getfreqtracking(float notefreq); | |||||
| float getgain(); | |||||
| float getfreq() const ; | |||||
| float getq() const ; | |||||
| float getfreqtracking(float notefreq) const ; | |||||
| float getgain() const ; | |||||
| unsigned char Pcategory; //Filter category (Analog/Formant/StVar) | unsigned char Pcategory; //Filter category (Analog/Formant/StVar) | ||||
| unsigned char Ptype; // Filter type (for analog lpf,hpf,bpf..) | unsigned char Ptype; // Filter type (for analog lpf,hpf,bpf..) | ||||
| @@ -80,14 +80,14 @@ class FilterParams:public PresetsArray | |||||
| unsigned char nvowel; //the vowel from the position | unsigned char nvowel; //the vowel from the position | ||||
| } Psequence[FF_MAX_SEQUENCE]; | } Psequence[FF_MAX_SEQUENCE]; | ||||
| float getcenterfreq(); | |||||
| float getoctavesfreq(); | |||||
| float getfreqpos(float freq); | |||||
| float getfreqx(float x); | |||||
| float getcenterfreq() const ; | |||||
| float getoctavesfreq() const ; | |||||
| float getfreqpos(float freq) const ; | |||||
| float getfreqx(float x) const ; | |||||
| float getformantfreq(unsigned char freq); | |||||
| float getformantamp(unsigned char amp); | |||||
| float getformantq(unsigned char q); | |||||
| float getformantfreq(unsigned char freq) const ; | |||||
| float getformantamp(unsigned char amp) const ; | |||||
| float getformantq(unsigned char q) const ; | |||||
| void defaults(int n); | void defaults(int n); | ||||
| @@ -29,9 +29,8 @@ | |||||
| #include "../globals.h" | #include "../globals.h" | ||||
| #include "../Misc/Util.h" | #include "../Misc/Util.h" | ||||
| #include "../Misc/Allocator.h" | #include "../Misc/Allocator.h" | ||||
| #include "../DSP/Filter.h" | |||||
| #include "../Params/ADnoteParameters.h" | #include "../Params/ADnoteParameters.h" | ||||
| #include "../Params/FilterParams.h" | |||||
| #include "ModFilter.h" | |||||
| #include "OscilGen.h" | #include "OscilGen.h" | ||||
| #include "ADnote.h" | #include "ADnote.h" | ||||
| @@ -63,13 +62,6 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||||
| NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; | NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; | ||||
| NoteGlobalPar.FilterCenterPitch = pars.GlobalPar.GlobalFilter->getfreq() //center freq | |||||
| + pars.GlobalPar.PFilterVelocityScale | |||||
| / 127.0f * 6.0f //velocity sensing | |||||
| * (VelF(velocity, | |||||
| pars.GlobalPar. | |||||
| PFilterVelocityScaleFunction) - 1); | |||||
| NoteGlobalPar.Fadein_adjustment = | NoteGlobalPar.Fadein_adjustment = | ||||
| pars.GlobalPar.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | pars.GlobalPar.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | ||||
| NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment; | NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment; | ||||
| @@ -373,17 +365,10 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||||
| NoteVoicePar[nvoice].AmpLfo = NULL; | NoteVoicePar[nvoice].AmpLfo = NULL; | ||||
| NoteVoicePar[nvoice].AmpEnvelope = NULL; | NoteVoicePar[nvoice].AmpEnvelope = NULL; | ||||
| NoteVoicePar[nvoice].VoiceFilterL = NULL; | |||||
| NoteVoicePar[nvoice].VoiceFilterR = NULL; | |||||
| NoteVoicePar[nvoice].Filter = NULL; | |||||
| NoteVoicePar[nvoice].FilterEnvelope = NULL; | NoteVoicePar[nvoice].FilterEnvelope = NULL; | ||||
| NoteVoicePar[nvoice].FilterLfo = NULL; | NoteVoicePar[nvoice].FilterLfo = NULL; | ||||
| NoteVoicePar[nvoice].FilterCenterPitch = | |||||
| pars.VoicePar[nvoice].VoiceFilter->getfreq() | |||||
| + pars.VoicePar[nvoice].PFilterVelocityScale | |||||
| / 127.0f * 6.0f //velocity sensing | |||||
| * (VelF(velocity, | |||||
| pars.VoicePar[nvoice].PFilterVelocityScaleFunction) - 1); | |||||
| NoteVoicePar[nvoice].filterbypass = | NoteVoicePar[nvoice].filterbypass = | ||||
| pars.VoicePar[nvoice].Pfilterbypass; | pars.VoicePar[nvoice].Pfilterbypass; | ||||
| @@ -514,13 +499,9 @@ void ADnote::legatonote(LegatoParams lpars) | |||||
| else | else | ||||
| NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; | NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f; | ||||
| //center freq | |||||
| NoteGlobalPar.FilterCenterPitch = pars.GlobalPar.GlobalFilter->getfreq() | |||||
| + pars.GlobalPar.PFilterVelocityScale | |||||
| / 127.0f * 6.0f //velocity sensing | |||||
| * (VelF(velocity, | |||||
| pars.GlobalPar. | |||||
| PFilterVelocityScaleFunction) - 1); | |||||
| NoteGlobalPar.Filter->updateSense(velocity, | |||||
| pars.GlobalPar.PFilterVelocityScale, | |||||
| pars.GlobalPar.PFilterVelocityScaleFunction); | |||||
| for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { | for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) { | ||||
| @@ -580,13 +561,12 @@ void ADnote::legatonote(LegatoParams lpars) | |||||
| + i] = | + i] = | ||||
| NoteVoicePar[nvoice].OscilSmp[i]; | NoteVoicePar[nvoice].OscilSmp[i]; | ||||
| NoteVoicePar[nvoice].FilterCenterPitch = | |||||
| pars.VoicePar[nvoice].VoiceFilter->getfreq() | |||||
| + pars.VoicePar[nvoice].PFilterVelocityScale | |||||
| / 127.0f * 6.0f //velocity sensing | |||||
| * (VelF(velocity, | |||||
| pars.VoicePar[nvoice].PFilterVelocityScaleFunction) - 1); | |||||
| auto &voiceFilter = NoteVoicePar[nvoice].Filter; | |||||
| if(voiceFilter) { | |||||
| const auto &vce = pars.VoicePar[nvoice]; | |||||
| voiceFilter->updateSense(velocity, vce.PFilterVelocityScale, | |||||
| vce.PFilterVelocityScaleFunction); | |||||
| } | |||||
| NoteVoicePar[nvoice].filterbypass = | NoteVoicePar[nvoice].filterbypass = | ||||
| pars.VoicePar[nvoice].Pfilterbypass; | pars.VoicePar[nvoice].Pfilterbypass; | ||||
| @@ -653,9 +633,12 @@ void ADnote::legatonote(LegatoParams lpars) | |||||
| * NoteGlobalPar.AmpEnvelope->envout_dB() | * NoteGlobalPar.AmpEnvelope->envout_dB() | ||||
| * NoteGlobalPar.AmpLfo->amplfoout(); | * NoteGlobalPar.AmpLfo->amplfoout(); | ||||
| NoteGlobalPar.FilterQ = pars.GlobalPar.GlobalFilter->getq(); | |||||
| NoteGlobalPar.FilterFreqTracking = | |||||
| pars.GlobalPar.GlobalFilter->getfreqtracking(basefreq); | |||||
| { | |||||
| auto *filter = NoteGlobalPar.Filter; | |||||
| filter->updateSense(velocity, pars.GlobalPar.PFilterVelocityScale, | |||||
| pars.GlobalPar.PFilterVelocityScaleFunction); | |||||
| filter->updateNoteFreq(basefreq); | |||||
| } | |||||
| // Forbids the Modulation Voice to be greater or equal than voice | // Forbids the Modulation Voice to be greater or equal than voice | ||||
| for(int i = 0; i < NUM_VOICES; ++i) | for(int i = 0; i < NUM_VOICES; ++i) | ||||
| @@ -693,10 +676,12 @@ void ADnote::legatonote(LegatoParams lpars) | |||||
| if(pars.VoicePar[nvoice].PAmpLfoEnabled && NoteVoicePar[nvoice].AmpLfo) | if(pars.VoicePar[nvoice].PAmpLfoEnabled && NoteVoicePar[nvoice].AmpLfo) | ||||
| newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); | newamplitude[nvoice] *= NoteVoicePar[nvoice].AmpLfo->amplfoout(); | ||||
| NoteVoicePar[nvoice].FilterFreqTracking = | |||||
| pars.VoicePar[nvoice].VoiceFilter->getfreqtracking(basefreq); | |||||
| auto *voiceFilter = NoteVoicePar[nvoice].Filter; | |||||
| if(voiceFilter) { | |||||
| voiceFilter->updateSense(velocity, pars.VoicePar[nvoice].PFilterVelocityScale, | |||||
| pars.VoicePar[nvoice].PFilterVelocityScaleFunction); | |||||
| voiceFilter->updateNoteFreq(basefreq); | |||||
| } | |||||
| /* Voice Modulation Parameters Init */ | /* Voice Modulation Parameters Init */ | ||||
| if((NoteVoicePar[nvoice].FMEnabled != NONE) | if((NoteVoicePar[nvoice].FMEnabled != NONE) | ||||
| @@ -857,21 +842,24 @@ void ADnote::initparameters() | |||||
| vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | ||||
| /* Voice Filter Parameters Init */ | /* Voice Filter Parameters Init */ | ||||
| if(param.PFilterEnabled != 0) { | |||||
| vce.VoiceFilterL = Filter::generate(memory, param.VoiceFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| vce.VoiceFilterR = Filter::generate(memory, param.VoiceFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| } | |||||
| if(param.PFilterEnabled) { | |||||
| vce.Filter = memory.alloc<ModFilter>(*param.VoiceFilter, synth, time, memory, stereo, | |||||
| basefreq); | |||||
| vce.Filter->updateSense(velocity, param.PFilterVelocityScale, | |||||
| param.PFilterVelocityScaleFunction); | |||||
| if(param.PFilterEnvelopeEnabled) | |||||
| vce.FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||||
| if(param.PFilterLfoEnabled) | |||||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||||
| if(param.PFilterEnvelopeEnabled) { | |||||
| vce.FilterEnvelope = | |||||
| memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | |||||
| vce.Filter->addMod(*vce.FilterEnvelope); | |||||
| } | |||||
| vce.FilterFreqTracking = | |||||
| param.VoiceFilter->getfreqtracking(basefreq); | |||||
| if(param.PFilterLfoEnabled) { | |||||
| vce.FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | |||||
| vce.Filter->addMod(*vce.FilterLfo); | |||||
| } | |||||
| } | |||||
| /* Voice Modulation Parameters Init */ | /* Voice Modulation Parameters Init */ | ||||
| if((vce.FMEnabled != NONE) && (vce.FMVoice < 0)) { | if((vce.FMEnabled != NONE) && (vce.FMVoice < 0)) { | ||||
| @@ -1050,8 +1038,8 @@ float ADnote::getFMvoicebasefreq(int nvoice) const | |||||
| void ADnote::computecurrentparameters() | void ADnote::computecurrentparameters() | ||||
| { | { | ||||
| int nvoice; | int nvoice; | ||||
| float voicefreq, voicepitch, filterpitch, filterfreq, FMfreq, | |||||
| FMrelativepitch, globalpitch, globalfilterpitch; | |||||
| float voicefreq, voicepitch, FMfreq, | |||||
| FMrelativepitch, globalpitch; | |||||
| globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | ||||
| + NoteGlobalPar.FreqLfo->lfoout() | + NoteGlobalPar.FreqLfo->lfoout() | ||||
| * ctl.modwheel.relmod); | * ctl.modwheel.relmod); | ||||
| @@ -1060,19 +1048,9 @@ void ADnote::computecurrentparameters() | |||||
| * NoteGlobalPar.AmpEnvelope->envout_dB() | * NoteGlobalPar.AmpEnvelope->envout_dB() | ||||
| * NoteGlobalPar.AmpLfo->amplfoout(); | * NoteGlobalPar.AmpLfo->amplfoout(); | ||||
| globalfilterpitch = NoteGlobalPar.FilterEnvelope->envout() | |||||
| + NoteGlobalPar.FilterLfo->lfoout() | |||||
| + NoteGlobalPar.FilterCenterPitch; | |||||
| float tmpfilterfreq = globalfilterpitch + ctl.filtercutoff.relfreq | |||||
| + NoteGlobalPar.FilterFreqTracking; | |||||
| tmpfilterfreq = Filter::getrealfreq(tmpfilterfreq); | |||||
| NoteGlobalPar.Filter->update(ctl.filtercutoff.relfreq, | |||||
| ctl.filterq.relq); | |||||
| float globalfilterq = NoteGlobalPar.FilterQ * ctl.filterq.relq; | |||||
| NoteGlobalPar.GlobalFilterL->setfreq_and_q(tmpfilterfreq, globalfilterq); | |||||
| if(stereo != 0) | |||||
| NoteGlobalPar.GlobalFilterR->setfreq_and_q(tmpfilterfreq, globalfilterq); | |||||
| //compute the portamento, if it is used by this note | //compute the portamento, if it is used by this note | ||||
| float portamentofreqrap = 1.0f; | float portamentofreqrap = 1.0f; | ||||
| @@ -1107,21 +1085,10 @@ void ADnote::computecurrentparameters() | |||||
| /****************/ | /****************/ | ||||
| /* Voice Filter */ | /* Voice Filter */ | ||||
| /****************/ | /****************/ | ||||
| if(NoteVoicePar[nvoice].VoiceFilterL) { | |||||
| filterpitch = NoteVoicePar[nvoice].FilterCenterPitch; | |||||
| if(NoteVoicePar[nvoice].FilterEnvelope) | |||||
| filterpitch += NoteVoicePar[nvoice].FilterEnvelope->envout(); | |||||
| if(NoteVoicePar[nvoice].FilterLfo) | |||||
| filterpitch += NoteVoicePar[nvoice].FilterLfo->lfoout(); | |||||
| filterfreq = filterpitch + NoteVoicePar[nvoice].FilterFreqTracking; | |||||
| filterfreq = Filter::getrealfreq(filterfreq); | |||||
| NoteVoicePar[nvoice].VoiceFilterL->setfreq(filterfreq); | |||||
| if(stereo && NoteVoicePar[nvoice].VoiceFilterR) | |||||
| NoteVoicePar[nvoice].VoiceFilterR->setfreq(filterfreq); | |||||
| auto *voiceFilter = NoteVoicePar[nvoice].Filter; | |||||
| if(voiceFilter) { | |||||
| voiceFilter->update(ctl.filtercutoff.relfreq, | |||||
| ctl.filterq.relq); | |||||
| } | } | ||||
| if(NoteVoicePar[nvoice].noisetype == 0) { //compute only if the voice isn't noise | if(NoteVoicePar[nvoice].noisetype == 0) { //compute only if the voice isn't noise | ||||
| @@ -1702,12 +1669,13 @@ int ADnote::noteout(float *outl, float *outr) | |||||
| firsttick[nvoice] = 0; | firsttick[nvoice] = 0; | ||||
| } | } | ||||
| // Filter | // Filter | ||||
| if(NoteVoicePar[nvoice].VoiceFilterL) | |||||
| NoteVoicePar[nvoice].VoiceFilterL->filterout(&tmpwavel[0]); | |||||
| if(stereo && NoteVoicePar[nvoice].VoiceFilterR) | |||||
| NoteVoicePar[nvoice].VoiceFilterR->filterout(&tmpwaver[0]); | |||||
| if(NoteVoicePar[nvoice].Filter) { | |||||
| if(stereo) | |||||
| NoteVoicePar[nvoice].Filter->filter(tmpwavel, tmpwaver); | |||||
| else | |||||
| NoteVoicePar[nvoice].Filter->filter(tmpwavel, 0); | |||||
| } | |||||
| //check if the amplitude envelope is finished, if yes, the voice will be fadeout | //check if the amplitude envelope is finished, if yes, the voice will be fadeout | ||||
| if(NoteVoicePar[nvoice].AmpEnvelope) | if(NoteVoicePar[nvoice].AmpEnvelope) | ||||
| @@ -1767,14 +1735,13 @@ int ADnote::noteout(float *outl, float *outr) | |||||
| //Processing Global parameters | //Processing Global parameters | ||||
| NoteGlobalPar.GlobalFilterL->filterout(&outl[0]); | |||||
| if(stereo == 0) { //set the right channel=left channel | |||||
| if(stereo) { | |||||
| NoteGlobalPar.Filter->filter(outl, outr); | |||||
| } else { //set the right channel=left channel | |||||
| NoteGlobalPar.Filter->filter(outl, 0); | |||||
| memcpy(outr, outl, synth.bufferbytes); | memcpy(outr, outl, synth.bufferbytes); | ||||
| memcpy(bypassr, bypassl, synth.bufferbytes); | memcpy(bypassr, bypassl, synth.bufferbytes); | ||||
| } | } | ||||
| else | |||||
| NoteGlobalPar.GlobalFilterR->filterout(&outr[0]); | |||||
| for(int i = 0; i < synth.buffersize; ++i) { | for(int i = 0; i < synth.buffersize; ++i) { | ||||
| outl[i] += bypassl[i]; | outl[i] += bypassl[i]; | ||||
| @@ -1881,8 +1848,7 @@ void ADnote::Voice::kill(Allocator &memory, const SYNTH_T &synth) | |||||
| memory.dealloc(FreqLfo); | memory.dealloc(FreqLfo); | ||||
| memory.dealloc(AmpEnvelope); | memory.dealloc(AmpEnvelope); | ||||
| memory.dealloc(AmpLfo); | memory.dealloc(AmpLfo); | ||||
| memory.dealloc(VoiceFilterL); | |||||
| memory.dealloc(VoiceFilterR); | |||||
| memory.dealloc(Filter); | |||||
| memory.dealloc(FilterEnvelope); | memory.dealloc(FilterEnvelope); | ||||
| memory.dealloc(FilterLfo); | memory.dealloc(FilterLfo); | ||||
| memory.dealloc(FMFreqEnvelope); | memory.dealloc(FMFreqEnvelope); | ||||
| @@ -1905,8 +1871,7 @@ void ADnote::Global::kill(Allocator &memory) | |||||
| memory.dealloc(FreqLfo); | memory.dealloc(FreqLfo); | ||||
| memory.dealloc(AmpEnvelope); | memory.dealloc(AmpEnvelope); | ||||
| memory.dealloc(AmpLfo); | memory.dealloc(AmpLfo); | ||||
| memory.dealloc(GlobalFilterL); | |||||
| memory.dealloc(GlobalFilterR); | |||||
| memory.dealloc(Filter); | |||||
| memory.dealloc(FilterEnvelope); | memory.dealloc(FilterEnvelope); | ||||
| memory.dealloc(FilterLfo); | memory.dealloc(FilterLfo); | ||||
| } | } | ||||
| @@ -1927,16 +1892,17 @@ void ADnote::Global::initparameters(const ADnoteGlobalParam ¶m, | |||||
| Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB | Volume = 4.0f * powf(0.1f, 3.0f * (1.0f - param.PVolume / 96.0f)) //-60 dB .. 0 dB | ||||
| * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing | * VelF(velocity, param.PAmpVelocityScaleFunction); //sensing | ||||
| GlobalFilterL = Filter::generate(memory, param.GlobalFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| if(stereo) | |||||
| GlobalFilterR = Filter::generate(memory, param.GlobalFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| else | |||||
| GlobalFilterR = NULL; | |||||
| Filter = memory.alloc<ModFilter>(*param.GlobalFilter, synth, time, memory, | |||||
| stereo, basefreq); | |||||
| FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | FilterEnvelope = memory.alloc<Envelope>(*param.FilterEnvelope, basefreq, synth.dt()); | ||||
| FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | FilterLfo = memory.alloc<LFO>(*param.FilterLfo, basefreq, time); | ||||
| FilterQ = param.GlobalFilter->getq(); | |||||
| FilterFreqTracking = param.GlobalFilter->getfreqtracking(basefreq); | |||||
| Filter->addMod(*FilterEnvelope); | |||||
| Filter->addMod(*FilterLfo); | |||||
| { | |||||
| Filter->updateSense(velocity, param.PFilterVelocityScale, | |||||
| param.PFilterVelocityScaleFunction); | |||||
| } | |||||
| } | } | ||||
| @@ -154,15 +154,9 @@ class ADnote:public SynthNote | |||||
| /****************************************** | /****************************************** | ||||
| * FILTER GLOBAL PARAMETERS * | * FILTER GLOBAL PARAMETERS * | ||||
| ******************************************/ | ******************************************/ | ||||
| class Filter * GlobalFilterL, *GlobalFilterR; | |||||
| float FilterCenterPitch; //octaves | |||||
| float FilterQ; | |||||
| float FilterFreqTracking; | |||||
| Envelope *FilterEnvelope; | |||||
| LFO *FilterLfo; | |||||
| ModFilter *Filter; | |||||
| Envelope *FilterEnvelope; | |||||
| LFO *FilterLfo; | |||||
| } NoteGlobalPar; | } NoteGlobalPar; | ||||
| @@ -226,15 +220,9 @@ class ADnote:public SynthNote | |||||
| /************************* | /************************* | ||||
| * FILTER PARAMETERS * | * FILTER PARAMETERS * | ||||
| *************************/ | *************************/ | ||||
| class Filter * VoiceFilterL; | |||||
| class Filter * VoiceFilterR; | |||||
| float FilterCenterPitch; /* Filter center Pitch*/ | |||||
| float FilterFreqTracking; | |||||
| Envelope *FilterEnvelope; | |||||
| LFO *FilterLfo; | |||||
| ModFilter *Filter; | |||||
| Envelope *FilterEnvelope; | |||||
| LFO *FilterLfo; | |||||
| /**************************** | /**************************** | ||||
| @@ -0,0 +1,149 @@ | |||||
| #include "ModFilter.h" | |||||
| #include "Envelope.h" | |||||
| #include "LFO.h" | |||||
| #include "../Misc/Util.h" | |||||
| #include "../Misc/Allocator.h" | |||||
| #include "../Params/FilterParams.h" | |||||
| #include "../DSP/Filter.h" | |||||
| #include "../DSP/SVFilter.h" | |||||
| #include "../DSP/AnalogFilter.h" | |||||
| #include "../DSP/FormantFilter.h" | |||||
| #include <cassert> | |||||
| ModFilter::ModFilter(const FilterParams &pars_, | |||||
| const SYNTH_T &synth_, | |||||
| const AbsTime &time_, | |||||
| Allocator &alloc_, | |||||
| bool stereo, | |||||
| float notefreq) | |||||
| :pars(pars_), synth(synth_), time(time_), alloc(alloc_), | |||||
| baseQ(pars.getq()), baseFreq(pars.getfreq()), | |||||
| noteFreq(notefreq), | |||||
| left(nullptr), | |||||
| right(nullptr), | |||||
| env(nullptr), | |||||
| lfo(nullptr) | |||||
| { | |||||
| tracking = pars.getfreqtracking(notefreq); | |||||
| baseQ = pars.getq(); | |||||
| baseFreq = pars.getfreq(); | |||||
| left = Filter::generate(alloc, &pars, | |||||
| synth.samplerate, synth.buffersize); | |||||
| if(stereo) | |||||
| right = Filter::generate(alloc, &pars, | |||||
| synth.samplerate, synth.buffersize); | |||||
| } | |||||
| ModFilter::~ModFilter(void) | |||||
| { | |||||
| alloc.dealloc(left); | |||||
| alloc.dealloc(right); | |||||
| } | |||||
| void ModFilter::addMod(LFO &lfo_) | |||||
| { | |||||
| lfo = &lfo_; | |||||
| } | |||||
| void ModFilter::addMod(Envelope &env_) | |||||
| { | |||||
| env = &env_; | |||||
| } | |||||
| //Recompute Filter Parameters | |||||
| void ModFilter::update(float relfreq, float relq) | |||||
| { | |||||
| if(pars.last_update_timestamp == time.time()) { | |||||
| paramUpdate(left); | |||||
| if(right) | |||||
| paramUpdate(right); | |||||
| baseFreq = pars.getfreq(); | |||||
| baseQ = pars.getq(); | |||||
| tracking = pars.getfreqtracking(noteFreq); | |||||
| } | |||||
| //Controller Free Center Frequency | |||||
| const float Fc = baseFreq | |||||
| + sense | |||||
| + (env ? env->envout() : 0) | |||||
| + (lfo ? lfo->lfoout() : 0); | |||||
| const float Fc_mod = Fc + relfreq + tracking; | |||||
| //Convert into Hz | |||||
| const float Fc_Hz = Filter::getrealfreq(Fc_mod); | |||||
| const float q = baseQ * relq; | |||||
| left->setfreq_and_q(Fc_Hz, q); | |||||
| if(right) | |||||
| right->setfreq_and_q(Fc_Hz, q); | |||||
| } | |||||
| void ModFilter::updateNoteFreq(float noteFreq_) | |||||
| { | |||||
| noteFreq = noteFreq_; | |||||
| tracking = pars.getfreqtracking(noteFreq); | |||||
| } | |||||
| void ModFilter::updateSense(float velocity, uint8_t scale, | |||||
| uint8_t func) | |||||
| { | |||||
| const float velScale = scale / 127.0f; | |||||
| sense = velScale * 6.0f * (VelF(velocity, func) - 1); | |||||
| } | |||||
| void ModFilter::filter(float *l, float *r) | |||||
| { | |||||
| if(left && l) | |||||
| left->filterout(l); | |||||
| if(right && r) | |||||
| right->filterout(r); | |||||
| } | |||||
| static int current_category(Filter *f) | |||||
| { | |||||
| if(dynamic_cast<AnalogFilter*>(f)) | |||||
| return 0; | |||||
| else if(dynamic_cast<FormantFilter*>(f)) | |||||
| return 1; | |||||
| else if(dynamic_cast<SVFilter*>(f)) | |||||
| return 2; | |||||
| assert(false); | |||||
| } | |||||
| void ModFilter::paramUpdate(Filter *&f) | |||||
| { | |||||
| //Common parameters | |||||
| baseQ = pars.getq(); | |||||
| baseFreq = pars.getfreq(); | |||||
| if(current_category(f) != pars.Pcategory) { | |||||
| alloc.dealloc(f); | |||||
| f = Filter::generate(alloc, &pars, | |||||
| synth.samplerate, synth.buffersize); | |||||
| return; | |||||
| } | |||||
| if(auto *sv = dynamic_cast<SVFilter*>(f)) | |||||
| svParamUpdate(*sv); | |||||
| else if(auto *an = dynamic_cast<AnalogFilter*>(f)) | |||||
| anParamUpdate(*an); | |||||
| } | |||||
| void ModFilter::svParamUpdate(SVFilter &sv) | |||||
| { | |||||
| sv.settype(pars.Ptype); | |||||
| sv.setstages(pars.Pstages); | |||||
| } | |||||
| void ModFilter::anParamUpdate(AnalogFilter &an) | |||||
| { | |||||
| an.settype(pars.Ptype); | |||||
| an.setstages(pars.Pstages); | |||||
| an.setgain(pars.getgain()); | |||||
| } | |||||
| @@ -0,0 +1,55 @@ | |||||
| #pragma once | |||||
| #include "../globals.h" | |||||
| #include "../Misc/Time.h" | |||||
| //Modulated instance of one of the filters in src/DSP/ | |||||
| //Supports stereo modes | |||||
| class ModFilter | |||||
| { | |||||
| public: | |||||
| ModFilter(const FilterParams &pars, | |||||
| const SYNTH_T &synth, | |||||
| const AbsTime &time, | |||||
| Allocator &alloc, | |||||
| bool stereo, | |||||
| float notefreq_); | |||||
| ~ModFilter(void); | |||||
| void addMod(LFO &lfo); | |||||
| void addMod(Envelope &env); | |||||
| //normal per tick update | |||||
| void update(float relfreq, float relq); | |||||
| //updates typically seen in note-init | |||||
| void updateNoteFreq(float noteFreq_); | |||||
| void updateSense(float velocity, | |||||
| uint8_t scale, uint8_t func); | |||||
| //filter stereo/mono signal(s) in-place | |||||
| void filter(float *l, float *r); | |||||
| private: | |||||
| void paramUpdate(Filter *&f); | |||||
| void svParamUpdate(SVFilter &sv); | |||||
| void anParamUpdate(AnalogFilter &an); | |||||
| const FilterParams &pars; //Parameters to Pull Updates From | |||||
| const SYNTH_T &synth; //Synthesizer Buffer Parameters | |||||
| const AbsTime &time; //Time for RT Updates | |||||
| Allocator &alloc; //RT Memory Pool | |||||
| float baseQ; //filter sharpness | |||||
| float baseFreq; //base filter frequency | |||||
| float noteFreq; //frequency note was initialized to | |||||
| float tracking; //shift due to note frequency | |||||
| float sense; //shift due to note velocity | |||||
| Filter *left; //left channel filter | |||||
| Filter *right;//right channel filter | |||||
| Envelope *env; //center freq envelope | |||||
| LFO *lfo; //center freq lfo | |||||
| }; | |||||
| @@ -18,11 +18,12 @@ | |||||
| along with this program; if not, write to the Free Software Foundation, | along with this program; if not, write to the Free Software Foundation, | ||||
| Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||||
| */ | */ | ||||
| #include <cassert> | |||||
| #include <cmath> | #include <cmath> | ||||
| #include "PADnote.h" | #include "PADnote.h" | ||||
| #include "ModFilter.h" | |||||
| #include "../Misc/Config.h" | #include "../Misc/Config.h" | ||||
| #include "../Misc/Allocator.h" | #include "../Misc/Allocator.h" | ||||
| #include "../DSP/Filter.h" | |||||
| #include "../Params/PADnoteParameters.h" | #include "../Params/PADnoteParameters.h" | ||||
| #include "../Params/Controller.h" | #include "../Params/Controller.h" | ||||
| #include "../Params/FilterParams.h" | #include "../Params/FilterParams.h" | ||||
| @@ -32,6 +33,10 @@ PADnote::PADnote(const PADnoteParameters *parameters, | |||||
| SynthParams pars, const int& interpolation) | SynthParams pars, const int& interpolation) | ||||
| :SynthNote(pars), pars(*parameters), interpolation(interpolation) | :SynthNote(pars), pars(*parameters), interpolation(interpolation) | ||||
| { | { | ||||
| NoteGlobalPar.GlobalFilter = nullptr; | |||||
| NoteGlobalPar.FilterEnvelope = nullptr; | |||||
| NoteGlobalPar.FilterLfo = nullptr; | |||||
| firsttime = true; | firsttime = true; | ||||
| setup(pars.frequency, pars.velocity, pars.portamento, pars.note); | setup(pars.frequency, pars.velocity, pars.portamento, pars.note); | ||||
| } | } | ||||
| @@ -112,13 +117,6 @@ void PADnote::setup(float freq, | |||||
| else | else | ||||
| NoteGlobalPar.Panning = pars.PPanning / 128.0f; | NoteGlobalPar.Panning = pars.PPanning / 128.0f; | ||||
| NoteGlobalPar.FilterCenterPitch = pars.GlobalFilter->getfreq() //center freq | |||||
| + pars.PFilterVelocityScale / 127.0f | |||||
| * 6.0f //velocity sensing | |||||
| * (VelF(velocity, | |||||
| pars. | |||||
| PFilterVelocityScaleFunction) - 1); | |||||
| if(!legato) { | if(!legato) { | ||||
| NoteGlobalPar.Fadein_adjustment = | NoteGlobalPar.Fadein_adjustment = | ||||
| pars.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | pars.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | ||||
| @@ -157,17 +155,25 @@ void PADnote::setup(float freq, | |||||
| * NoteGlobalPar.AmpLfo->amplfoout(); | * NoteGlobalPar.AmpLfo->amplfoout(); | ||||
| if(!legato) { | if(!legato) { | ||||
| NoteGlobalPar.GlobalFilterL = Filter::generate(memory, pars.GlobalFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| NoteGlobalPar.GlobalFilterR = Filter::generate(memory, pars.GlobalFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| auto &flt = NoteGlobalPar.GlobalFilter; | |||||
| auto &env = NoteGlobalPar.FilterEnvelope; | |||||
| auto &lfo = NoteGlobalPar.FilterLfo; | |||||
| assert(flt == nullptr); | |||||
| flt = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, true, basefreq); | |||||
| //setup mod | |||||
| env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt()); | |||||
| lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time); | |||||
| flt->addMod(*env); | |||||
| flt->addMod(*lfo); | |||||
| } | |||||
| NoteGlobalPar.FilterEnvelope = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt()); | |||||
| NoteGlobalPar.FilterLfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time); | |||||
| { | |||||
| auto &flt = *NoteGlobalPar.GlobalFilter; | |||||
| flt.updateSense(velocity, pars.PFilterVelocityScale, | |||||
| pars.PFilterVelocityScaleFunction); | |||||
| flt.updateNoteFreq(basefreq); | |||||
| } | } | ||||
| NoteGlobalPar.FilterQ = pars.GlobalFilter->getq(); | |||||
| NoteGlobalPar.FilterFreqTracking = pars.GlobalFilter->getfreqtracking( | |||||
| basefreq); | |||||
| if(!pars.sample[nsample].smp) { | if(!pars.sample[nsample].smp) { | ||||
| finished_ = true; | finished_ = true; | ||||
| @@ -198,8 +204,7 @@ PADnote::~PADnote() | |||||
| memory.dealloc(NoteGlobalPar.FreqLfo); | memory.dealloc(NoteGlobalPar.FreqLfo); | ||||
| memory.dealloc(NoteGlobalPar.AmpEnvelope); | memory.dealloc(NoteGlobalPar.AmpEnvelope); | ||||
| memory.dealloc(NoteGlobalPar.AmpLfo); | memory.dealloc(NoteGlobalPar.AmpLfo); | ||||
| memory.dealloc(NoteGlobalPar.GlobalFilterL); | |||||
| memory.dealloc(NoteGlobalPar.GlobalFilterR); | |||||
| memory.dealloc(NoteGlobalPar.GlobalFilter); | |||||
| memory.dealloc(NoteGlobalPar.FilterEnvelope); | memory.dealloc(NoteGlobalPar.FilterEnvelope); | ||||
| memory.dealloc(NoteGlobalPar.FilterLfo); | memory.dealloc(NoteGlobalPar.FilterLfo); | ||||
| } | } | ||||
| @@ -230,8 +235,7 @@ inline void PADnote::fadein(float *smps) | |||||
| void PADnote::computecurrentparameters() | void PADnote::computecurrentparameters() | ||||
| { | { | ||||
| float globalpitch, globalfilterpitch; | |||||
| globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | |||||
| const float globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | |||||
| + NoteGlobalPar.FreqLfo->lfoout() | + NoteGlobalPar.FreqLfo->lfoout() | ||||
| * ctl.modwheel.relmod + NoteGlobalPar.Detune); | * ctl.modwheel.relmod + NoteGlobalPar.Detune); | ||||
| globaloldamplitude = globalnewamplitude; | globaloldamplitude = globalnewamplitude; | ||||
| @@ -239,18 +243,8 @@ void PADnote::computecurrentparameters() | |||||
| * NoteGlobalPar.AmpEnvelope->envout_dB() | * NoteGlobalPar.AmpEnvelope->envout_dB() | ||||
| * NoteGlobalPar.AmpLfo->amplfoout(); | * NoteGlobalPar.AmpLfo->amplfoout(); | ||||
| globalfilterpitch = NoteGlobalPar.FilterEnvelope->envout() | |||||
| + NoteGlobalPar.FilterLfo->lfoout() | |||||
| + NoteGlobalPar.FilterCenterPitch; | |||||
| float tmpfilterfreq = globalfilterpitch + ctl.filtercutoff.relfreq | |||||
| + NoteGlobalPar.FilterFreqTracking; | |||||
| tmpfilterfreq = Filter::getrealfreq(tmpfilterfreq); | |||||
| float globalfilterq = NoteGlobalPar.FilterQ * ctl.filterq.relq; | |||||
| NoteGlobalPar.GlobalFilterL->setfreq_and_q(tmpfilterfreq, globalfilterq); | |||||
| NoteGlobalPar.GlobalFilterR->setfreq_and_q(tmpfilterfreq, globalfilterq); | |||||
| NoteGlobalPar.GlobalFilter->update(ctl.filtercutoff.relfreq, | |||||
| ctl.filterq.relq); | |||||
| //compute the portamento, if it is used by this note | //compute the portamento, if it is used by this note | ||||
| float portamentofreqrap = 1.0f; | float portamentofreqrap = 1.0f; | ||||
| @@ -377,8 +371,7 @@ int PADnote::noteout(float *outl, float *outr) | |||||
| firsttime = false; | firsttime = false; | ||||
| } | } | ||||
| NoteGlobalPar.GlobalFilterL->filterout(outl); | |||||
| NoteGlobalPar.GlobalFilterR->filterout(outr); | |||||
| NoteGlobalPar.GlobalFilter->filter(outl, outr); | |||||
| //Apply the punch | //Apply the punch | ||||
| if(NoteGlobalPar.Punch.Enabled != 0) | if(NoteGlobalPar.Punch.Enabled != 0) | ||||
| @@ -98,15 +98,9 @@ class PADnote:public SynthNote | |||||
| /****************************************** | /****************************************** | ||||
| * FILTER GLOBAL PARAMETERS * | * FILTER GLOBAL PARAMETERS * | ||||
| ******************************************/ | ******************************************/ | ||||
| class Filter * GlobalFilterL, *GlobalFilterR; | |||||
| float FilterCenterPitch; //octaves | |||||
| float FilterQ; | |||||
| float FilterFreqTracking; | |||||
| Envelope *FilterEnvelope; | |||||
| LFO *FilterLfo; | |||||
| ModFilter *GlobalFilter; | |||||
| Envelope *FilterEnvelope; | |||||
| LFO *FilterLfo; | |||||
| } NoteGlobalPar; | } NoteGlobalPar; | ||||
| @@ -28,16 +28,24 @@ | |||||
| #include "../globals.h" | #include "../globals.h" | ||||
| #include "SUBnote.h" | #include "SUBnote.h" | ||||
| #include "Envelope.h" | #include "Envelope.h" | ||||
| #include "ModFilter.h" | |||||
| #include "../Params/Controller.h" | #include "../Params/Controller.h" | ||||
| #include "../Params/SUBnoteParameters.h" | #include "../Params/SUBnoteParameters.h" | ||||
| #include "../Params/FilterParams.h" | #include "../Params/FilterParams.h" | ||||
| #include "../Misc/Time.h" | |||||
| #include "../Misc/Util.h" | #include "../Misc/Util.h" | ||||
| #include "../Misc/Allocator.h" | #include "../Misc/Allocator.h" | ||||
| SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) | SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) | ||||
| :SynthNote(spars), pars(*parameters) | |||||
| :SynthNote(spars), pars(*parameters), | |||||
| AmpEnvelope(nullptr), | |||||
| FreqEnvelope(nullptr), | |||||
| BandWidthEnvelope(nullptr), | |||||
| GlobalFilter(nullptr), | |||||
| GlobalFilterEnvelope(nullptr), | |||||
| NoteEnabled(ON), | |||||
| lfilter(nullptr), rfilter(nullptr) | |||||
| { | { | ||||
| NoteEnabled = ON; | |||||
| setup(spars.frequency, spars.velocity, spars.portamento, spars.note); | setup(spars.frequency, spars.velocity, spars.portamento, spars.note); | ||||
| } | } | ||||
| @@ -56,12 +64,14 @@ void SUBnote::setup(float freq, | |||||
| panning = pars.PPanning / 127.0f; | panning = pars.PPanning / 127.0f; | ||||
| else | else | ||||
| panning = RND; | panning = RND; | ||||
| if(!legato) { | |||||
| if(!legato) { //normal note | |||||
| numstages = pars.Pnumstages; | numstages = pars.Pnumstages; | ||||
| stereo = pars.Pstereo; | stereo = pars.Pstereo; | ||||
| start = pars.Pstart; | start = pars.Pstart; | ||||
| firsttick = 1; | firsttick = 1; | ||||
| } | } | ||||
| int pos[MAX_SUB_HARMONICS]; | int pos[MAX_SUB_HARMONICS]; | ||||
| if(pars.Pfixedfreq == 0) | if(pars.Pfixedfreq == 0) | ||||
| @@ -91,20 +101,6 @@ void SUBnote::setup(float freq, | |||||
| basefreq *= powf(2.0f, detune / 1200.0f); //detune | basefreq *= powf(2.0f, detune / 1200.0f); //detune | ||||
| // basefreq*=ctl.pitchwheel.relfreq;//pitch wheel | // basefreq*=ctl.pitchwheel.relfreq;//pitch wheel | ||||
| //global filter | |||||
| GlobalFilterCenterPitch = pars.GlobalFilter->getfreq() //center freq | |||||
| + (pars.PGlobalFilterVelocityScale / 127.0f | |||||
| * 6.0f) //velocity sensing | |||||
| * (VelF(velocity, | |||||
| pars.PGlobalFilterVelocityScaleFunction) | |||||
| - 1); | |||||
| if(!legato) { | |||||
| GlobalFilterL = NULL; | |||||
| GlobalFilterR = NULL; | |||||
| GlobalFilterEnvelope = NULL; | |||||
| } | |||||
| int harmonics = 0; | int harmonics = 0; | ||||
| //select only harmonics that desire to compute | //select only harmonics that desire to compute | ||||
| @@ -113,7 +109,7 @@ void SUBnote::setup(float freq, | |||||
| continue; | continue; | ||||
| pos[harmonics++] = n; | pos[harmonics++] = n; | ||||
| } | } | ||||
| if(!legato) | |||||
| if(!legato) //normal note | |||||
| firstnumharmonics = numharmonics = harmonics; | firstnumharmonics = numharmonics = harmonics; | ||||
| else { | else { | ||||
| if(harmonics > firstnumharmonics) | if(harmonics > firstnumharmonics) | ||||
| @@ -129,7 +125,7 @@ void SUBnote::setup(float freq, | |||||
| } | } | ||||
| if(!legato) { | |||||
| if(!legato) { //normal note | |||||
| lfilter = memory.valloc<bpfilter>(numstages * numharmonics); | lfilter = memory.valloc<bpfilter>(numstages * numharmonics); | ||||
| if(stereo) | if(stereo) | ||||
| rfilter = memory.valloc<bpfilter>(numstages * numharmonics); | rfilter = memory.valloc<bpfilter>(numstages * numharmonics); | ||||
| @@ -199,7 +195,7 @@ void SUBnote::setup(float freq, | |||||
| oldpitchwheel = 0; | oldpitchwheel = 0; | ||||
| oldbandwidth = 64; | oldbandwidth = 64; | ||||
| if(!legato) { | |||||
| if(!legato) { //normal note | |||||
| if(pars.Pfixedfreq == 0) | if(pars.Pfixedfreq == 0) | ||||
| initparameters(basefreq); | initparameters(basefreq); | ||||
| else | else | ||||
| @@ -211,13 +207,14 @@ void SUBnote::setup(float freq, | |||||
| else | else | ||||
| freq *= basefreq / 440.0f; | freq *= basefreq / 440.0f; | ||||
| if(pars.PGlobalFilterEnabled) { | |||||
| globalfiltercenterq = pars.GlobalFilter->getq(); | |||||
| GlobalFilterFreqTracking = pars.GlobalFilter->getfreqtracking( | |||||
| basefreq); | |||||
| } | |||||
| if(GlobalFilter) | |||||
| GlobalFilter->updateNoteFreq(basefreq); | |||||
| } | } | ||||
| if(GlobalFilter) | |||||
| GlobalFilter->updateSense(velocity, pars.PGlobalFilterVelocityScale, | |||||
| pars.PGlobalFilterVelocityScaleFunction); | |||||
| oldamplitude = newamplitude; | oldamplitude = newamplitude; | ||||
| } | } | ||||
| @@ -260,8 +257,7 @@ void SUBnote::KillNote() | |||||
| memory.dealloc(AmpEnvelope); | memory.dealloc(AmpEnvelope); | ||||
| memory.dealloc(FreqEnvelope); | memory.dealloc(FreqEnvelope); | ||||
| memory.dealloc(BandWidthEnvelope); | memory.dealloc(BandWidthEnvelope); | ||||
| memory.dealloc(GlobalFilterL); | |||||
| memory.dealloc(GlobalFilterR); | |||||
| memory.dealloc(GlobalFilter); | |||||
| memory.dealloc(GlobalFilterEnvelope); | memory.dealloc(GlobalFilterEnvelope); | ||||
| NoteEnabled = OFF; | NoteEnabled = OFF; | ||||
| } | } | ||||
| @@ -383,23 +379,19 @@ void SUBnote::filter(bpfilter &filter, float *smps) | |||||
| void SUBnote::initparameters(float freq) | void SUBnote::initparameters(float freq) | ||||
| { | { | ||||
| AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, synth.dt()); | AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, synth.dt()); | ||||
| if(pars.PFreqEnvelopeEnabled) | if(pars.PFreqEnvelopeEnabled) | ||||
| FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, synth.dt()); | FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, synth.dt()); | ||||
| else | |||||
| FreqEnvelope = NULL; | |||||
| if(pars.PBandWidthEnvelopeEnabled) | if(pars.PBandWidthEnvelopeEnabled) | ||||
| BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, freq, synth.dt()); | BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, freq, synth.dt()); | ||||
| else | |||||
| BandWidthEnvelope = NULL; | |||||
| if(pars.PGlobalFilterEnabled) { | if(pars.PGlobalFilterEnabled) { | ||||
| globalfiltercenterq = pars.GlobalFilter->getq(); | |||||
| GlobalFilterL = Filter::generate(memory, pars.GlobalFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| if(stereo) | |||||
| GlobalFilterR = Filter::generate(memory, pars.GlobalFilter, | |||||
| synth.samplerate, synth.buffersize); | |||||
| GlobalFilterEnvelope = memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq, synth.dt()); | GlobalFilterEnvelope = memory.alloc<Envelope>(*pars.GlobalFilterEnvelope, freq, synth.dt()); | ||||
| GlobalFilterFreqTracking = pars.GlobalFilter->getfreqtracking(basefreq); | |||||
| GlobalFilter = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, stereo, freq); | |||||
| GlobalFilter->addMod(*GlobalFilterEnvelope); | |||||
| } | } | ||||
| computecurrentparameters(); | computecurrentparameters(); | ||||
| } | } | ||||
| @@ -492,21 +484,9 @@ void SUBnote::computecurrentparameters() | |||||
| newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f; | newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f; | ||||
| //Filter | //Filter | ||||
| if(GlobalFilterL != NULL) { | |||||
| float globalfilterpitch = GlobalFilterCenterPitch | |||||
| + GlobalFilterEnvelope->envout(); | |||||
| float filterfreq = globalfilterpitch + ctl.filtercutoff.relfreq | |||||
| + GlobalFilterFreqTracking; | |||||
| filterfreq = Filter::getrealfreq(filterfreq); | |||||
| GlobalFilterL->setfreq_and_q(filterfreq, | |||||
| globalfiltercenterq * ctl.filterq.relq); | |||||
| if(GlobalFilterR != NULL) | |||||
| GlobalFilterR->setfreq_and_q( | |||||
| filterfreq, | |||||
| globalfiltercenterq | |||||
| * ctl.filterq.relq); | |||||
| } | |||||
| if(GlobalFilter) | |||||
| GlobalFilter->update(ctl.filtercutoff.relfreq, | |||||
| ctl.filterq.relq); | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -534,8 +514,6 @@ int SUBnote::noteout(float *outl, float *outr) | |||||
| outl[i] += tmpsmp[i] * rolloff; | outl[i] += tmpsmp[i] * rolloff; | ||||
| } | } | ||||
| if(GlobalFilterL != NULL) | |||||
| GlobalFilterL->filterout(&outl[0]); | |||||
| //right channel | //right channel | ||||
| if(stereo) { | if(stereo) { | ||||
| @@ -549,11 +527,15 @@ int SUBnote::noteout(float *outl, float *outr) | |||||
| for(int i = 0; i < synth.buffersize; ++i) | for(int i = 0; i < synth.buffersize; ++i) | ||||
| outr[i] += tmpsmp[i] * rolloff; | outr[i] += tmpsmp[i] * rolloff; | ||||
| } | } | ||||
| if(GlobalFilterR != NULL) | |||||
| GlobalFilterR->filterout(&outr[0]); | |||||
| } | |||||
| else | |||||
| if(GlobalFilter) | |||||
| GlobalFilter->filter(outl, outr); | |||||
| } else { | |||||
| if(GlobalFilter) | |||||
| GlobalFilter->filter(outl, 0); | |||||
| memcpy(outr, outl, synth.bufferbytes); | memcpy(outr, outl, synth.bufferbytes); | ||||
| } | |||||
| if(firsttick != 0) { | if(firsttick != 0) { | ||||
| int n = 10; | int n = 10; | ||||
| @@ -25,7 +25,6 @@ | |||||
| #include "SynthNote.h" | #include "SynthNote.h" | ||||
| #include "../globals.h" | #include "../globals.h" | ||||
| #include "../DSP/Filter.h" | |||||
| class SUBnote:public SynthNote | class SUBnote:public SynthNote | ||||
| { | { | ||||
| @@ -48,6 +47,10 @@ class SUBnote:public SynthNote | |||||
| int midinote, | int midinote, | ||||
| bool legato = false); | bool legato = false); | ||||
| void computecurrentparameters(); | void computecurrentparameters(); | ||||
| /* | |||||
| * Initialize envelopes and global filter | |||||
| * calls computercurrentparameters() | |||||
| */ | |||||
| void initparameters(float freq); | void initparameters(float freq); | ||||
| void KillNote(); | void KillNote(); | ||||
| @@ -67,18 +70,14 @@ class SUBnote:public SynthNote | |||||
| Envelope *FreqEnvelope; | Envelope *FreqEnvelope; | ||||
| Envelope *BandWidthEnvelope; | Envelope *BandWidthEnvelope; | ||||
| Filter *GlobalFilterL, *GlobalFilterR; | |||||
| Envelope *GlobalFilterEnvelope; | |||||
| ModFilter *GlobalFilter; | |||||
| Envelope *GlobalFilterEnvelope; | |||||
| //internal values | //internal values | ||||
| ONOFFTYPE NoteEnabled; | ONOFFTYPE NoteEnabled; | ||||
| int firsttick, portamento; | int firsttick, portamento; | ||||
| float volume, oldamplitude, newamplitude; | float volume, oldamplitude, newamplitude; | ||||
| float GlobalFilterCenterPitch; //octaves | |||||
| float GlobalFilterFreqTracking; | |||||
| struct bpfilter { | struct bpfilter { | ||||
| float freq, bw, amp; //filter parameters | float freq, bw, amp; //filter parameters | ||||
| float a1, a2, b0, b2; //filter coefs. b1=0 | float a1, a2, b0, b2; //filter coefs. b1=0 | ||||
| @@ -72,10 +72,7 @@ class BankUI {open | |||||
| dirname=fl_input("New empty Bank:"); | dirname=fl_input("New empty Bank:"); | ||||
| if (dirname==NULL) return; | if (dirname==NULL) return; | ||||
| osc->write("/newbank", "s", dirname); | |||||
| /*if (result!=0) fl_alert("Error: Could not make a new bank (directory)..");*/ | |||||
| osc->write("/bank/newbank", "s", dirname); | |||||
| refreshmainwindow();} | refreshmainwindow();} | ||||
| xywh {685 5 93 25} labelfont 1 labelsize 11 align 128 | xywh {685 5 93 25} labelfont 1 labelsize 11 align 128 | ||||
| } | } | ||||
| @@ -336,9 +336,7 @@ filename=fl_file_chooser("Open:","({*.xsz})",NULL,0); | |||||
| if (filename==NULL) return; | if (filename==NULL) return; | ||||
| osc->write("/load_xsz", "s", filename); | osc->write("/load_xsz", "s", filename); | ||||
| /* | |||||
| if (result==-10) fl_alert("Error: Could not load the file\\nbecause it is not a scale file."); | |||||
| else if (result<0) fl_alert("Error: Could not load the file.");*/} | |||||
| } | |||||
| xywh {40 40 100 20} | xywh {40 40 100 20} | ||||
| } | } | ||||
| MenuItem {} { | MenuItem {} { | ||||
| @@ -358,9 +356,7 @@ if (result) { | |||||
| }; | }; | ||||
| osc->write("/save_xsz", "s", filename); | |||||
| /*if (result<0) fl_alert("Error: Could not save the file.");*/} | |||||
| osc->write("/save_xsz", "s", filename); } | |||||
| xywh {30 30 100 20} | xywh {30 30 100 20} | ||||
| } | } | ||||
| MenuItem {} { | MenuItem {} { | ||||
| @@ -96,7 +96,8 @@ class MicrotonalUI {} { | |||||
| Fl_Input tuningsinput { | Fl_Input tuningsinput { | ||||
| label {Tunings:} | label {Tunings:} | ||||
| xywh {8 144 182 264} type Multiline labelfont 1 labelsize 11 align 5 when 2 | xywh {8 144 182 264} type Multiline labelfont 1 labelsize 11 align 5 when 2 | ||||
| code0 {updateTuningsInput();} | |||||
| code0 {o->init("tunings");} | |||||
| class Fl_Osc_Input | |||||
| } | } | ||||
| Fl_Input commentinput { | Fl_Input commentinput { | ||||
| label {Comment:} | label {Comment:} | ||||
| @@ -113,23 +114,18 @@ class MicrotonalUI {} { | |||||
| } | } | ||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label {Import .SCL file} | label {Import .SCL file} | ||||
| callback {/*const char *filename; | |||||
| callback {const char *filename; | |||||
| filename=fl_file_chooser("Open:","(*.scl)",NULL,0); | filename=fl_file_chooser("Open:","(*.scl)",NULL,0); | ||||
| if (filename==NULL) return; | if (filename==NULL) return; | ||||
| int result=microtonal->loadscl(filename); | |||||
| if (result==0) { | |||||
| osc->write("/load_scl", "s", filename); | |||||
| if (true) { | |||||
| updateTuningsInput(); | updateTuningsInput(); | ||||
| nameinput->cut(0,nameinput->maximum_size()); | |||||
| nameinput->insert((char *)microtonal->Pname); | |||||
| nameinput->position(0); | |||||
| commentinput->cut(0,commentinput->maximum_size()); | |||||
| commentinput->insert((char *)microtonal->Pname); | |||||
| commentinput->position(0); | |||||
| nameinput->update(); | |||||
| commentinput->update(); | |||||
| tuningsinput->position(0); | tuningsinput->position(0); | ||||
| octavesizeoutput->do_callback(); | |||||
| } else { | |||||
| fl_alert("Error: Could not load the file."); | |||||
| };*/} | |||||
| octavesizeoutput->update(); | |||||
| } | |||||
| } | |||||
| tooltip {Inport Scala .scl file (tunnings)} xywh {243 411 84 15} box THIN_UP_BOX labelfont 1 labelsize 10 | tooltip {Inport Scala .scl file (tunnings)} xywh {243 411 84 15} box THIN_UP_BOX labelfont 1 labelsize 10 | ||||
| } | } | ||||
| Fl_Group keymappinggroup { | Fl_Group keymappinggroup { | ||||
| @@ -138,7 +134,8 @@ if (result==0) { | |||||
| } { | } { | ||||
| Fl_Input mappinginput { | Fl_Input mappinginput { | ||||
| xywh {250 147 146 258} type Multiline labelfont 1 labelsize 11 align 5 when 2 | xywh {250 147 146 258} type Multiline labelfont 1 labelsize 11 align 5 when 2 | ||||
| code0 {updateMappingInput();} | |||||
| code0 {o->init("mapping");} | |||||
| class Fl_Osc_Input | |||||
| } | } | ||||
| Fl_Counter firstnotecounter { | Fl_Counter firstnotecounter { | ||||
| label {First note} | label {First note} | ||||
| @@ -177,12 +174,11 @@ o->show();} | |||||
| Fl_Button {} { | Fl_Button {} { | ||||
| label {Import .kbm file} | label {Import .kbm file} | ||||
| callback { | callback { | ||||
| //TODO Disabled until this can be moved into middleware | |||||
| /*const char *filename; | |||||
| const char *filename; | |||||
| filename=fl_file_chooser("Open:","(*.kbm)",NULL,0); | filename=fl_file_chooser("Open:","(*.kbm)",NULL,0); | ||||
| if (filename==NULL) return; | if (filename==NULL) return; | ||||
| int result=microtonal->loadkbm(filename); | |||||
| if (result==0) { | |||||
| osc->write("/load_kbm", "s", filename); | |||||
| if (true) { | |||||
| updateMappingInput(); | updateMappingInput(); | ||||
| mappinginput->position(0); | mappinginput->position(0); | ||||
| mapsizeoutput->update(); | mapsizeoutput->update(); | ||||
| @@ -192,9 +188,7 @@ if (result==0) { | |||||
| mappingenabledbutton->update(); | mappingenabledbutton->update(); | ||||
| afreqinput->update(); | afreqinput->update(); | ||||
| anotecounter->update(); | anotecounter->update(); | ||||
| } else { | |||||
| fl_alert("Error: Could not load the file."); | |||||
| };*/} | |||||
| }} | |||||
| tooltip {Inport Scala .kbm file (keyboard mapping)} xywh {243 428 84 16} box THIN_UP_BOX labelfont 1 labelsize 10 | tooltip {Inport Scala .kbm file (keyboard mapping)} xywh {243 428 84 16} box THIN_UP_BOX labelfont 1 labelsize 10 | ||||
| } | } | ||||
| } | } | ||||
| @@ -236,39 +230,13 @@ o->redraw();} | |||||
| } | } | ||||
| } | } | ||||
| Function {updateTuningsInput()} {} { | Function {updateTuningsInput()} {} { | ||||
| code {char *tmpbuf=new char[100]; | |||||
| /* | |||||
| tuningsinput->cut(0,tuningsinput->maximum_size()); | |||||
| for (int i=0;i<microtonal->getoctavesize();i++){ | |||||
| if (i!=0) tuningsinput->insert("\\n"); | |||||
| microtonal->tuningtoline(i,tmpbuf,100); | |||||
| tuningsinput->insert(tmpbuf); | |||||
| }; | |||||
| */ | |||||
| delete []tmpbuf;} {} | |||||
| code {tuningsinput->update();} {} | |||||
| } | } | ||||
| Function {updateMappingInput()} {} { | Function {updateMappingInput()} {} { | ||||
| code {char *tmpbuf=new char[100]; | |||||
| /* | |||||
| mappinginput->cut(0,tuningsinput->maximum_size()); | |||||
| for (int i=0;i<microtonal->Pmapsize;i++){ | |||||
| if (i!=0) mappinginput->insert("\\n"); | |||||
| if ((microtonal->Pmapping[i])==-1) | |||||
| snprintf(tmpbuf,100,"x"); | |||||
| else snprintf(tmpbuf,100,"%d",microtonal->Pmapping[i]); | |||||
| mappinginput->insert(tmpbuf); | |||||
| }; | |||||
| */ | |||||
| delete []tmpbuf;} {} | |||||
| code { mappinginput->update(); } {} | |||||
| } | } | ||||
| Function {MicrotonalUI(Fl_Osc_Interface *osc, std::string base)} {} { | |||||
| code {make_window(osc, base);} {} | |||||
| Function {MicrotonalUI(Fl_Osc_Interface *osc_, std::string base)} {} { | |||||
| code {osc=osc_;make_window(osc, base);} {} | |||||
| } | } | ||||
| Function {~MicrotonalUI()} {} { | Function {~MicrotonalUI()} {} { | ||||
| code {microtonaluiwindow->hide(); | code {microtonaluiwindow->hide(); | ||||
| @@ -278,15 +246,14 @@ delete(microtonaluiwindow);} {} | |||||
| code {microtonaluiwindow->show();} {} | code {microtonaluiwindow->show();} {} | ||||
| } | } | ||||
| Function {apply()} {} { | Function {apply()} {} { | ||||
| code {/*int err=microtonal->texttotunings(tuningsinput->value()); | |||||
| if (err>=0) fl_alert("Parse Error: The input may contain only numbers (like 232.59)\\n or divisions (like 121/64)."); | |||||
| if (err==-2) fl_alert("Parse Error: The input is empty."); | |||||
| octavesizeoutput->do_callback(); | |||||
| microtonal->texttomapping(mappinginput->value()); | |||||
| mapsizeoutput->do_callback(); | |||||
| anotecounter->do_callback(); | |||||
| */ | |||||
| //applybutton->color(FL_GRAY);} {} | |||||
| code { | |||||
| osc->write("/microtonal/tunings", "s", tuningsinput->value()); | |||||
| osc->write("/microtonal/mapping", "s", mappinginput->value()); | |||||
| octavesizeoutput->update(); | |||||
| mapsizeoutput->update(); | |||||
| anotecounter->update(); | |||||
| } {} | |||||
| } | |||||
| decl {Fl_Osc_Interface *osc;} {private local | |||||
| } | } | ||||
| } | } | ||||
| @@ -62,6 +62,12 @@ class Controller; | |||||
| class Master; | class Master; | ||||
| class Part; | class Part; | ||||
| class Filter; | |||||
| class AnalogFilter; | |||||
| class SVFilter; | |||||
| class FormantFilter; | |||||
| class ModFilter; | |||||
| #if defined(__APPLE__) || defined(__FreeBSD__) | #if defined(__APPLE__) || defined(__FreeBSD__) | ||||
| #include <complex> | #include <complex> | ||||
| #else | #else | ||||
| @@ -136,7 +136,7 @@ int main(int argc, char *argv[]) | |||||
| << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others" | << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others" | ||||
| << endl; | << endl; | ||||
| cerr | cerr | ||||
| << " Copyright (c) 2009-2015 Mark McCurry [active maintainer]" | |||||
| << " Copyright (c) 2009-2016 Mark McCurry [active maintainer]" | |||||
| << endl; | << endl; | ||||
| cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; | cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; | ||||
| cerr << "This program is free software (GNU GPL v2 or later) and \n"; | cerr << "This program is free software (GNU GPL v2 or later) and \n"; | ||||