@@ -402,6 +402,13 @@ extern "C" { | |||
#undef rChangeCb | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/ModFilter.cpp" | |||
#undef rObject | |||
#undef rStdString | |||
#undef rStdStringCb | |||
#undef rChangeCb | |||
#define rChangeCb | |||
#include "zynaddsubfx/Synth/OscilGen.cpp" | |||
#undef PC | |||
#undef DIFF | |||
@@ -279,7 +279,7 @@ void NotePool::enforceKeyLimit(int limit) | |||
void NotePool::releasePlayingNotes(void) | |||
{ | |||
for(auto &d:activeDesc()) { | |||
if(d.playing()) { | |||
if(d.playing() || d.sustained()) { | |||
d.setStatus(KEY_RELEASED); | |||
for(auto s:activeNotes(d)) | |||
s.note->releasekey(); | |||
@@ -323,9 +323,11 @@ void AnalogFilter::setstages(int stages_) | |||
{ | |||
if(stages_ >= MAX_FILTER_STAGES) | |||
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]) | |||
@@ -39,7 +39,7 @@ Filter::Filter(unsigned int srate, int bufsize) | |||
alias(); | |||
} | |||
Filter *Filter::generate(Allocator &memory, FilterParams *pars, | |||
Filter *Filter::generate(Allocator &memory, const FilterParams *pars, | |||
unsigned int srate, int bufsize) | |||
{ | |||
assert(srate != 0); | |||
@@ -29,7 +29,7 @@ class Filter | |||
{ | |||
public: | |||
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); | |||
Filter(unsigned int srate, int bufsize); | |||
@@ -28,8 +28,8 @@ | |||
#include "AnalogFilter.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; | |||
for(int i = 0; i < numformants; ++i) | |||
@@ -27,11 +27,10 @@ | |||
#include "Filter.h" | |||
class Allocator; | |||
class FormantFilter:public Filter | |||
{ | |||
public: | |||
FormantFilter(class FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize); | |||
FormantFilter(const FilterParams *pars, Allocator *alloc, unsigned int srate, int bufsize); | |||
~FormantFilter(); | |||
void filterout(float *smp); | |||
void setfreq(float frequency); | |||
@@ -85,7 +85,7 @@ void SVFilter::setfreq(float frequency) | |||
bool nyquistthresh = (abovenq ^ oldabovenq); | |||
//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) | |||
needsinterpolation = true; | |||
ipar = par; | |||
@@ -212,6 +212,7 @@ int Bank::loadfromslot(unsigned int ninstrument, Part *part) | |||
*/ | |||
int Bank::loadbank(string bankdirname) | |||
{ | |||
normalizedirsuffix(bankdirname); | |||
DIR *dir = opendir(bankdirname.c_str()); | |||
clearbank(); | |||
@@ -285,9 +286,8 @@ int Bank::newbank(string newbankdirname) | |||
string bankdir; | |||
bankdir = config->cfg.bankRootDirList[0]; | |||
if(((bankdir[bankdir.size() - 1]) != '/') | |||
&& ((bankdir[bankdir.size() - 1]) != '\\')) | |||
bankdir += "/"; | |||
expanddirname(bankdir); | |||
normalizedirsuffix(bankdir); | |||
bankdir += newbankdirname; | |||
#ifdef _WIN32 | |||
@@ -404,6 +404,8 @@ void Bank::setLsb(uint8_t lsb) | |||
void Bank::scanrootdir(string rootdir) | |||
{ | |||
expanddirname(rootdir); | |||
DIR *dir = opendir(rootdir.c_str()); | |||
if(dir == NULL) | |||
return; | |||
@@ -498,3 +500,23 @@ void Bank::deletefrombank(int pos) | |||
Bank::ins_t::ins_t() | |||
: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; | |||
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; | |||
public: | |||
@@ -23,6 +23,7 @@ | |||
#include <cmath> | |||
#include <cstring> | |||
#include <cstdio> | |||
#include <cassert> | |||
#include <rtosc/ports.h> | |||
#include <rtosc/port-sugar.h> | |||
@@ -72,6 +73,118 @@ const rtosc::Ports Microtonal::ports = { | |||
Microtonal &m = *(Microtonal*)d.obj; | |||
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; | |||
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].x1 = 2; | |||
@@ -298,7 +408,7 @@ bool Microtonal::operator!=(const Microtonal µ) const | |||
/* | |||
* 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; | |||
float x = -1.0f, tmp, tuning = 1.0f; | |||
@@ -346,10 +456,10 @@ int Microtonal::linetotunings(unsigned int nline, const char *line) | |||
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 | |||
} | |||
@@ -359,10 +469,11 @@ int Microtonal::linetotunings(unsigned int nline, const char *line) | |||
*/ | |||
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)) { | |||
int i; | |||
for(i = 0; i < MAX_LINE_SIZE; ++i) { | |||
lin[i] = text[k++]; | |||
if(lin[i] < 0x20) | |||
@@ -371,7 +482,7 @@ int Microtonal::texttotunings(const char *text) | |||
lin[i] = '\0'; | |||
if(strlen(lin) == 0) | |||
continue; | |||
int err = linetotunings(nl, lin); | |||
int err = linetotunings(tmpoctave[nl], lin); | |||
if(err != -1) { | |||
delete [] lin; | |||
return nl; //Parse error | |||
@@ -384,7 +495,7 @@ int Microtonal::texttotunings(const char *text) | |||
if(nl == 0) | |||
return -2; //the input is empty | |||
octavesize = nl; | |||
for(i = 0; i < octavesize; ++i) { | |||
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; | |||
@@ -449,28 +560,37 @@ void Microtonal::tuningtoline(int n, char *line, int maxn) | |||
int Microtonal::loadline(FILE *file, char *line) | |||
{ | |||
memset(line, 0, 500); | |||
do { | |||
if(fgets(line, 500, file) == 0) | |||
return 1; | |||
} while(line[0] == '!'); | |||
return 0; | |||
} | |||
/* | |||
* 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"); | |||
char tmp[500]; | |||
OctaveTuning tmpoctave[MAX_OCTAVE_SIZE]; | |||
fseek(file, 0, SEEK_SET); | |||
//loads the short description | |||
if(loadline(file, &tmp[0]) != 0) | |||
return 2; | |||
for(int i = 0; i < 500; ++i) | |||
if(tmp[i] < 32) | |||
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 | |||
if(loadline(file, &tmp[0]) != 0) | |||
return 2; | |||
@@ -478,29 +598,31 @@ int Microtonal::loadscl(const char *filename) | |||
sscanf(&tmp[0], "%d", &nnotes); | |||
if(nnotes > MAX_OCTAVE_SIZE) | |||
return 2; | |||
//load the tunnings | |||
for(int nline = 0; nline < nnotes; ++nline) { | |||
if(loadline(file, &tmp[0]) != 0) | |||
return 2; | |||
linetotunings(nline, &tmp[0]); | |||
linetotunings(tmpoctave[nline], tmp); | |||
} | |||
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; | |||
} | |||
/* | |||
* 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"); | |||
int x; | |||
@@ -511,32 +633,32 @@ int Microtonal::loadkbm(const char *filename) | |||
//loads the mapsize | |||
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | |||
return 2; | |||
Pmapsize = limit(x, 0, 127); | |||
kbm.Pmapsize = limit(x, 0, 127); | |||
//loads first MIDI note to retune | |||
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | |||
return 2; | |||
Pfirstkey = limit(x, 0, 127); | |||
kbm.Pfirstkey = limit(x, 0, 127); | |||
//loads last MIDI note to retune | |||
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | |||
return 2; | |||
Plastkey = limit(x, 0, 127); | |||
kbm.Plastkey = limit(x, 0, 127); | |||
//loads last the middle note where scale fro scale degree=0 | |||
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | |||
return 2; | |||
Pmiddlenote = limit(x, 0, 127); | |||
kbm.Pmiddlenote = limit(x, 0, 127); | |||
//loads the reference note | |||
if(loadline(file, tmp) != 0 || sscanf(tmp, "%d", &x) == 0) | |||
return 2; | |||
PAnote = limit(x,0,127); | |||
kbm.PAnote = limit(x,0,127); | |||
//loads the reference freq. | |||
if(loadline(file, tmp) != 0 || sscanf(tmp, "%f", &tmpPAfreq) == 0) | |||
return 2; | |||
PAfreq = tmpPAfreq; | |||
kbm.PAfreq = tmpPAfreq; | |||
//the scale degree(which is the octave) is not loaded, | |||
//it is obtained by the tunnings with getoctavesize() method | |||
@@ -544,20 +666,20 @@ int Microtonal::loadkbm(const char *filename) | |||
return 2; | |||
//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) | |||
return 2; | |||
if(sscanf(tmp, "%d", &x) == 0) | |||
x = -1; | |||
Pmapping[nline] = x; | |||
kbm.Pmapping[nline] = x; | |||
} | |||
Pmappingenabled = 1; | |||
kbm.Pmappingenabled = 1; | |||
} | |||
else { | |||
Pmappingenabled = 0; | |||
Pmapping[0] = 0; | |||
Pmapsize = 1; | |||
kbm.Pmappingenabled = 0; | |||
kbm.Pmapping[0] = 0; | |||
kbm.Pmapsize = 1; | |||
} | |||
fclose(file); | |||
@@ -24,12 +24,43 @@ | |||
#define MICROTONAL_H | |||
#include <cstdio> | |||
#include <stdint.h> | |||
#include "../globals.h" | |||
#define MAX_OCTAVE_SIZE 128 | |||
#define MICROTONAL_MAX_NAME_LEN 120 | |||
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*/ | |||
class Microtonal | |||
@@ -88,9 +119,9 @@ class Microtonal | |||
/**Convert tunning to string*/ | |||
void tuningtoline(int n, char *line, int maxn); | |||
/**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*/ | |||
int loadkbm(const char *filename); | |||
static int loadkbm(KbmInfo &kbm, const char *filename); | |||
/**Load text into the internal tunings | |||
* | |||
*\todo better description*/ | |||
@@ -114,23 +145,19 @@ class Microtonal | |||
bool operator==(const Microtonal µ) const; | |||
bool operator!=(const Microtonal µ) const; | |||
void clone(Microtonal &m); | |||
static const rtosc::Ports ports; | |||
//only paste handler should access there (quasi-private) | |||
unsigned char octavesize; | |||
OctaveTuning octave[MAX_OCTAVE_SIZE]; | |||
private: | |||
int linetotunings(unsigned int nline, const char *line); | |||
//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 | |||
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; | |||
}; | |||
@@ -157,6 +157,12 @@ void deallocate(const char *str, void *v) | |||
delete (Master*)v; | |||
else if(!strcmp(str, "fft_t")) | |||
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 | |||
fprintf(stderr, "Unknown type '%s', leaking pointer %p!!\n", str, v); | |||
} | |||
@@ -556,6 +562,48 @@ public: | |||
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) | |||
{ | |||
obj_store.clear(); | |||
@@ -861,6 +909,12 @@ rtosc::Ports bankPorts = { | |||
rBegin; | |||
impl.setLsb(rtosc_argument(msg, 0).i); | |||
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); | |||
loadMidiLearn(xml, impl.midi_mapper); | |||
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, | |||
rBegin; | |||
const char *file = rtosc_argument(msg, 0).s; | |||
@@ -312,7 +312,7 @@ void Part::defaultsinstrument() | |||
Pdrummode = 0; | |||
for(int n = 0; n < NUM_KIT_ITEMS; ++n) { | |||
//kit[n].Penabled = false; | |||
kit[n].Penabled = false; | |||
kit[n].Pmuted = false; | |||
kit[n].Pminkey = 0; | |||
kit[n].Pmaxkey = 127; | |||
@@ -847,9 +847,6 @@ void Part::setkititemstatus(unsigned kititem, bool Penabled_) | |||
delete kkit.adpars; | |||
delete kkit.subpars; | |||
delete kkit.padpars; | |||
kkit.adpars = nullptr; | |||
kkit.subpars = nullptr; | |||
kkit.padpars = nullptr; | |||
kkit.Pname[0] = '\0'; | |||
notePool.killAllNotes(); | |||
@@ -98,7 +98,7 @@ XMLwrapper::XMLwrapper() | |||
{ | |||
version.Major = 2; | |||
version.Minor = 5; | |||
version.Revision = 2; | |||
version.Revision = 3; | |||
minimal = true; | |||
@@ -237,21 +237,21 @@ void FilterParams::getfromFilterParams(FilterParams *pars) | |||
/* | |||
* Parameter control | |||
*/ | |||
float FilterParams::getfreq() | |||
float FilterParams::getfreq() const | |||
{ | |||
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; | |||
} | |||
float FilterParams::getfreqtracking(float notefreq) | |||
float FilterParams::getfreqtracking(float notefreq) const | |||
{ | |||
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 | |||
} | |||
@@ -259,7 +259,7 @@ float FilterParams::getgain() | |||
/* | |||
* 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); | |||
} | |||
@@ -267,7 +267,7 @@ float FilterParams::getcenterfreq() | |||
/* | |||
* 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; | |||
} | |||
@@ -275,7 +275,7 @@ float FilterParams::getoctavesfreq() | |||
/* | |||
* 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) | |||
x = 1.0f; | |||
@@ -286,7 +286,7 @@ float FilterParams::getfreqx(float x) | |||
/* | |||
* 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(); | |||
} | |||
@@ -294,19 +294,19 @@ float FilterParams::getfreqpos(float freq) | |||
/* | |||
* 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); | |||
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); | |||
return result; | |||
} | |||
float FilterParams::getformantq(unsigned char q) | |||
float FilterParams::getformantq(unsigned char q) const | |||
{ | |||
//temp | |||
float result = powf(25.0f, (q - 32.0f) / 64.0f); | |||
@@ -48,10 +48,10 @@ class FilterParams:public PresetsArray | |||
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 Ptype; // Filter type (for analog lpf,hpf,bpf..) | |||
@@ -80,14 +80,14 @@ class FilterParams:public PresetsArray | |||
unsigned char nvowel; //the vowel from the position | |||
} 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); | |||
@@ -29,9 +29,8 @@ | |||
#include "../globals.h" | |||
#include "../Misc/Util.h" | |||
#include "../Misc/Allocator.h" | |||
#include "../DSP/Filter.h" | |||
#include "../Params/ADnoteParameters.h" | |||
#include "../Params/FilterParams.h" | |||
#include "ModFilter.h" | |||
#include "OscilGen.h" | |||
#include "ADnote.h" | |||
@@ -63,13 +62,6 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
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 = | |||
pars.GlobalPar.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | |||
NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment; | |||
@@ -373,17 +365,10 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) | |||
NoteVoicePar[nvoice].AmpLfo = NULL; | |||
NoteVoicePar[nvoice].AmpEnvelope = NULL; | |||
NoteVoicePar[nvoice].VoiceFilterL = NULL; | |||
NoteVoicePar[nvoice].VoiceFilterR = NULL; | |||
NoteVoicePar[nvoice].Filter = NULL; | |||
NoteVoicePar[nvoice].FilterEnvelope = 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 = | |||
pars.VoicePar[nvoice].Pfilterbypass; | |||
@@ -514,13 +499,9 @@ void ADnote::legatonote(LegatoParams lpars) | |||
else | |||
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) { | |||
@@ -580,13 +561,12 @@ void ADnote::legatonote(LegatoParams lpars) | |||
+ 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 = | |||
pars.VoicePar[nvoice].Pfilterbypass; | |||
@@ -653,9 +633,12 @@ void ADnote::legatonote(LegatoParams lpars) | |||
* NoteGlobalPar.AmpEnvelope->envout_dB() | |||
* 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 | |||
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) | |||
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 */ | |||
if((NoteVoicePar[nvoice].FMEnabled != NONE) | |||
@@ -857,21 +842,24 @@ void ADnote::initparameters() | |||
vce.FreqLfo = memory.alloc<LFO>(*param.FreqLfo, basefreq, time); | |||
/* 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 */ | |||
if((vce.FMEnabled != NONE) && (vce.FMVoice < 0)) { | |||
@@ -1050,8 +1038,8 @@ float ADnote::getFMvoicebasefreq(int nvoice) const | |||
void ADnote::computecurrentparameters() | |||
{ | |||
int nvoice; | |||
float voicefreq, voicepitch, filterpitch, filterfreq, FMfreq, | |||
FMrelativepitch, globalpitch, globalfilterpitch; | |||
float voicefreq, voicepitch, FMfreq, | |||
FMrelativepitch, globalpitch; | |||
globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | |||
+ NoteGlobalPar.FreqLfo->lfoout() | |||
* ctl.modwheel.relmod); | |||
@@ -1060,19 +1048,9 @@ void ADnote::computecurrentparameters() | |||
* NoteGlobalPar.AmpEnvelope->envout_dB() | |||
* 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 | |||
float portamentofreqrap = 1.0f; | |||
@@ -1107,21 +1085,10 @@ void ADnote::computecurrentparameters() | |||
/****************/ | |||
/* 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 | |||
@@ -1702,12 +1669,13 @@ int ADnote::noteout(float *outl, float *outr) | |||
firsttick[nvoice] = 0; | |||
} | |||
// 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 | |||
if(NoteVoicePar[nvoice].AmpEnvelope) | |||
@@ -1767,14 +1735,13 @@ int ADnote::noteout(float *outl, float *outr) | |||
//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(bypassr, bypassl, synth.bufferbytes); | |||
} | |||
else | |||
NoteGlobalPar.GlobalFilterR->filterout(&outr[0]); | |||
for(int i = 0; i < synth.buffersize; ++i) { | |||
outl[i] += bypassl[i]; | |||
@@ -1881,8 +1848,7 @@ void ADnote::Voice::kill(Allocator &memory, const SYNTH_T &synth) | |||
memory.dealloc(FreqLfo); | |||
memory.dealloc(AmpEnvelope); | |||
memory.dealloc(AmpLfo); | |||
memory.dealloc(VoiceFilterL); | |||
memory.dealloc(VoiceFilterR); | |||
memory.dealloc(Filter); | |||
memory.dealloc(FilterEnvelope); | |||
memory.dealloc(FilterLfo); | |||
memory.dealloc(FMFreqEnvelope); | |||
@@ -1905,8 +1871,7 @@ void ADnote::Global::kill(Allocator &memory) | |||
memory.dealloc(FreqLfo); | |||
memory.dealloc(AmpEnvelope); | |||
memory.dealloc(AmpLfo); | |||
memory.dealloc(GlobalFilterL); | |||
memory.dealloc(GlobalFilterR); | |||
memory.dealloc(Filter); | |||
memory.dealloc(FilterEnvelope); | |||
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 | |||
* 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()); | |||
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 * | |||
******************************************/ | |||
class Filter * GlobalFilterL, *GlobalFilterR; | |||
float FilterCenterPitch; //octaves | |||
float FilterQ; | |||
float FilterFreqTracking; | |||
Envelope *FilterEnvelope; | |||
LFO *FilterLfo; | |||
ModFilter *Filter; | |||
Envelope *FilterEnvelope; | |||
LFO *FilterLfo; | |||
} NoteGlobalPar; | |||
@@ -226,15 +220,9 @@ class ADnote:public SynthNote | |||
/************************* | |||
* 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, | |||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
*/ | |||
#include <cassert> | |||
#include <cmath> | |||
#include "PADnote.h" | |||
#include "ModFilter.h" | |||
#include "../Misc/Config.h" | |||
#include "../Misc/Allocator.h" | |||
#include "../DSP/Filter.h" | |||
#include "../Params/PADnoteParameters.h" | |||
#include "../Params/Controller.h" | |||
#include "../Params/FilterParams.h" | |||
@@ -32,6 +33,10 @@ PADnote::PADnote(const PADnoteParameters *parameters, | |||
SynthParams pars, const int& interpolation) | |||
:SynthNote(pars), pars(*parameters), interpolation(interpolation) | |||
{ | |||
NoteGlobalPar.GlobalFilter = nullptr; | |||
NoteGlobalPar.FilterEnvelope = nullptr; | |||
NoteGlobalPar.FilterLfo = nullptr; | |||
firsttime = true; | |||
setup(pars.frequency, pars.velocity, pars.portamento, pars.note); | |||
} | |||
@@ -112,13 +117,6 @@ void PADnote::setup(float freq, | |||
else | |||
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) { | |||
NoteGlobalPar.Fadein_adjustment = | |||
pars.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE; | |||
@@ -157,17 +155,25 @@ void PADnote::setup(float freq, | |||
* NoteGlobalPar.AmpLfo->amplfoout(); | |||
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) { | |||
finished_ = true; | |||
@@ -198,8 +204,7 @@ PADnote::~PADnote() | |||
memory.dealloc(NoteGlobalPar.FreqLfo); | |||
memory.dealloc(NoteGlobalPar.AmpEnvelope); | |||
memory.dealloc(NoteGlobalPar.AmpLfo); | |||
memory.dealloc(NoteGlobalPar.GlobalFilterL); | |||
memory.dealloc(NoteGlobalPar.GlobalFilterR); | |||
memory.dealloc(NoteGlobalPar.GlobalFilter); | |||
memory.dealloc(NoteGlobalPar.FilterEnvelope); | |||
memory.dealloc(NoteGlobalPar.FilterLfo); | |||
} | |||
@@ -230,8 +235,7 @@ inline void PADnote::fadein(float *smps) | |||
void PADnote::computecurrentparameters() | |||
{ | |||
float globalpitch, globalfilterpitch; | |||
globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | |||
const float globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout() | |||
+ NoteGlobalPar.FreqLfo->lfoout() | |||
* ctl.modwheel.relmod + NoteGlobalPar.Detune); | |||
globaloldamplitude = globalnewamplitude; | |||
@@ -239,18 +243,8 @@ void PADnote::computecurrentparameters() | |||
* NoteGlobalPar.AmpEnvelope->envout_dB() | |||
* 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 | |||
float portamentofreqrap = 1.0f; | |||
@@ -377,8 +371,7 @@ int PADnote::noteout(float *outl, float *outr) | |||
firsttime = false; | |||
} | |||
NoteGlobalPar.GlobalFilterL->filterout(outl); | |||
NoteGlobalPar.GlobalFilterR->filterout(outr); | |||
NoteGlobalPar.GlobalFilter->filter(outl, outr); | |||
//Apply the punch | |||
if(NoteGlobalPar.Punch.Enabled != 0) | |||
@@ -98,15 +98,9 @@ class PADnote:public SynthNote | |||
/****************************************** | |||
* 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; | |||
@@ -28,16 +28,24 @@ | |||
#include "../globals.h" | |||
#include "SUBnote.h" | |||
#include "Envelope.h" | |||
#include "ModFilter.h" | |||
#include "../Params/Controller.h" | |||
#include "../Params/SUBnoteParameters.h" | |||
#include "../Params/FilterParams.h" | |||
#include "../Misc/Time.h" | |||
#include "../Misc/Util.h" | |||
#include "../Misc/Allocator.h" | |||
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); | |||
} | |||
@@ -56,12 +64,14 @@ void SUBnote::setup(float freq, | |||
panning = pars.PPanning / 127.0f; | |||
else | |||
panning = RND; | |||
if(!legato) { | |||
if(!legato) { //normal note | |||
numstages = pars.Pnumstages; | |||
stereo = pars.Pstereo; | |||
start = pars.Pstart; | |||
firsttick = 1; | |||
} | |||
int pos[MAX_SUB_HARMONICS]; | |||
if(pars.Pfixedfreq == 0) | |||
@@ -91,20 +101,6 @@ void SUBnote::setup(float freq, | |||
basefreq *= powf(2.0f, detune / 1200.0f); //detune | |||
// 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; | |||
//select only harmonics that desire to compute | |||
@@ -113,7 +109,7 @@ void SUBnote::setup(float freq, | |||
continue; | |||
pos[harmonics++] = n; | |||
} | |||
if(!legato) | |||
if(!legato) //normal note | |||
firstnumharmonics = numharmonics = harmonics; | |||
else { | |||
if(harmonics > firstnumharmonics) | |||
@@ -129,7 +125,7 @@ void SUBnote::setup(float freq, | |||
} | |||
if(!legato) { | |||
if(!legato) { //normal note | |||
lfilter = memory.valloc<bpfilter>(numstages * numharmonics); | |||
if(stereo) | |||
rfilter = memory.valloc<bpfilter>(numstages * numharmonics); | |||
@@ -199,7 +195,7 @@ void SUBnote::setup(float freq, | |||
oldpitchwheel = 0; | |||
oldbandwidth = 64; | |||
if(!legato) { | |||
if(!legato) { //normal note | |||
if(pars.Pfixedfreq == 0) | |||
initparameters(basefreq); | |||
else | |||
@@ -211,13 +207,14 @@ void SUBnote::setup(float freq, | |||
else | |||
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; | |||
} | |||
@@ -260,8 +257,7 @@ void SUBnote::KillNote() | |||
memory.dealloc(AmpEnvelope); | |||
memory.dealloc(FreqEnvelope); | |||
memory.dealloc(BandWidthEnvelope); | |||
memory.dealloc(GlobalFilterL); | |||
memory.dealloc(GlobalFilterR); | |||
memory.dealloc(GlobalFilter); | |||
memory.dealloc(GlobalFilterEnvelope); | |||
NoteEnabled = OFF; | |||
} | |||
@@ -383,23 +379,19 @@ void SUBnote::filter(bpfilter &filter, float *smps) | |||
void SUBnote::initparameters(float freq) | |||
{ | |||
AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, freq, synth.dt()); | |||
if(pars.PFreqEnvelopeEnabled) | |||
FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, freq, synth.dt()); | |||
else | |||
FreqEnvelope = NULL; | |||
if(pars.PBandWidthEnvelopeEnabled) | |||
BandWidthEnvelope = memory.alloc<Envelope>(*pars.BandWidthEnvelope, freq, synth.dt()); | |||
else | |||
BandWidthEnvelope = NULL; | |||
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()); | |||
GlobalFilterFreqTracking = pars.GlobalFilter->getfreqtracking(basefreq); | |||
GlobalFilter = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, stereo, freq); | |||
GlobalFilter->addMod(*GlobalFilterEnvelope); | |||
} | |||
computecurrentparameters(); | |||
} | |||
@@ -492,21 +484,9 @@ void SUBnote::computecurrentparameters() | |||
newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f; | |||
//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; | |||
} | |||
if(GlobalFilterL != NULL) | |||
GlobalFilterL->filterout(&outl[0]); | |||
//right channel | |||
if(stereo) { | |||
@@ -549,11 +527,15 @@ int SUBnote::noteout(float *outl, float *outr) | |||
for(int i = 0; i < synth.buffersize; ++i) | |||
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); | |||
} | |||
if(firsttick != 0) { | |||
int n = 10; | |||
@@ -25,7 +25,6 @@ | |||
#include "SynthNote.h" | |||
#include "../globals.h" | |||
#include "../DSP/Filter.h" | |||
class SUBnote:public SynthNote | |||
{ | |||
@@ -48,6 +47,10 @@ class SUBnote:public SynthNote | |||
int midinote, | |||
bool legato = false); | |||
void computecurrentparameters(); | |||
/* | |||
* Initialize envelopes and global filter | |||
* calls computercurrentparameters() | |||
*/ | |||
void initparameters(float freq); | |||
void KillNote(); | |||
@@ -67,18 +70,14 @@ class SUBnote:public SynthNote | |||
Envelope *FreqEnvelope; | |||
Envelope *BandWidthEnvelope; | |||
Filter *GlobalFilterL, *GlobalFilterR; | |||
Envelope *GlobalFilterEnvelope; | |||
ModFilter *GlobalFilter; | |||
Envelope *GlobalFilterEnvelope; | |||
//internal values | |||
ONOFFTYPE NoteEnabled; | |||
int firsttick, portamento; | |||
float volume, oldamplitude, newamplitude; | |||
float GlobalFilterCenterPitch; //octaves | |||
float GlobalFilterFreqTracking; | |||
struct bpfilter { | |||
float freq, bw, amp; //filter parameters | |||
float a1, a2, b0, b2; //filter coefs. b1=0 | |||
@@ -72,10 +72,7 @@ class BankUI {open | |||
dirname=fl_input("New empty Bank:"); | |||
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();} | |||
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; | |||
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} | |||
} | |||
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} | |||
} | |||
MenuItem {} { | |||
@@ -96,7 +96,8 @@ class MicrotonalUI {} { | |||
Fl_Input tuningsinput { | |||
label {Tunings:} | |||
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 { | |||
label {Comment:} | |||
@@ -113,23 +114,18 @@ class MicrotonalUI {} { | |||
} | |||
Fl_Button {} { | |||
label {Import .SCL file} | |||
callback {/*const char *filename; | |||
callback {const char *filename; | |||
filename=fl_file_chooser("Open:","(*.scl)",NULL,0); | |||
if (filename==NULL) return; | |||
int result=microtonal->loadscl(filename); | |||
if (result==0) { | |||
osc->write("/load_scl", "s", filename); | |||
if (true) { | |||
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); | |||
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 | |||
} | |||
Fl_Group keymappinggroup { | |||
@@ -138,7 +134,8 @@ if (result==0) { | |||
} { | |||
Fl_Input mappinginput { | |||
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 { | |||
label {First note} | |||
@@ -177,12 +174,11 @@ o->show();} | |||
Fl_Button {} { | |||
label {Import .kbm file} | |||
callback { | |||
//TODO Disabled until this can be moved into middleware | |||
/*const char *filename; | |||
const char *filename; | |||
filename=fl_file_chooser("Open:","(*.kbm)",NULL,0); | |||
if (filename==NULL) return; | |||
int result=microtonal->loadkbm(filename); | |||
if (result==0) { | |||
osc->write("/load_kbm", "s", filename); | |||
if (true) { | |||
updateMappingInput(); | |||
mappinginput->position(0); | |||
mapsizeoutput->update(); | |||
@@ -192,9 +188,7 @@ if (result==0) { | |||
mappingenabledbutton->update(); | |||
afreqinput->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 | |||
} | |||
} | |||
@@ -236,39 +230,13 @@ o->redraw();} | |||
} | |||
} | |||
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()} {} { | |||
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()} {} { | |||
code {microtonaluiwindow->hide(); | |||
@@ -278,15 +246,14 @@ delete(microtonaluiwindow);} {} | |||
code {microtonaluiwindow->show();} {} | |||
} | |||
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 Part; | |||
class Filter; | |||
class AnalogFilter; | |||
class SVFilter; | |||
class FormantFilter; | |||
class ModFilter; | |||
#if defined(__APPLE__) || defined(__FreeBSD__) | |||
#include <complex> | |||
#else | |||
@@ -136,7 +136,7 @@ int main(int argc, char *argv[]) | |||
<< "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others" | |||
<< endl; | |||
cerr | |||
<< " Copyright (c) 2009-2015 Mark McCurry [active maintainer]" | |||
<< " Copyright (c) 2009-2016 Mark McCurry [active maintainer]" | |||
<< endl; | |||
cerr << "Compiled: " << __DATE__ << " " << __TIME__ << endl; | |||
cerr << "This program is free software (GNU GPL v2 or later) and \n"; | |||