diff options
Diffstat (limited to 'synth/parser.cpp')
-rw-r--r-- | synth/parser.cpp | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/synth/parser.cpp b/synth/parser.cpp new file mode 100644 index 0000000..52717a5 --- /dev/null +++ b/synth/parser.cpp @@ -0,0 +1,618 @@ +#include <cstdlib> +#include <fstream> + +#include "parser.h" +#include "defines.h" +#include "programs.h" +#include "globals.h" +#include "util.h" +#include "readwave.h" + + +Parser::Parser() +{ + n_osc=0; + osc=NULL; + env=NULL; + for (int i=0;i<128;i++) + controller_default[i]=0; +} + +Parser::~Parser() +{ + uninit_stuff(); +} + +list<string> Parser::extract_terms(string s) +{ + list<string> result; + + size_t p=-1,p2; + + s="+"+s+"+"; + + p=0; + p2=s.find_first_of("+-",p+1); + + while(p2!=string::npos) + { + result.push_back(s.substr(p,p2-p)); + p=p2; + p2=s.find_first_of("+-",p+1); + } + return result; +} + +list<string> Parser::extract_factors(string s) +{ + list<string> result; + + size_t p=-1,p2; + + s="*"+s+"*"; + + p=0; + p2=s.find_first_of("*/",p+1); + + while(p2!=string::npos) + { + result.push_back(s.substr(p,p2-p)); + p=p2; + p2=s.find_first_of("*/",p+1); + } + return result; +} + +list<term_t> Parser::extract_formula(string s) +{ + list<term_t> result; + term_t tmp; + list<string> terms=extract_terms(s); + + for (list<string>::iterator term=terms.begin(); term!=terms.end(); term++) + { + list<string> factors=extract_factors(term->substr(1)); + double fac= ((*term)[0]=='+') ? 1.0 : -1.0; + string cont=""; + for (list<string>::iterator factor=factors.begin(); factor!=factors.end(); factor++) + { + if (factor->find_first_not_of("0123456789.*/+-")==string::npos) + { + if ((*factor)[0]=='*') + fac*=atof((*factor).substr(1).c_str()); + else + { + if (atof((*factor).substr(1).c_str())==0) + throw string("dividing by zero is not allowed"); + + fac/=atof((*factor).substr(1).c_str()); + } + } + else + { + if (cont!="") + throw string("multiplicating controllers is not allowed"); + + if ((*factor)[0]!='*') + throw string("dividing through a controller is not allowed"); + + cont=(*factor).substr(1); + } + } + if (cont=="") + tmp.c=NO_CONT; + else + { + if (extract_array_name(cont)!="cont") + throw string("expected 'cont', found '"+extract_array_name(cont)+"'"); + + tmp.c=extract_array_index(cont,1); + if ((tmp.c<0) || (tmp.c>127)) + throw string("invalid controller specified"); + } + tmp.f=fac*ONE; + result.push_back(tmp); + } + return result; +} + +param_factor_t Parser::parse_pfactor(string s) //TODO fast dasselbe wie oben. mergen? +{ //TODO cont müsste vel heißen FINDMICH ---> ^ ^ ^ + param_factor_t result; + result.offset=0; + result.vel_amount=0; + + list<string> terms=extract_terms(s); + + for (list<string>::iterator term=terms.begin(); term!=terms.end(); term++) + { + list<string> factors=extract_factors(term->substr(1)); + double fac= ((*term)[0]=='+') ? 1.0 : -1.0; + string cont=""; + for (list<string>::iterator factor=factors.begin(); factor!=factors.end(); factor++) + { + if (factor->find_first_not_of("0123456789.*/+-")==string::npos) + { + if ((*factor)[0]=='*') + fac*=atof((*factor).substr(1).c_str()); + else + { + if (atof((*factor).substr(1).c_str())==0) + throw string("dividing by zero is not allowed"); + + fac/=atof((*factor).substr(1).c_str()); + } + } + else + { + if (cont!="") + throw string("multiplicating velocity is not allowed"); + + if ((*factor)[0]!='*') + throw string("dividing through velocity is not allowed"); + + cont=(*factor).substr(1); + } + } + if (cont=="") + { + result.offset+= fac*ONE; + } + else + { + if (cont!="vel") + throw string("expected 'vel', found '"+cont+"'"); + + result.vel_amount+= fac*ONE; + } + } + return result; +} + +void Parser::init_stuff() +{ + env=new env_settings_t[n_osc]; + osc=new oscillator_t[n_osc]; + for (int i=0;i<n_osc;i++) + { + osc[i].n_osc=n_osc; + + osc[i].fm_strength=new fixed_t[n_osc]; + for (int j=0;j<n_osc;j++) + osc[i].fm_strength[j]=0; + + osc[i].output=0; + osc[i].waveform=0; + osc[i].factor=ONE; + osc[i].tremolo_depth=0; + osc[i].tremolo_lfo=0; + osc[i].vibrato_depth=0; + osc[i].vibrato_lfo=0; + osc[i].custom_wave=NULL; + + + env[i].attack=0; + env[i].decay=0; + env[i].sustain=ONE; + env[i].release=0; + env[i].hold=true; + } + + filter.enabled=false; + filter.env_amount=0; + filter.env_settings.attack=filter.env_settings.decay= + filter.env_settings.release=0; + filter.env_settings.sustain=0; + filter.env_settings.hold=true; + + filter.freqfactor_offset=0; + filter.resonance=0; + filter.trem_strength=0; + filter.trem_lfo=0; + + + + pfactor.out=new param_factor_t [n_osc]; + pfactor.fm=new param_factor_t* [n_osc]; + + pfactor.filter_env.offset=ONE; + pfactor.filter_env.vel_amount=0; + + pfactor.filter_res.offset=ONE; + pfactor.filter_res.vel_amount=0; + + pfactor.filter_offset.offset=ONE; + pfactor.filter_offset.vel_amount=0; + + for (int i=0;i<n_osc;i++) + { + pfactor.out[i].offset=0; + pfactor.out[i].vel_amount=ONE; + + pfactor.fm[i]=new param_factor_t [n_osc]; + for (int j=0;j<n_osc;j++) + { + pfactor.fm[i][j].offset=ONE; + pfactor.fm[i][j].vel_amount=0; + } + } + +} +void Parser::uninit_stuff() +{ + if (osc) + { + for (int i=0;i<n_osc;i++) + delete [] osc[i].fm_strength; + + delete [] osc; + osc=NULL; + } + if (env) + delete [] env; +} + +string Parser::extract_array_name(string s) +{ + size_t p; + p=s.find('['); + if (p!=string::npos) + return s.substr(0,p); + else + return s; +} + +int Parser::extract_array_index(string s, int dim) +{ + size_t p=-1,p2; + for (int i=0;i<dim;i++) + { + p=s.find('[',p+1); + if (p==string::npos) return -1; + } + + p2=s.find(']',p+1); + if (p2==string::npos) return -1; + + return atoi(s.substr(p+1,p2-p-1).c_str()); +} + +//if this function fails, this WILL BE fatal if unhandled in the +//caller function. so this function throws errors +//if this function fails the settings are in an undefined, illegal +//state. if these settings are given to some oscillator_t by +//operator=, it will probably die while trying to create an array +//with size 0 or so. +void Parser::parse(string fn) +{ + char buf[2000]; + list<term_t> terms; + string line; + string var; + string array; + string strval; + float val; + + parameter_enum p; + + int ind,ind2=0; + + int state; + + uninit_stuff(); + + ifstream f; + f.open(fn.c_str()); + if (f.good()) + { + state=0; + while (!f.eof()) + { + f.getline(buf,sizeof(buf)/sizeof(*buf)-1); + line=buf; + line=remove_all_spaces(buf); + if ((line!="") && (line[0]!='#')) //ignore comments and empty lines + { + if (line=="controllers:") + { + state=2; + continue; + } + else if (line=="defaults:") + { + state=3; + continue; + } + else if (line=="velocity:") + { + state=4; + continue; + } + + var=extract_var(line); + array=extract_array_name(var); + strval=extract_val(line); + val=atof(strval.c_str()); + + switch (state) + { + case 0: //expect and read number of oscillators + if (var!="oscillators") + throw string("need to know number of oscillators"); + else + n_osc=val; + + if (n_osc<=0) throw string("invalid number of oscillators"); + + init_stuff(); + + state=1; + break; + + case 1: //read and set information about oscillator settings + p=param_to_enum(array); + + ind=extract_array_index(var,1); + if ( param_needs_index(p) && (!((ind>=0) && (ind<n_osc))) ) + throw string("out of array bounds"); + + + switch (p) + { + case MODULATION: + ind2=extract_array_index(var,2); + if (!((ind2>=0) && (ind2<n_osc))) + throw string("out of array bounds"); + + osc[ind].fm_strength[ind2]=val*ONE; + break; + case OUTPUT: + osc[ind].output=val*ONE; + break; + case WAVEFORM: + if (isfloat(strval)) + { + osc[ind].waveform=int(val); + } + else + { + size_t pos=strval.find(':'); + if (pos==string::npos) + throw string("expected 'freq:file.wav', found no ':'"); + + float given_freq=atof(strval.substr(0,pos).c_str()); + string wavefile=strval.substr(pos+1); + + if (given_freq<=0) + throw string("illegal freq specified for custom wave '"+wavefile+"'"); + + osc[ind].custom_wave=new custom_wave_t; + read_wave(wavefile.c_str(), osc[ind].custom_wave); + osc[ind].custom_wave->samp_rate/=given_freq; + } + break; + case FACTOR: + osc[ind].factor=val*ONE; + break; + case TREMOLO: + osc[ind].tremolo_depth=int(val); + break; + case TREM_LFO: + if (strval=="snh") + osc[ind].tremolo_lfo=SNH_LFO; + else + { + osc[ind].tremolo_lfo= int(val); + if ((val<0) || (val>=N_LFOS)) + throw string("invalid value for tremolo_lfo"); + } + break; + case VIBRATO: + osc[ind].vibrato_depth=val; + break; + case VIB_LFO: + if (strval=="snh") + osc[ind].vibrato_lfo= SNH_LFO; + else + { + osc[ind].vibrato_lfo= int(val); + if ((val<0) || (val>=N_LFOS)) + throw string("invalid value for vibrato_lfo"); + } + break; + case ATTACK: + env[ind].attack=val*samp_rate; + break; + case DECAY: + env[ind].decay=val*samp_rate; + break; + case SUSTAIN: + env[ind].sustain=val*ONE; + break; + case RELEASE: + env[ind].release=val*samp_rate; + break; + case HOLD: + env[ind].hold=(val!=0); + break; + case KSR: + osc[ind].ksr=val; + break; + case KSL: + osc[ind].ksl=val; + break; + case SYNC: + osc[ind].sync=(val!=0); + break; + case FILTER_ENABLED: + filter.enabled=(val!=0); + break; + case FILTER_ENV_AMOUNT: + filter.env_amount=val; + break; + case FILTER_ATTACK: + filter.env_settings.attack=val*samp_rate/filter_update_frames; + break; + case FILTER_DECAY: + filter.env_settings.decay=val*samp_rate/filter_update_frames; + break; + case FILTER_SUSTAIN: + filter.env_settings.sustain=val*ONE; + break; + case FILTER_RELEASE: + filter.env_settings.release=val*samp_rate/filter_update_frames; + break; + case FILTER_HOLD: + filter.env_settings.hold=(val!=0); + break; + case FILTER_OFFSET: + filter.freqfactor_offset=val; + break; + case FILTER_RESONANCE: + filter.resonance=val; + break; + case FILTER_TREMOLO: + filter.trem_strength=int(val); + break; + case FILTER_TREM_LFO: + if (strval=="snh") + filter.trem_lfo=SNH_LFO; + else + { + filter.trem_lfo=int(val); + if ((val<0) || (val>=N_LFOS)) + throw string("invalid value for filter_trem_lfo"); + } + break; + case SYNC_FACTOR: + sync_factor=val*ONE; + break; + default: + throw string("unknown variable ('"+array+"')"); + } + break; + + case 2: //read how controllers influence parameters + p=param_to_enum(array); + + ind=extract_array_index(var,1); + if ( param_needs_index(p) && (!((ind>=0) && (ind<n_osc))) ) + throw string("out of array bounds"); + + parameter_t par; + par.par=p; + + if (par.par==UNKNOWN) + throw string("unknown variable ('"+array+"')"); + + if (par.par==MODULATION) + { + ind2=extract_array_index(var,2); + if (!((ind2>=0) && (ind2<n_osc))) + throw string("out of array bounds"); + } + + par.osc=ind; + par.index=ind2; + + terms=extract_formula(strval); + for (list<term_t>::iterator it=terms.begin(); it!=terms.end(); it++) + if (it->c!=NO_CONT) + affect[it->c].insert(par); + + + formula[par]=terms; + break; + + case 3: //read controller default values + if (array=="cont") + { + ind=extract_array_index(var,1); + + if ((ind<0) || (ind>127)) + throw string("out of array bounds"); + + if ((val<0) || (val>127)) + throw string("value out of range"); + + controller_default[ind]=val; + } + else + throw string("expected cont, found '"+array+"'"); + + break; + + case 4: //read velocity-influence over certain params + p=param_to_enum(array); + + ind=extract_array_index(var,1); + if ( param_needs_index(p) && (!((ind>=0) && (ind<n_osc))) ) + throw string("out of array bounds"); + + switch(p) + { + case MODULATION: + ind2=extract_array_index(var,2); + if (!((ind2>=0) && (ind2<n_osc))) + throw string("out of array bounds"); + + pfactor.fm[ind][ind2]=parse_pfactor(strval); + break; + + case OUTPUT: + pfactor.out[ind]=parse_pfactor(strval); + break; + + case FILTER_ENV_AMOUNT: + pfactor.filter_env=parse_pfactor(strval); + break; + + case FILTER_RESONANCE: + pfactor.filter_res=parse_pfactor(strval); + break; + + case FILTER_OFFSET: + pfactor.filter_offset=parse_pfactor(strval); + break; + + default: + throw string("velocity cannot influence parameter '"+array+"'"); + + + } + } + } + } + } + else + throw string ("could not open '"+fn+"'"); +} + + +program_t Parser::get_results() const +{ + program_t result; + + result.n_osc=n_osc; + + copy(&affect[0],&affect[128],result.controller_affects); + + result.formula=formula; + + result.osc_settings=new oscillator_t[n_osc]; + + copy(&osc[0],&osc[n_osc],result.osc_settings); + result.env_settings=new env_settings_t[n_osc]; + copy(&env[0],&env[n_osc],result.env_settings); + + for (int i=0;i<128;i++) + result.controller[i]=controller_default[i]; + + result.filter_settings=filter; + + result.sync_factor=sync_factor; + + result.pfactor=pfactor; + + return result; +} |