From 3d95a25600b5cab8a7e5245b7a581bd8c8939276 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Wed, 5 Jan 2011 15:47:55 +0100 Subject: Note-compiler is _mostly_ complete; plus some tiny bugfixes The note-compiler can read a program-definition and emits a cpp-file which implements that definition. This implementation is optimized. HOWEVER, the compiler still does not emit a set_param() function. this will lead to a linker error because the function is not implemented (but defined). After adding an empty implementation by hand the emitted compiles well, and also seems to work when used in the synth. TODO: - implement set_param() - compiler must emit a loader-function (which returns a new Note) - use that loader function in the synth --- TODO | 12 + note_compiler/main.cpp | 707 ++++++++++++++++++++++++++++++++++- note_compiler/note.cpp.todo | 72 ++++ note_compiler/parser.cpp | 41 +- note_compiler/programs.h | 3 + note_compiler/templates/ctor.foot | 11 + note_compiler/templates/get_sample.1 | 23 ++ note_compiler/templates/head.1 | 35 ++ note_compiler/templates/head.2 | 11 + synth/note.cpp | 1 - synth/note.h | 7 - synth/parser.cpp | 2 +- 12 files changed, 907 insertions(+), 18 deletions(-) create mode 100644 note_compiler/note.cpp.todo create mode 100644 note_compiler/templates/ctor.foot create mode 100644 note_compiler/templates/get_sample.1 create mode 100644 note_compiler/templates/head.1 create mode 100644 note_compiler/templates/head.2 diff --git a/TODO b/TODO index 97475fb..e9ee05b 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,16 @@ TODO für den synth +!!!o BUG: bei Note::set_param sollte statt dem eigentlichen param + der orig gesetzt werden und dann neu berechnet werden! +!!!o BUG: bei release und reattack: BEIDE male die filter-envelope beachten! + o KSL mit powf und floats statt mit double umschreiben + o statt lfo-nummer direkten zugriff auf curr_lfo angeben? + o oscval-nullen kann in get_sample() weggelassen werden + o bei tremolo (und vibrato?): eventuell nicht prüfen, obs aktiviert + ist, sondern zur not einfach *1 rechnen? + o in get_sample(), beim aufaddieren zum out (und vmtl auf fm-)wert: + erst nach dem addieren scalen, statt für jeden faktor einzeln + + o filter_envelope könnte mit anderem ctor geinitet werden (weniger schreibarbeit) o notes compilieren und als .so-datei laden o programme on-the-fly (um)laden diff --git a/note_compiler/main.cpp b/note_compiler/main.cpp index cd83eee..7f48c0b 100644 --- a/note_compiler/main.cpp +++ b/note_compiler/main.cpp @@ -1,4 +1,7 @@ +//TODO: auf unbenutzte envelopes achten! + #include +#include #include "parser.h" #include "programs.h" @@ -7,18 +10,710 @@ using namespace std; -int main(int argc, char** argv) +ostream &out=cout; +ostream &comment=cout; +program_t prog; + +void write_empty_line() +{ + out << "\n"; +} + +void include_file(string file) +{ + file="templates/"+file; + + ifstream in; + in.open(file.c_str()); + if (!in.good()) + throw string ("include: could not open '"+file+"'"); + + char tempbuf[2000]; + while (!in.eof()) + { + in.getline(tempbuf, sizeof(tempbuf)); + out << tempbuf << "\n"; + } +} + +void write_env_decs() +{ + for (int i=0;iwave_len, osc"<samp_rate);\n"; + else + out << "ONE * PHASE_INIT;\n"; + } + + out << "\t\n" + "\tdo_ksl();\n" + "\t\n" + "\t\n"; + + if ((prog.filter.enabled==true) || (prog.filter.enabled_const==false)) + { + //a filter_params and orig.filter_params member exist + out << "\tfilter_params=prg.filter_settings;\n"; + if (prog.use_pfactor) + out << "\torig.filter_params=prg.filter_settings;\n"; + + if (prog.filter.enabled_const==false) + { + out << "\tif (filter_params.enabled)\n" + "\t{\n"; + tabtmp="\t"; + } + out << tabtmp << "\tfilter_envelope=new Envelope(filter_params.env_settings);\n" << + tabtmp << "\tfilter_update_counter=filter_update_frames;\n"; + if (prog.filter.enabled_const==false) + out << "\t}\n"; + tabtmp=""; + + out << "\t\n" + "\t\n"; + } + + + if ((prog.sync_factor!=0) || (prog.sync_factor_const==false)) + out << "\tsync_factor=prg.sync_factor;\n" + "\tsync_phase=0;\n" + "\t\n" + "\t\n"; + + + include_file("ctor.foot"); +} + +void write_dtor() +{ + int i; + + out << "Note::~Note()\n" + "{\n"; + + for (i=0;ipfactor.filter_env, vel);\n" + "\tpfactor.filter_res=calc_pfactor(curr_prg->pfactor.filter_res, vel);\n" + "\tpfactor.filter_offset=calc_pfactor(curr_prg->pfactor.filter_offset, vel);\n" + "\t\n"; + } + + out << "\tfor (int i=0;i<"<pfactor.out[i], vel);\n" + "\t\t\n" + "\t\tfor (int j=0;j<"<pfactor.fm[i][j], vel);\n" + "\t}\n"; + } + + out << "}\n"; +} - cout << "n_osc="<>SCALE;\n" + "\tfor (int i=0;i<"<>SCALE;\n"; } } + + out << "}\n"; +} + +void write_still_active() +{ + out << "bool Note::still_active()\n" + "{\n"; + + out << "\tif ( "; + if (prog.env[0].enabled) + out << " ((osc"<<0<<".output>0) && (env"<<0<<"->still_active()))"; + else + out << " // envelope"<<0<<" is disabled"; + + for (int i=1;i0) && (env"<still_active()))"; + else + out << "\n\t /* envelope"<release_key();\n"; + else + comment << "\t//envelope"<release_key();\n"; + else if (prog.filter.enabled_const==false) + out << "\n\tif (filter_params.enabled)\n" + "\t\tfilter_envelope->release_key();\n"; + + out << "}\n"; +} + +void write_release_quickly() +{ + out << "void Note::release_quickly(jack_nframes_t maxt)\n" + "{\n"; + + for (int i=0;iget_release() > maxt)\n" + "\t\tenv"<set_release(maxt);\n" + "\tenv"<release_key();\n" + "\t\n"; + else + comment << "\t//envelope"<reattack();\n"; + else + comment << "\t//envelope"<reattack();\n"; + else if (prog.filter.enabled_const==false) + out << "\n\tif (filter_params.enabled)\n" + "\t\tfilter_envelope->reattack();\n"; + + out << "}\n"; +} + +void write_do_ksr() +{ + out << "void Note::do_ksr()\n" + "{\n"; + + for (int i=0;iset_ratefactor(1.0 / pow(freq>>SCALE, osc"<> SCALE );\n" + "\t\n"; + + for (int i=0;iset_max( "<> SCALE;\n" + << tabtemp << "\t\n" + << tabtemp << "\tif (sync_phase >= ONE)\n" + << tabtemp << "\t{\n" + << tabtemp << "\t\tsync_phase-=ONE;\n" + << tabtemp << "\t\t\n"; + + for (int i=0;iwave_len, osc"+IntToStr(i)+".custom_wave->samp_rate)"; + else + initstring="ONE * PHASE_INIT"; + + string full_command="osc"+IntToStr(i)+".phase="+initstring+";\n"; + + if ( prog.osc[i].sync_const && prog.osc[i].sync ) + out << tabtemp << "\t\t" << full_command; + else if ( prog.osc[i].sync_const && (prog.osc[i].sync==false) ) + comment << tabtemp << "\t\t//sync is disabled for osc"<>SCALE)*osc"+IntToStr(i)+".factor/samp_rate)>>SCALE"; + string phase_inc = "(actual_freq*osc"+IntToStr(i)+".factor/samp_rate)>>SCALE"; + if (prog.osc[i].vibrato_depth_const == false) + out << "\tosc"<>SCALE fehlt noch! + for (int j=0;j>SCALE ) )"; + else + phase="osc"+IntToStr(i)+".phase"; + + // generate string for wave + string wavetemp; + if (prog.osc[i].have_custom_wave) + { + string cw="osc"+IntToStr(i)+".custom_wave"; + wavetemp=cw+"->wave[ ( "+phase+" * "+cw+"->samp_rate >>(2*SCALE) ) % "+cw+"->wave_len ]"; + } + else + { + string waveformtemp; + if (prog.osc[i].waveform_const) + waveformtemp=IntToStr(prog.osc[i].waveform); + else + waveformtemp="osc"+IntToStr(i)+".waveform"; + + wavetemp="wave["+waveformtemp+"][ ( "+phase+" * WAVE_RES >>SCALE ) % WAVE_RES ]"; + } + + // finally write "oscval[n]=..." + out << "\toscval["<get_level() >>SCALE;\n"; + else if ( (prog.osc[i].ksl!=0) || (prog.osc[i].ksl_const==false) ) + // i.e.: if osc[i] has a kslval variable + out<<" * kslval"<>SCALE;\n"; + else //no envelope, no kslval + out<<";\n"; + + + // maybe do tremolo + string tremlfo; + if (prog.osc[i].tremolo_lfo_const==false) + tremlfo="osc"+IntToStr(i)+".tremolo_lfo"; + else + tremlfo=IntToStr(prog.osc[i].tremolo_lfo); + + if (prog.osc[i].tremolo_depth_const==false) + out << "\tif (osc"<>SCALE;\n"; + else if (prog.osc[i].tremolo_depth!=0) + out << "\toscval["<>SCALE;\n"; + else + comment << "\t//oscillator"<>SCALE )"; + if (outstring_nonscaled!="") + outstring+=" "+outstring_nonscaled; + + if (outstring=="") + throw string ("this instrument has no output at all!"); + + out << "\tfixed_t out = "<=filter_update_frames)\n" + << tabtemp << "\t{\n" + << tabtemp << "\t\tfilter_update_counter=0;\n" + << tabtemp << "\t\t\n" + << tabtemp << "\t\tfloat cutoff= float(actual_freq)/ONE * \n" + << tabtemp << "\t\t\tfloat(curr_lfo[filter_params.trem_lfo][filter_params.trem_strength])/ONE *\n" + << tabtemp << "\t\t\t( filter_params.freqfactor_offset + filter_envelope->get_level() * filter_params.env_amount / float(ONE) );\n" + << tabtemp << "\t\tfilter.set_params( cutoff, filter_params.resonance );\n" + << tabtemp << "\t}\n" + << tabtemp << "\t\n" + << tabtemp << "\tfilter.process_sample(&out);\n"; + + if (prog.filter.enabled_const==false) + { + out << "\t}\n"; + tabtemp=""; + } + out << "\t\n"; + } + + out << "\treturn out;\n"; + out << "}\n"; +} + +void write_foo() +{ + out << "void Note::foo()\n" + "{\n"; + + out << "}\n"; +} + +void generate_source() +{ + //#includes and definition of class Note + include_file("head.1"); + write_env_decs(); + write_empty_line(); + write_oscval_decs(); + write_empty_line(); + write_osc_decs(); + write_empty_line(); + write_sync_decs(); + write_empty_line(); + write_filter_decs(); + write_empty_line(); + write_pfactor_decs(); + include_file("head.2"); + + + //implementation of Note's functions + write_ctor(); + write_dtor(); + write_empty_line(); + + write_recalc_factors(); + write_apply_pfactor(); + write_empty_line(); + + write_still_active(); + write_release(); + write_release_quickly(); + write_reattack(); + write_empty_line(); + + write_do_ksr(); + write_do_ksl(); + write_empty_line(); + + write_get_sample(); +} + +int main(int argc, char** argv) +{ + prog=parse("../../filtertest.prog"); + + prog.env[1].enabled=false; + + + generate_source(); + return 0; } diff --git a/note_compiler/note.cpp.todo b/note_compiler/note.cpp.todo new file mode 100644 index 0000000..a256525 --- /dev/null +++ b/note_compiler/note.cpp.todo @@ -0,0 +1,72 @@ +void Note::set_param(const parameter_t &p, fixed_t v) //ACHTUNG: +{ + //wenn das verändert wird, muss auch program_t::set_param verändert werden! + switch(p.par) + { + case ATTACK: envelope[p.osc]->set_attack(v*samp_rate >>SCALE); break; + case DECAY: envelope[p.osc]->set_decay(v*samp_rate >>SCALE); break; + case SUSTAIN: envelope[p.osc]->set_sustain(v); break; + case RELEASE: envelope[p.osc]->set_release(v*samp_rate >>SCALE); break; + case HOLD: envelope[p.osc]->set_hold(v!=0); break; + + case KSR: oscillator[p.osc].ksr=float(v)/ONE; break; + case KSL: oscillator[p.osc].ksl=float(v)/ONE; break; + + case FACTOR: oscillator[p.osc].factor=v; break; + case MODULATION: oscillator[p.osc].fm_strength[p.index]=v*pfactor.fm[p.osc][p.index] >>SCALE; break; + case OUTPUT: oscillator[p.osc].output=v*pfactor.out[p.osc] >>SCALE; break; + case TREMOLO: oscillator[p.osc].tremolo_depth=v; break; + case TREM_LFO: oscillator[p.osc].tremolo_lfo=v; break; + case VIBRATO: oscillator[p.osc].vibrato_depth=v; break; + case VIB_LFO: oscillator[p.osc].vibrato_lfo=v; break; + case WAVEFORM: oscillator[p.osc].waveform=v; break; + case SYNC: oscillator[p.osc].sync=(v!=0); break; + + case FILTER_ENABLED: output_note("NOTE: cannot enable filter in playing notes"); break; + case FILTER_ENV_AMOUNT: filter_params.env_amount=float(v*pfactor.filter_env)/ONE/ONE; break; + + case FILTER_ATTACK: + if (filter_params.enabled) + filter_envelope->set_attack(v*samp_rate/filter_update_frames >>SCALE); + else + output_note("NOTE: cannot set filter-attack when filter is disabled"); + break; + + case FILTER_DECAY: + if (filter_params.enabled) + filter_envelope->set_decay(v*samp_rate/filter_update_frames >>SCALE); + else + output_note("NOTE: cannot set filter-decay when filter is disabled"); + break; + + case FILTER_SUSTAIN: + if (filter_params.enabled) + filter_envelope->set_sustain(v); + else + output_note("NOTE: cannot set filter-sustain when filter is disabled"); + break; + + case FILTER_RELEASE: + if (filter_params.enabled) + filter_envelope->set_release(v*samp_rate/filter_update_frames >>SCALE); + else + output_note("NOTE: cannot set filter-release when filter is disabled"); + break; + + case FILTER_HOLD: + if (filter_params.enabled) + filter_envelope->set_hold(v!=0); + else + output_note("NOTE: cannot set filter-hold when filter is disabled"); + break; + + case FILTER_OFFSET: filter_params.freqfactor_offset=float(v*pfactor.filter_offset)/ONE/ONE; break; + case FILTER_RESONANCE: filter_params.resonance=float(v*pfactor.filter_res)/ONE/ONE; break; + case FILTER_TREMOLO: filter_params.trem_strength=v; break; + case FILTER_TREM_LFO: filter_params.trem_lfo=v; break; + + case SYNC_FACTOR: sync_factor=v; break; + default: throw string("trying to set an unknown parameter"); + + } +} diff --git a/note_compiler/parser.cpp b/note_compiler/parser.cpp index 98c65ac..3f142bd 100644 --- a/note_compiler/parser.cpp +++ b/note_compiler/parser.cpp @@ -37,6 +37,8 @@ void init_oscs(int n_osc, oscillator_t *osc) osc[i].vibrato_lfo=0; osc[i].vibrato_lfo_const=true; osc[i].have_custom_wave=false; + osc[i].sync=false; + osc[i].sync_const=true; } } @@ -44,11 +46,12 @@ void init_envs(int n_osc, env_settings_t *env) { for (int i=0;i100)) //TODO FINDMICH besseres kriterium? + env[i].enabled=false; + + if ( ((filter.env_settings.attack==0) && (filter.env_settings.sustain==1.0) + && (filter.env_settings.release>100)) //TODO FINDMICH siehe oben + || ((filter.env_amount==0) && (filter.env_amount_const==true)) ) + filter.env_settings.enabled=false; + + bool use_sync=false; + for (int i=0;i=portamento_frames) + freq=dest_freq; + else //will only happen if p_t < p_frames -> p_frames is always > 0 -> div. ok + freq = old_freq + (dest_freq-old_freq)*portamento_t/portamento_frames; + + do_ksl(); + + portamento_t++; + } + + fixed_t actual_freq=freq*pitchbend >>SCALE; + + fixed_t *temp; + temp=old_oscval; //swap the current and old oscval-pointers + old_oscval=oscval; + oscval=temp; + diff --git a/note_compiler/templates/head.1 b/note_compiler/templates/head.1 new file mode 100644 index 0000000..3d9e903 --- /dev/null +++ b/note_compiler/templates/head.1 @@ -0,0 +1,35 @@ +#include + +#include + +#include "globals.h" +#include "defines.h" +#include "programs.h" +#include "envelope.h" +#include "fixed.h" +#include "filter.h" +#include "note_skel.h" + +using namespace std; + +class Note : public NoteSkel +{ + public: + Note(int n, float v,program_t &prg, jack_nframes_t pf, fixed_t pb, int prg_no); + ~Note(); + fixed_t get_sample(); + + void release_quickly(jack_nframes_t maxt); + void release(); + void reattack(); + bool still_active(); + void set_param(const parameter_t &p, fixed_t v); + + private: + void do_ksl(); + void do_ksr(); + + void recalc_factors(); + void apply_pfactor(); + +// member variables begin here diff --git a/note_compiler/templates/head.2 b/note_compiler/templates/head.2 new file mode 100644 index 0000000..917030c --- /dev/null +++ b/note_compiler/templates/head.2 @@ -0,0 +1,11 @@ +// member variables end here +}; + +//this function returns the smallest phase_init possible for a +//given custom_wave which is greater or equal than PHASE_INIT +#define PHASE_INIT 100 +inline fixed_t init_custom_osc_phase(int len, fixed_t sr) +{ + return ( (fixed_t(ceil( float(PHASE_INIT) * sr / len / ONE )) *len << (2*SCALE)) / sr); +} + diff --git a/synth/note.cpp b/synth/note.cpp index 8ab0546..2f192f2 100644 --- a/synth/note.cpp +++ b/synth/note.cpp @@ -1,4 +1,3 @@ -#include #include #include "note.h" diff --git a/synth/note.h b/synth/note.h index 0e76505..aab471c 100644 --- a/synth/note.h +++ b/synth/note.h @@ -51,13 +51,6 @@ class Note : public NoteSkel oscillator_t *oscillator; filter_params_t filter_params; } orig; - -/* *einstellungen: oszillatoren, deren lautstärke etc. - * note - * lautstärke - * *pitchbend - * *portamento time - */ }; diff --git a/synth/parser.cpp b/synth/parser.cpp index 96648d6..a2ab747 100644 --- a/synth/parser.cpp +++ b/synth/parser.cpp @@ -286,7 +286,7 @@ program_t parse(string fn) pfactor_formula_t pfactor; - fixed_t sync_factor; + fixed_t sync_factor=0; -- cgit v1.2.1