summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Jung <flo@thinkpad.(none)>2010-12-29 16:55:25 +0100
committerFlorian Jung <flo@thinkpad.(none)>2010-12-29 16:55:25 +0100
commit7113f02ae87482211aec5046f9ac46c3cc9ad017 (patch)
treeb6484b45317e7e80567d9902cf94843d227ce30e
Initial commit
-rw-r--r--.depend25
-rw-r--r--.gitignore1
-rw-r--r--synth/.gitignore1
-rw-r--r--synth/CHANGELOG131
-rw-r--r--synth/Makefile33
-rw-r--r--synth/OPTIMIZATIONS20
-rw-r--r--synth/README.developer28
-rw-r--r--synth/TODO32
-rw-r--r--synth/TODO.done71
-rw-r--r--synth/channel.cpp338
-rw-r--r--synth/channel.h63
-rw-r--r--synth/cli.cpp174
-rw-r--r--synth/cli.h6
-rw-r--r--synth/defines.cpp3
-rw-r--r--synth/defines.h62
-rw-r--r--synth/envelope.cpp168
-rw-r--r--synth/envelope.h65
-rw-r--r--synth/filter.cpp59
-rw-r--r--synth/filter.h27
-rw-r--r--synth/fixed.h10
-rw-r--r--synth/globals.cpp60
-rw-r--r--synth/globals.h73
-rw-r--r--synth/jack.cpp467
-rw-r--r--synth/jack.h12
-rw-r--r--synth/load.cpp191
-rw-r--r--synth/load.h11
-rw-r--r--synth/main.cpp190
-rw-r--r--synth/note.cpp437
-rw-r--r--synth/note.h80
-rw-r--r--synth/parser.cpp618
-rw-r--r--synth/parser.h47
-rw-r--r--synth/programs.cpp224
-rw-r--r--synth/programs.h193
-rw-r--r--synth/readwave.cpp137
-rw-r--r--synth/readwave.h9
-rw-r--r--synth/util.cpp206
-rw-r--r--synth/util.h31
37 files changed, 4303 insertions, 0 deletions
diff --git a/.depend b/.depend
new file mode 100644
index 0000000..3a0b0ed
--- /dev/null
+++ b/.depend
@@ -0,0 +1,25 @@
+channel.o: channel.cpp channel.h fixed.h programs.h note.h envelope.h \
+ filter.h defines.h util.h globals.h
+cli.o: cli.cpp util.h programs.h fixed.h globals.h channel.h note.h \
+ envelope.h filter.h defines.h load.h
+defines.o: defines.cpp defines.h
+envelope.o: envelope.cpp envelope.h programs.h fixed.h
+filter.o: filter.cpp filter.h fixed.h defines.h globals.h programs.h \
+ channel.h note.h envelope.h util.h
+globals.o: globals.cpp globals.h programs.h fixed.h channel.h note.h \
+ envelope.h filter.h defines.h util.h
+jack.o: jack.cpp defines.h globals.h programs.h fixed.h channel.h note.h \
+ envelope.h filter.h util.h jack.h
+load.o: load.cpp util.h programs.h fixed.h globals.h channel.h note.h \
+ envelope.h filter.h defines.h
+main.o: main.cpp jack.h load.h cli.h parser.h fixed.h programs.h \
+ channel.h note.h envelope.h filter.h defines.h util.h globals.h
+note.o: note.cpp note.h programs.h fixed.h envelope.h filter.h globals.h \
+ channel.h defines.h util.h
+parser.o: parser.cpp parser.h fixed.h programs.h defines.h globals.h \
+ channel.h note.h envelope.h filter.h util.h readwave.h
+programs.o: programs.cpp programs.h fixed.h globals.h channel.h note.h \
+ envelope.h filter.h defines.h util.h
+readwave.o: readwave.cpp readwave.h programs.h fixed.h util.h
+util.o: util.cpp util.h programs.h fixed.h globals.h channel.h note.h \
+ envelope.h filter.h defines.h
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8862c08
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.[oa]
diff --git a/synth/.gitignore b/synth/.gitignore
new file mode 100644
index 0000000..6569f67
--- /dev/null
+++ b/synth/.gitignore
@@ -0,0 +1 @@
+synth
diff --git a/synth/CHANGELOG b/synth/CHANGELOG
new file mode 100644
index 0000000..81c9756
--- /dev/null
+++ b/synth/CHANGELOG
@@ -0,0 +1,131 @@
+änderungen in 42b:winzige optimierung bei envelope-attack-phase
+änderungen in 42: sample and hold wird als LFO angesehen und funzt.
+änderungen in 40c:lfo_ und filter_update_freq im cli und in der
+ konfig einstellbar
+änderungen in 40b:kleinigkeiten verbessert
+änderungen in 40: auf LFOs umgestellt
+
+ STABILE VERSION: 38b: FM-Synthese mit einstellungen und controllern
+ möglich. wellenformen können auch aus wav-dateien geladen werden.
+ vibrato, tremolo, portamento, reattack, festsetzbare stimmenanzahl,
+ "abgewürgte" noten können ausgeblendet werden. channel-modulation
+ muss manuell definiert werden. pitchbend und controllerreset,
+ frameskip, stereo, KSR, KSL funktionieren. CLI, configs und
+ instrumentendefinitionen funktionieren. anschlagsdynamik kann
+ verschiedene parameter steuern. analogsynthese und osc-sync.
+
+änderungen in 38b:bugfixes: sustain_orig eingeführt, bei reattack
+ wird die vel neu gesetzt, divisionen durch null
+ verhindert
+änderungen in 38: frameskip ist nun absolut alltagstauglich
+ valgrind-gecheckt, halbwegs clean
+änderungen in 37e:velocity kann fm-stärke, vol und filter steuern
+ 37d coredumpt am ende... sehr merkwürdig. dort
+ wird result im parser direkt geführt
+ 37d_ii funktioniert. hier wird das result wie
+ gehabt erst am ende gesetzt. merkwürdig...
+ außerdem: bug gefixt (s|atoi|atof| beim parsen)
+änderungen in 37b:velocity-kontrolle implementiert, kann aber nicht
+ geladen werden
+änderungen in 36: frameskipping berichtigt (hängt von bufsize ab)
+änderungen in 35c:auto-connect für midi-in funktioniert
+ diverse bugs behoben
+änderungen in 35b:debugging-outputs entfernt oder auf output_verbose
+ umgestellt
+änderungen in 35: osc-sync funktioniert; seltener segfault-bug
+ behoben. xrun-panic funktioniert
+änderungen in 34: phase auf hohen wert geinitet, +WAVE_RES entfällt
+ es wurde berechnet, dass mit diesen einstellungen
+ oscillator.phase in 44 Jahren überlaufen wird.
+änderungen in 33: filter-settings werden aus konfig geladen. per
+ controller setzen muss noch getestet werden
+ filter-update-frames kann per CLI gesetzt werden.
+ bug in parameter_t::operator< gefixt
+änderungen in 32e:größtenteils wie 33
+änderungen in 32d:Note-ctor geändert; er erhält nun nur noch ein
+ program_t& statt den einzelnen settings-pointern
+änderungen in 32c:tiefpassfilter funktioniert, kann aber noch
+ nicht geladen oder per cont. gesetzt werden.
+änderungen in 31: sample-and-hold-generator geschrieben, kann aber
+ nirgends angewendet werden!
+änderungen in 30d:custom-waves können nun via program-datei gesetzt
+ werden; cw verbessert
+änderungen in 30b:custom-waves können geladen werden. deren samp_rate
+ ist nun in fixed_t angegeben, um ungenauigkeiten
+ wegen sr=sr/gegebene_freq zu beheben (detuned)
+änderungen in 30: custom-waves können benutzt, aber nicht geladen
+ werden
+änderungen in 29k:fehler nun über eigene funktionen ausgegeben.
+ diese funktionen können entscheiden, ob sie
+ das programm beenden (fatal-warnings etc.) oder
+ den hinweis ignorieren (quiet)
+änderungen in 29j:fehlerbehandlung fortgesetzt: einheitliches
+ schema
+änderungen in 29i:fehlerbehandlung begonnen: nicht-fatale fehler
+ werden ausgegeben, aber ignoriert
+änderungen in 29h:CLI verwendet nun floats statt ints
+ zusätzliche throw-anweisungen
+änderungen in 29g:space-sicher gemacht, frameskip=0 kann per CLI
+ gesetzt werden
+änderungen in 29f:konfig-dateien-parsen begonnen
+änderungen in 29e:clean_int., vib- und trem-freq. per CLI einstellbar
+änderungen in 29d:last_cleanup durch next_cleanup ersetzt
+änderungen in 29c:CLI begonnen; einlesen aller programme eines
+ verzeichnisses und einzelnes einlesen sollte
+ funktionieren. wird keine passende programm-
+ definition gefunden, wird ein sinus verwendet
+
+ STABILE VERSION: 28: FM-Synthese mit einstellungen und controllern
+ möglich. vibrato, tremolo, portamento, reattack, festsetzbare
+ stimmenanzahl. "abgewürgte" noten können ausgeblendet werden.
+ channel-modulation muss manuell definiert werden. pitchbend und
+ controllerreset, frameskip, stereo, KSR, KSL funktionieren.
+
+änderungen in 28: knistern bei frameskip behoben
+änderungen in 27g:noch mehr bugfixes
+änderungen in 27f:einige bugfixes
+änderungen in 27e:bei set_note wird jetzt geprüft, ob das program
+ gewechselt wurde. wenn ja: neue note anlegen
+änderungen in 27d:reattack im monomode nurnoch wenn always_reattack=true
+änderungen in 27c:tremolo/vibrato an samplingrate angepasst
+änderungen in 27: samp_rate von jack erhalten
+änderungen in 26: stimmenlimit implementiert, aber ungetestet!
+ bei verwerfen einer stimme kann sie schnell weg-
+ gefadet werden. auch das ist ungetestet!
+änderungen in 25: stereo wurde implementiert, aber nicht getestet!
+änderungen in 24: frameskip funktioniert
+änderungen in 23c:KSR und KSL werden eingelesen
+änderungen in 23b:KSR funktioniert
+änderungen in 23: KSL funktioniert
+änderungen in 22b:controllerreset hinzugefügt
+änderungen in 22: pitchbend hinzugefügt
+änderungen in 21: memcpy nur noch bei trivialen typen (int), sonst
+ copy. osc_t hat operator=. folge: deep-copy funzt
+änderungen in 20: memory-leaks (bei einem simplen 5-sec-testlauf
+ stolze 8kb!) entfernt. sollte jetzt leakfrei sein.
+änderungen in 19: reattack und portamento funktionieren
+änderungen in 18: code aufgeräumt: t entfernt, TO DOs entfernt
+änderungen in 17: envelopes können werte ändern
+änderungen in 16: controllerdefaults werden aus cfg gelesen
+ und in program_t gepackt
+änderungen in 15: jeder channel hat einen port
+bugfix in 14d:program_t hat nun einen deep-copy-=-operator
+änderungen in 14c:geänderte parameter werden gesichert und an
+ neue noten weitergegeben
+änderungen in 13: controller für bereits spielende noten funktioniert.
+ fehlt noch das der-nächsten-note-mitgeben
+änderungen in 12: programm-presets fertig, funktioniert.
+ außerdem: bug beim parser ausgebessert
+änderungen in 11: parsen und programme angefangen
+änderungen in 10: wie 09, aber mit (unbenutzter) Parser-klasse
+änderungen in 09: notes besitzen nun eigenen framecounter
+änderungen in 08: vibratoeffekt hinzugefügt
+änderungen in 07: tremoloeffekt hinzugefügt
+änderungen zu 06_opt3: alles arbeitet mit fixed_t (bitshifting)
+änderungen zu 06_opt2: envelope-generator arbeitet mit fixed_t, aber jetzt
+ mit bitshifting statt normalen divisionen
+änderungen zu 06_optimized: envelope-generator arbeitet mit fixed_t
+änderungen zu 06: floats statt doubles
+stand in 06: FM-synthese (und theoretisch AM) funktioniert, aber langsam
+ MIDI via jack und sound-out via jack funktioniert.
+
diff --git a/synth/Makefile b/synth/Makefile
new file mode 100644
index 0000000..3870262
--- /dev/null
+++ b/synth/Makefile
@@ -0,0 +1,33 @@
+CXX=g++
+CXXFLAGS=-Wall -g
+LDFLAGS=-lm `pkg-config --cflags --libs jack`
+
+OBJ=channel.o cli.o defines.o envelope.o filter.o globals.o jack.o load.o main.o note.o parser.o programs.o readwave.o util.o
+BIN=synth
+
+DEPENDFILE = .depend
+
+
+SRC = $(OBJ:%.o=%.cpp)
+
+all: $(BIN)
+
+
+$(BIN): $(OBJ)
+ $(CXX) $(CFLAGS) -o synth $(OBJ) $(LDFLAGS)
+
+
+dep: $(SRC)
+ $(CC) -MM $(SRC) > $(DEPENDFILE)
+
+-include $(DEPENDFILE)
+
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) -g -c $<
+
+.PHONY: clean
+
+clean:
+ rm -f $(OBJ) $(BIN)
+
diff --git a/synth/OPTIMIZATIONS b/synth/OPTIMIZATIONS
new file mode 100644
index 0000000..2dd003f
--- /dev/null
+++ b/synth/OPTIMIZATIONS
@@ -0,0 +1,20 @@
+Sinnlose Optimierungen
+ o if(foo.fm_strength!=0) ...: kein effekt, höchstens leichter anstieg!
+
+ Mögliche Optimierungen
+ o 10% filter ganz auf fixed_t umstellen?
+ o 5% envelope::get_level nur alle n frames arbeiten lassen, sonst cachen?
+ o 2% bei LFOs: bei jedem LFO-update die werte für env-max, freqfactor
+ und filter-offset aus orig berechnen
+ o 2% beim filter: evtl nur mit floats statt mit doubles rechnen?
+ o <2% in note::get_sample u.a.: pitch-bending effizienter lösen?
+ x 0% beim channel::get_sample: pro note immer mehrere samples auf
+ einmal holen (iterator braucht recht viel leistung)
+ wird von g++ automatisch wegoptimiert -> ok
+
+ Mögliche Bugs und ihre Lösung:
+ o frequenz wird nicht genau eingehalten: phase um
+ foo*WAVE_RES erhöhen, entsprechend wave[][bar] ändern.
+ ABER: im testfall um bis zu 15% langsamer
+
+
diff --git a/synth/README.developer b/synth/README.developer
new file mode 100644
index 0000000..c2ee3a2
--- /dev/null
+++ b/synth/README.developer
@@ -0,0 +1,28 @@
+Wenn neue Instrumentenparameter definiert werden:
+ o bei parser::parse : case 1:
+ o bei parameter_enum
+ o param_t:: und Note::set_param
+ o param_to_enum
+ o param_needs_index
+ anpassen.
+
+
+regeln: TODO für todos. DEBUG für debugging-outputs und -dinge.
+ IMPLEMENTME für sachen, die in ferner zukunft gemacht werden sollten
+ FINDMICH nur für marker, die unmittelbar gebraucht werden.
+
+Abweichungen von General MIDI:
+ controller 3 steuert always_reattack im polymode
+ controller 1 muss von der config definiert werden, sonst wird er ignoriert
+ controller 119 steuert quick-release-zeit im falle eines voice-limits.
+ 0 bedeutet: sofort abschalten
+
+
+oscillator.phase will overflow in t seconds, with t being:
+t=fixed_t.max_value / (max_possible_freq << SCALE)
+with fixed_t being a uint64, max_possible_freq being 12544 Hz,
+the highest freq possible with MIDI and SCALE being 20,
+t = 1402438300 sec = 44,47 years.
+--> phase will never overflow
+
+
diff --git a/synth/TODO b/synth/TODO
new file mode 100644
index 0000000..d507df0
--- /dev/null
+++ b/synth/TODO
@@ -0,0 +1,32 @@
+TODO für den synth
+ o notes compilieren und als .so-datei laden
+
+ o RAM aufräumen?
+
+ o jedes programm eigene LFOs?
+ o andere wellenformen bei LFOs?
+ o mehr wellen für wave[]
+
+ o parser: sehr redundante funktionen zusammenführen
+ o parser: direkt in result schreiben?
+
+ o attack und release ggf. auf niedrigen wert (<=0.01) initen, um
+ knackser zu vermeiden?
+
+ o chorus, reverb etc.
+
+ o konnte-nicht-verbinden-warnung weniger schlimm machen
+
+ o max_pitchbend per controller setzen?
+ o nur auf bestimmte channels reagieren
+
+ o diverse pedale (soft, sostenuto, halte, legato (?))
+
+ (o)programs on-the-fly ändern (n_osc ändern)
+ (o)lfo-maxima getrennt regeln. nää
+ (o)bei filter-envelopes: ksr/ksl? nää.
+ (o)resonanz-tremolo bei tiefpass? nää.
+
+
+TODO fürs CLI
+ x ...
diff --git a/synth/TODO.done b/synth/TODO.done
new file mode 100644
index 0000000..0cd6d16
--- /dev/null
+++ b/synth/TODO.done
@@ -0,0 +1,71 @@
+TODO für den synth
+ x knistern bei aktivem frameskip
+ x tremolo und vibrato: phase nurnoch ++
+ x rettack im monomode abschalten (sondern einfach nur freq ändern)
+ x bei freqänderung: aufpassen, ob nicht das programm auch geändert
+ wurde. wenn ja: delete && new
+ x note-limit
+ x stereo: pan/balance
+ x envelopes on-the-fly ändern
+ x programs laden
+ x beim note-limit: statt abzuschneiden ausblenden
+ x einstellmöglichkeiten via MIDI-controller
+ x grund-controller bearbeiten
+ x ein port pro channel
+ x controllern einen defaultwert pro programm mitgeben
+ x monophoner modus
+ x reattack
+ x alwaysreattack setzen können
+ x portamento testen
+ x memcheck-clean!
+ x memcpy durch copy() plus operator= ersetzen?
+ x pitch-bend
+ x controller-reset
+ x key scale level, key scale rate
+ x akkurates note-on
+ x frameskip
+ x globale config-datei, oder alle programs in einem verz. einlesen etc.
+ x auf dateifehler reagieren!
+ x sampler-"oscs", d.h. laden von wav-dateien, die sich sonst
+ wie oscs verhalten (fm möglich usw.)
+ x analoge synthese auf den output jeder note anwenden
+ x tiefpass via def.datei setzen: trem, env, res
+ x tiefpass via controller setzen
+ x osc-sync
+ x bei genügend xruns noten töten
+ x filter knackst
+ x bei self-mod mit faktor 1 (auch ohne filter): segfault
+ x bei starken vibratos: segfault, weil fm zu extrem wird
+ x bei bug.prog: auch ohne filter: knacksen bei den meisten noten (z.B. C)
+ x per velocity statt lautstärke andere params steuern
+ x frameskip so implementieren, dass bufsize irrelevant ist
+ x lfo_update_frames einstellbar machen
+ sollte durch frameskip dividiert werden
+ x tremolo- und vibrato-arrays sind mit mehreren MB zu groß!
+ ein wert pro sample ist unnötig. könnte auch rechenzeit in
+ calc_foo sparen, da seltener aufgerufen
+ x sample-and-hold -> fm_strength, -> freq, -> VCF
+ * bei osc-envelopes ggf. auch nur alle n frames neu setzen? [verschoben]
+ * filter optimieren? (arbeiten momentan mit floats) [verschoben]
+ verstehen, optimieren und dann profilen
+ x stimmt die stereo-implementierung? [ja]
+
+
+TODO fürs CLI
+ x filter_update_frames, lfo_update_frames in config, in sec (auch im CLI)
+ x max_port_time einstellen
+ x manuelle program -> datei - mappings
+ x automatische mappings ( xxxIGNORIERT.prg, xxx ist die programmnummer),
+ alle dateien eines verzeichnisses einlesen. bei nichtexistenz
+ auf normalen sinus zurückfallen
+ x konfigdateien lesen (inhalt wie CLI-optionen)
+ x vibrato- und tremolo-frequenzen einstellen
+ x cleanup-intervall setzen
+ x space-sicher machen
+ x fehlerbehandlung:
+ syntaxfehler sollten übergangen werden, sofern möglich
+ in parser: throw!, sonst: meckern und nächste zeile parsen
+ x automatisch an alle midi-outs hängen
+ x interface div-by-zero-sicher machen
+
+
diff --git a/synth/channel.cpp b/synth/channel.cpp
new file mode 100644
index 0000000..35ee375
--- /dev/null
+++ b/synth/channel.cpp
@@ -0,0 +1,338 @@
+#include "channel.h"
+
+#include "math.h"
+#include "globals.h"
+
+Channel::Channel()
+{
+ volume=ONE;
+ set_program(0);
+ curr_prg.controller[NO_CONT]=1;
+ quick_release=0;
+ always_reattack=false;
+ portamento_frames2=portamento_frames=0;
+ do_portamento=false;
+ pitchbend=ONE;
+ n_voices=0;
+
+ max_pitchbend=1.0;
+
+ set_balance(64);
+}
+
+Channel::~Channel()
+{
+ panic(); //deletes all notes and empties notes-list
+}
+
+void Channel::cleanup()
+{
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ if ((*it)->still_active()==false)
+ {
+ delete *it;
+ it=notes.erase(it);
+ }
+}
+
+fixed_t Channel::get_sample()
+{
+ fixed_t sum=0;
+
+ for (list<Note*>::iterator it=notes.begin(); it!=notes.end(); it++)
+ sum+=(*it)->get_sample();
+
+ return sum*volume >>SCALE;
+}
+
+void Channel::event(uint8_t a, uint8_t b, uint8_t c)
+{
+ switch(a & 0xF0)
+ {
+ case 0x80: note_off(b); break;
+ case 0x90: note_on(b,c); break;
+ case 0xA0: break; //IMPLEMENTME: polyphonic aftertouch (note, dynamic)
+ case 0xB0: set_controller(b,c); break;
+ case 0xC0: set_program(b); break;
+ case 0xD0: break; //IMPLEMENTME: monotonic aftertouch (dynamic)
+ case 0xE0: set_pitch_bend( ( (((b&0x7F) + ((c&0x7F)<<7)) - 8192) / 8192.0 ) * max_pitchbend ); break;
+ case 0xF0: break; //own controls/sysex (to be implemented) IMPLEMENTME
+ default: output_verbose("NOTE: got unknown command "+ IntToStrHex(a&0xF0) +", ignoring it\n");
+ ;
+ }
+}
+
+void Channel::note_off(int note)
+{
+ note_on(note,0);
+}
+
+void Channel::note_on(int note, int vel)
+{
+ list<Note*>::iterator it;
+ if (vel>0) //note on
+ {
+ if ( (n_voices==1) && (!notes.empty()) )
+ {
+ //no need to create a new note; reuse the existing
+ Note *n; //i'm lazy
+ n= *(notes.begin());
+
+ if (n->get_program() != program)
+ {
+ //if the program has changed, kill the previous note and
+ //create a new one
+ delete n;
+ notes.clear();
+
+ notes.push_back( new Note(note,(float)vel/128.0,
+ curr_prg,
+ portamento_frames,
+ pitchbend,
+ program) );
+
+ }
+ else //program did not change
+ {
+ //if not still active, don't do portamento
+ n->set_note(note,n->still_active());
+ n->set_vel((float)vel/128.0);
+ if (always_reattack || !n->still_active()) n->reattack();
+ //no need to push back. would become #1 instead of #1
+ }
+ }
+ else
+ {
+ bool neednewnote=true;
+ if (always_reattack)
+ {
+ for (it=notes.begin(); it!=notes.end(); it++)
+ if ( ((*it)->get_note()==note) && ((*it)->get_program()==program) )
+ {
+ neednewnote=false;
+ (*it)->reattack();
+ (*it)->set_vel((float)vel/128.0);
+ notes.push_back(*it); //reorder notes
+ notes.erase(it);
+ break;
+ }
+ }
+ if (neednewnote)
+ notes.push_back( new Note(note,(float)vel/128.0,
+ curr_prg,
+ portamento_frames,
+ pitchbend,
+ program) );
+ apply_voice_limit();
+ }
+ }
+ else //note off
+ {
+ for (it=notes.begin(); it!=notes.end(); it++)
+ if ((*it)->get_note()==note)
+ (*it)->release();
+ }
+
+}
+
+void Channel::set_n_voices(int val)
+{
+ n_voices=val;
+
+ if ((n_voices<=0) || (n_voices>=128))
+ n_voices=0; //unlimited
+
+ apply_voice_limit();
+}
+
+void Channel::apply_voice_limit()
+{
+ if (n_voices) //is a limit defined?
+ {
+ int diff=notes.size()-n_voices;
+ if (diff>0)
+ {
+ list<Note*>::iterator it=notes.begin();
+
+ if (quick_release)
+ for (int i=0;i<diff;i++)
+ {
+ (*it)->release_quickly(quick_release);
+ it++;
+ }
+ else
+ for (int i=0;i<diff;i++)
+ {
+ delete (*it);
+ it=notes.erase(it);
+ }
+ }
+ }
+}
+
+
+void Channel::set_controller(int con,int val)
+{
+ switch (con)
+ {
+ case 3: always_reattack=(val>=64);
+ case 5: set_portamento_time(val); break;
+ case 7: set_volume(val); break;
+ case 8: set_balance(val); break;
+ case 65: set_portamento(val); break;
+ case 119: set_quick_release(val);
+ case 120: panic(); break;
+ case 121: reset_controllers(); break;
+ case 123: release_all(); break;
+ case 126: set_n_voices(val); break;
+ case 127: set_n_voices(999); break;
+ default: set_user_controller(con,val); break;
+ }
+}
+
+void Channel::set_user_controller(int con, int val)
+{
+ curr_prg.controller[con]=val;
+ for (set<parameter_t>::iterator it=curr_prg.controller_affects[con].begin(); it!=curr_prg.controller_affects[con].end(); it++)
+ recalc_param(*it,curr_prg);
+}
+
+void Channel::recalc_param(const parameter_t &par, program_t &prg)
+{
+ fixed_t val=0;
+
+ list<term_t> *l;
+ l=&(prg.formula[par]);
+
+ for (list<term_t>::iterator it=l->begin(); it!=l->end(); it++)
+ val+=curr_prg.controller[it->c]*it->f;
+
+ if (val<0) val=0;
+
+ // now we have the final value of the formula in units of fixed_t
+ // in the range 0..+infinity
+
+ switch(par.par)
+ {
+ case SUSTAIN:
+ case FILTER_SUSTAIN: if (val>ONE) val=ONE; break;
+
+ case TREM_LFO:
+ case VIB_LFO:
+ case FILTER_TREM_LFO: val=val>>SCALE; if (val>=N_LFOS+1) val=N_LFOS+1 -1; break;
+
+ case TREMOLO:
+ case VIBRATO:
+ case FILTER_TREMOLO: val=val>>SCALE; if (val>=N_LFO_LEVELS) val=N_LFO_LEVELS-1; break;
+
+ case WAVEFORM: val=val>>SCALE; if (val>=N_WAVEFORMS) val=N_WAVEFORMS-1; break;
+
+ case FILTER_RESONANCE: if (val>ONE) val=ONE; break;
+
+ default: break;
+ }
+
+ // now we have the value clipped to the valid range. for stuff
+ // expecting real numbers, it's in units of fixed_t. for booleans
+ // it's zero or nonzero. for stuff expecting integers, like lfo,
+ // waveform etc it's in int (i.e., val/ONE is very small, while
+ // val is what we want)
+
+ for (list<Note*>::iterator it=notes.begin(); it!=notes.end(); it++)
+ (*it)->set_param(par, val);
+
+ curr_prg.set_param(par, val);
+}
+
+void Channel::reset_controllers()
+{
+ program_t *orig=&program_settings[program];
+
+ for (int i=0;i<128;i++)
+ set_user_controller(i,orig->controller[i]);
+}
+
+void Channel::set_quick_release(int val)
+//ranges from zero to one second.
+{
+ quick_release=samp_rate*val/128;
+}
+
+void Channel::set_volume(int val)
+{
+ volume=val*ONE/128;
+}
+
+void Channel::set_balance(int val)
+{
+#ifdef STEREO
+ balR=val/64.0;
+ balL=(128-val)/64.0;
+#endif
+}
+
+void Channel::set_portamento(int val)
+{
+ if (val>=64)
+ {
+ do_portamento=true;
+ set_real_portamento_frames();
+ }
+ else
+ {
+ do_portamento=false;
+ set_real_portamento_frames();
+ }
+}
+
+void Channel::set_portamento_time(int val)
+{
+ portamento_frames2=samp_rate*val*max_port_time_sec/128;
+ if (do_portamento)
+ set_real_portamento_frames();
+}
+
+void Channel::set_real_portamento_frames()
+{
+ if (do_portamento)
+ portamento_frames=portamento_frames2;
+ else
+ portamento_frames=0;
+
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ (*it)->set_portamento_frames(portamento_frames);
+}
+
+void Channel::panic()
+{
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end();)
+ {
+ delete *it;
+ it=notes.erase(it);
+ }
+}
+
+void Channel::release_all()
+{
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ (*it)->release();
+}
+
+void Channel::set_program(int prog)
+{
+ program=prog;
+ curr_prg=program_settings[program];
+}
+
+void Channel::set_pitch_bend(float val)
+{
+ pitchbend=pow(2.0,val/12.0)*ONE;
+
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ (*it)->set_pitchbend(pitchbend);
+}
diff --git a/synth/channel.h b/synth/channel.h
new file mode 100644
index 0000000..ead3a67
--- /dev/null
+++ b/synth/channel.h
@@ -0,0 +1,63 @@
+#ifndef __CHANNEL_H__
+#define __CHANNEL_H__
+
+
+#include <stdint.h>
+#include <list>
+
+#include "fixed.h"
+#include "programs.h"
+#include "note.h"
+#include "defines.h"
+#include "util.h"
+
+
+class Channel
+{
+ public:
+ Channel();
+ ~Channel();
+ fixed_t get_sample();
+ void event(uint8_t a, uint8_t b, uint8_t c);
+ void set_controller(int con,int val);
+ void set_program(int prog);
+ void set_pitch_bend(float val);
+ void note_on(int note, int vel);
+ void note_off(int note);
+ void cleanup();
+ void release_all();
+ void panic();
+ void set_real_portamento_frames();
+ void set_portamento_time(int val);
+ void set_portamento(int val);
+ void set_volume(int val);
+ void set_balance(int val);
+ void set_n_voices(int val);
+ void set_quick_release(int val);
+ void reset_controllers();
+
+ float balL, balR;
+ private:
+ void recalc_param(const parameter_t &par, program_t &prg);
+ void set_user_controller(int con, int val);
+ void apply_voice_limit();
+
+ fixed_t volume;
+ fixed_t portamento_frames, portamento_frames2;
+ int program;
+ program_t curr_prg;
+
+ fixed_t pitchbend;
+ float max_pitchbend;
+
+ std::list<Note*> notes;
+
+ bool always_reattack;
+ bool do_portamento;
+
+ int n_voices;
+ jack_nframes_t quick_release;
+
+};
+
+#endif
diff --git a/synth/cli.cpp b/synth/cli.cpp
new file mode 100644
index 0000000..376edc3
--- /dev/null
+++ b/synth/cli.cpp
@@ -0,0 +1,174 @@
+#include <iostream>
+#include <cstdlib>
+#include <getopt.h>
+
+#include "util.h"
+#include "globals.h"
+#include "load.h"
+
+using namespace std;
+
+void show_help()
+{
+ cout << "TODO: help text" << endl;
+}
+
+void show_version()
+{
+ cout << "TODO: softsynth version foo" << endl;
+}
+
+void parse_args(int argc, char** argv)
+{
+ static const struct option long_options[]={
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"verbose", no_argument, 0, 'v'},
+ {"quiet", no_argument, 0, 'q'},
+ {"fatal-warnings", no_argument, 0, 'F'},
+ {"frameskip", required_argument, 0, 'f'},
+ {"xruns", required_argument, 0, 'x'},
+ {"dir", required_argument, 0, 'd'},
+ {"directory", required_argument, 0, 'd'},
+ {"program", required_argument, 0, 'p'},
+ {"cleanup-interval", required_argument, 0, 'i'},
+ {"lfo0-freq", required_argument, 0, 400}, //FINDLFO
+ {"lfo1-freq", required_argument, 0, 401},
+ {"lfo2-freq", required_argument, 0, 402},
+ {"snh-freq", required_argument, 0, 304},
+ {"sample-and-hold-freq", required_argument, 0, 304},
+ {"conf", required_argument, 0, 'c'},
+ {"config", required_argument, 0, 'c'},
+ {"max-port", required_argument, 0, 303},
+ {"max-port-time", required_argument, 0, 303},
+ {"max-portamento-time", required_argument, 0, 303},
+ {"filter-update-freq", required_argument, 0, 305},
+ {"lfo-update-freq", required_argument, 0, 306},
+ {"no-connect-audio-out", no_argument, 0, 'a'},
+ {"no-connect-audio", no_argument, 0, 'a'},
+ {"dont-connect-audio-out", no_argument, 0, 'a'},
+ {"dont-connect-audio", no_argument, 0, 'a'},
+ {"no-connect-midi-in", no_argument, 0, 'm'},
+ {"no-connect-midi", no_argument, 0, 'm'},
+ {"dont-connect-midi-in", no_argument, 0, 'm'},
+ {"dont-connect-midi", no_argument, 0, 'm'},
+ 0 };
+
+ while (optind<argc)
+ {
+ int index=-1;
+ int result=getopt_long(argc,argv,"hVf:d:p:i:c:x:vqFam", long_options, &index);
+ if (result==-1) break;
+
+ switch (result)
+ {
+ case 'h': show_help(); exit(0); break;
+ case 'V': show_version(); exit(0); break;
+ case 'v': verbose=true; quiet=false; break;
+ case 'q': quiet=true; verbose=false; break;
+ case 'F': fatal_warnings=true; break;
+ case 'c': read_config(optarg); break;
+ case 'a': connect_audio=false; break;
+ case 'm': connect_midi=false; break;
+ case 'f': if (optarg)
+ {
+ frameskip=atoi(optarg);
+ if (frameskip<=0) frameskip=0;
+ }
+ break;
+ case 'x': if (optarg)
+ {
+ string stropt=optarg;
+ size_t pos=stropt.find(':');
+ if (pos==string::npos)
+ output_warning("expected 'n_xruns:time' in --xruns option, found no ':'");
+
+ xrun_n=atoi(stropt.substr(0,pos).c_str());
+ xrun_time=atof(stropt.substr(pos+1).c_str());
+
+ if (xrun_n<=0) xrun_n=0;
+ if (xrun_time<=0) xrun_time=0;
+ }
+ break;
+ case 'd': add_dir(optarg); break;
+ case 'p': {
+ string str=optarg;
+ size_t pos=str.find_first_of(":=,");
+ if (pos!=string::npos)
+ {
+ string numstr=str.substr(0,pos);
+ if (isnum(numstr))
+ {
+ int num=atoi(numstr.c_str());
+
+ if ((num>=0) && (num<=127))
+ {
+ if (programfile[num]=="")
+ programfile[num]=str.substr(pos+1);
+ else
+ output_note("NOTE: program #"+IntToStr(num)+" has already been defined. ignoring it...");
+ }
+ else
+ {
+ output_warning("WARNING: number out of range (0..127) in --program option.\n ignoring the option...");
+ }
+ }
+ else
+ {
+ output_warning("WARNING: not a number in --program option. ignoring the option...");
+ }
+ }
+ else
+ {
+ output_warning("WARNING: missing number in --program option. ignoring the option...");
+ }
+ }
+ break;
+ case 'i': if (isfloat(optarg))
+ cleanup_interval_sec=atof(optarg);
+ else
+ output_warning("WARNING: not a number in --interval option. ignoring it...");
+ break;
+ case 304: if (isfloat(optarg))
+ snh_freq_hz=atof(optarg);
+ else
+ output_warning("WARNING: not a number in --sample-and-hold-freq option. ignoring it...");
+ break;
+ case 400: //FINDLFO
+ case 401:
+ case 402:
+ if (isfloat(optarg))
+ lfo_freq_hz[result-400]=atof(optarg);
+ else
+ output_warning("WARNING: not a number in --lfoN-freq option. ignoring it...");
+ break;
+ case 303: if (isfloat(optarg))
+ max_port_time_sec=atof(optarg);
+ else
+ output_warning("WARNING: not a number in --max-portamento-time option. ignoring it...");
+ break;
+
+ case 305: if (isfloat(optarg))
+ if (atoi(optarg)<=0)
+ output_warning("WARNING: filter-update-freq must be positive. ignoring it...");
+ else
+ filter_update_freq_hz=atof(optarg);
+ else
+ output_warning("WARNING: not a number in --filter-update-freq option. ignoring it...");
+ break;
+ case 306: if (isfloat(optarg))
+ if (atoi(optarg)<=0)
+ output_warning("WARNING: lfo-update-freq must be positive. ignoring it...");
+ else
+ lfo_update_freq_hz=atof(optarg);
+ else
+ output_warning("WARNING: not a number in --lfo-update-freq option. ignoring it...");
+ break;
+
+ default: cout << "ERROR: invalid command line options. try the --help switch" << endl;
+ exit(1);
+ }
+ }
+
+
+}
diff --git a/synth/cli.h b/synth/cli.h
new file mode 100644
index 0000000..d1f7a2b
--- /dev/null
+++ b/synth/cli.h
@@ -0,0 +1,6 @@
+#ifndef __CLI_H__
+#define __CLI_H__
+
+void parse_args(int argc, char** argv);
+
+#endif
diff --git a/synth/defines.cpp b/synth/defines.cpp
new file mode 100644
index 0000000..5b94954
--- /dev/null
+++ b/synth/defines.cpp
@@ -0,0 +1,3 @@
+#include "defines.h"
+
+float LFO_FREQ_HZ[]=__LFO_FREQ_HZ;
diff --git a/synth/defines.h b/synth/defines.h
new file mode 100644
index 0000000..4a2e929
--- /dev/null
+++ b/synth/defines.h
@@ -0,0 +1,62 @@
+#ifndef __DEFINES_H__
+#define __DEFINES_H__
+
+
+#define XRUN_TIME 2.0
+#define XRUN_N 8
+
+#define CLEANUP_INTERVAL_SEC 1.0
+
+#define MAX_PORTAMENTO_TIME 2.0
+
+#define FILTER_UPDATE_FREQ_HZ 250
+#define LFO_UPDATE_FREQ_HZ 500
+
+//when changing this, also change code marked with FINDLFO!
+#define N_LFOS 3
+#define N_LFO_LEVELS 1024
+#define LFO_MAX 1
+extern float LFO_FREQ_HZ[];
+#define __LFO_FREQ_HZ {7.0, 5.0, 1.0}
+
+#if N_LFO_LEVELS <= 1
+ #error "N_LFO_LEVELS must be greater than one!"
+#endif
+
+#define SNH_FREQ_HZ 10
+#define SNH_LFO N_LFOS
+
+
+//init the oscillator phases to wave_res * PHASE_INIT
+//negative values are not allowed, zero will cause the program
+//to segfault if phase modulation is done, higher values will make
+//the probability for a segfault smaller (i.e., zero)
+//only decrease if you know what you're doing!
+#define PHASE_INIT 100
+
+
+#define MIDI_IN_NAME "midi_in"
+#define OUT_NAME "output"
+
+#define N_CHANNELS 16
+
+
+//#define STEREO
+#define FRAMESKIP
+
+#define VOL_FACTOR (1/20.0)
+
+
+
+#define PI 3.141592654
+
+
+
+
+
+#define WAVE_RES 44100
+#define N_WAVEFORMS 5
+
+#define NO_CONT 128
+
+#endif
diff --git a/synth/envelope.cpp b/synth/envelope.cpp
new file mode 100644
index 0000000..481b78f
--- /dev/null
+++ b/synth/envelope.cpp
@@ -0,0 +1,168 @@
+#include "envelope.h"
+
+Envelope::Envelope(jack_nframes_t a, jack_nframes_t d, fixed_t s, jack_nframes_t r, bool h)
+{
+ level=0;
+ t=0;
+ state=ATTACK;
+ max=ONE;
+
+ set_ratefactor(1.0);
+
+ set_attack(a);
+ set_decay(d);
+ set_sustain(s);
+ set_release(r);
+ set_hold(h);
+}
+
+Envelope::Envelope(env_settings_t s)
+{
+ level=0;
+ t=0;
+ state=ATTACK;
+ max=ONE;
+
+ set_ratefactor(1.0);
+
+ set_attack(s.attack);
+ set_decay(s.decay);
+ set_sustain(s.sustain);
+ set_release(s.release);
+ set_hold(s.hold);
+}
+
+void Envelope::set_ratefactor(double factor)
+{
+ ratefactor=ONE*factor;
+
+ set_attack(attack_orig);
+ set_decay(decay_orig);
+ set_release(release_orig);
+}
+
+void Envelope::set_attack(jack_nframes_t a)
+{
+ attack_orig=a;
+ attack=a*ratefactor >>SCALE;
+
+ if (state==ATTACK)
+ t=attack*level >>SCALE;
+}
+
+void Envelope::set_decay(jack_nframes_t d)
+{
+ decay_orig=d;
+ decay=d*ratefactor >>SCALE;
+
+ if ((state==DECAY) && (sustain!=ONE))
+ if (sustain<ONE) //to avoid a div. by zero
+ t=decay*(ONE-level)/(ONE-sustain);
+}
+
+void Envelope::set_sustain(fixed_t s)
+{
+ sustain=s;
+ sustain_orig=s;
+}
+
+void Envelope::set_release(jack_nframes_t r)
+{
+ release_orig=r;
+ release=r*ratefactor >>SCALE;
+
+ if (state==RELEASE)
+ if (sustain>0) //to avoid a div. by zero
+ t=release*(sustain-level)/sustain;
+}
+
+
+void Envelope::set_hold(bool h)
+{
+ hold=h;
+ if ((h==false) && (state==HOLD))
+ {
+ t=0;
+ state=RELEASE;
+ }
+}
+
+void Envelope::reattack()
+{
+ state=ATTACK;
+ t=attack*level >>SCALE;
+ sustain=sustain_orig;
+}
+
+void Envelope::release_key()
+{
+ if ((state!=RELEASE) && (state!=DONE))
+ {
+ t=0;
+ state=RELEASE;
+ sustain=level;
+ }
+}
+
+bool Envelope::still_active()
+{
+ return (state!=DONE);
+}
+
+fixed_t Envelope::get_level() //must be called each frame
+{
+ switch (state)
+ {
+ case ATTACK:
+ if (t>=attack)
+ {
+ level=max;
+ state=DECAY;
+ t=0;
+ }
+ else //will only happen, if t < attack. so attack will
+ { //always be greater than zero -> no div. by zero
+ level=max * t / attack ;
+ }
+ break;
+
+ case DECAY:
+ if (t>=decay)
+ {
+ level=max*sustain >>SCALE;
+ if (hold)
+ state=HOLD;
+ else
+ state=RELEASE;
+ t=0;
+ }
+ else //will only happen, if t < decay. so decay will
+ { //always be greater than zero -> no div. by zero
+ level=(ONE - (ONE-sustain)*t/decay)*max >>SCALE;
+ }
+ break;
+
+ case HOLD:
+ level=sustain*max >>SCALE;
+ break;
+
+ case RELEASE:
+ if (t>=release)
+ {
+ level=0;
+ state=DONE;
+ }
+ else //will only happen, if t < release. so release will
+ { //always be greater than zero -> no div. by zero
+ level=(sustain - sustain * t/release)*max >>SCALE;
+ }
+ break;
+
+ case DONE:
+ level=0;
+ break;
+ }
+
+ t++;
+ return level;
+}
diff --git a/synth/envelope.h b/synth/envelope.h
new file mode 100644
index 0000000..e0d3502
--- /dev/null
+++ b/synth/envelope.h
@@ -0,0 +1,65 @@
+#ifndef __ENVELOPE_H__
+#define __ENVELOPE_H__
+
+#include <jack/jack.h>
+
+#include "programs.h"
+#include "fixed.h"
+
+
+class Envelope
+{
+ public:
+ Envelope(jack_nframes_t a, jack_nframes_t d, fixed_t s, jack_nframes_t r, bool h);
+ Envelope(env_settings_t s);
+ void release_key();
+ void reattack();
+ fixed_t get_level();
+ bool still_active();
+ void set_hold(bool h);
+ void set_attack(jack_nframes_t a);
+ void set_decay(jack_nframes_t d);
+ void set_sustain(fixed_t s);
+ void set_release(jack_nframes_t r);
+ void set_max(fixed_t m)
+ {
+ max=m;
+ if (max>ONE) max=ONE;
+ else if (max<0) max=0;
+ }
+ void set_ratefactor(double factor);
+
+ bool get_hold() { return hold; }
+ jack_nframes_t get_attack() { return attack_orig; }
+ jack_nframes_t get_decay() { return decay_orig; }
+ fixed_t get_sustain() { return sustain; }
+ jack_nframes_t get_release() { return release_orig; }
+
+
+ private:
+ fixed_t max;
+ jack_nframes_t attack;
+ jack_nframes_t decay;
+ jack_nframes_t release;
+ jack_nframes_t attack_orig;
+ jack_nframes_t decay_orig;
+ jack_nframes_t release_orig;
+ jack_nframes_t rel_t;
+ fixed_t sustain;
+ fixed_t sustain_orig;
+ fixed_t level;
+ bool hold;
+ jack_nframes_t t;
+ fixed_t ratefactor;
+
+ enum
+ {
+ ATTACK,
+ DECAY,
+ HOLD,
+ RELEASE,
+ DONE
+ } state;
+};
+
+#endif
diff --git a/synth/filter.cpp b/synth/filter.cpp
new file mode 100644
index 0000000..0ed7778
--- /dev/null
+++ b/synth/filter.cpp
@@ -0,0 +1,59 @@
+#include "math.h"
+
+#include "filter.h"
+#include "defines.h"
+#include "globals.h"
+
+LowPassFilter::LowPassFilter()
+{
+ rate=samp_rate;
+ nyquist=rate/2;
+ reset();
+}
+
+void LowPassFilter::reset()
+{
+ d1 = d2 = d3 = d4 = 0;
+}
+
+void LowPassFilter::set_params(float fc, float res)
+{
+ // constrain cutoff
+#define SAFE 0.99f // filter is unstable _AT_ PI
+ if (fc>(nyquist*SAFE))
+ fc=nyquist*SAFE;
+ if (fc<10)
+ {fc = 10;/*d1=d2=d3=d4=0;*/}
+ float w = (fc/(float)rate); // cutoff freq [ 0 <= w <= 0.5 ]
+
+ // find final coeff values for end of this buffer
+ double k, k2, bh;
+ double r = 2*(1-res);
+ if(r==0.0) r = 0.001;
+ k=tan(w*PI);
+ k2 = k*k;
+ bh = 1 + (r*k) + k2;
+ a0 = a2 = double(k2/bh);
+ a1 = a0 * 2;
+ b1 = double(2*(k2-1)/-bh);
+ b2 = double((1-(r*k)+k2)/-bh);
+}
+
+void LowPassFilter::process_sample (fixed_t *smp)
+{
+ fixed_t x,y;
+ x = *smp;
+
+ // first 2nd-order unit
+ y = ( a0*x ) + d1;
+ d1 = d2 + ( (a1)*x ) + ( (b1)*y );
+ d2 = ( (a2)*x ) + ( (b2)*y );
+ x=y;
+ // and the second
+
+ y = ( a0*x ) + d3;
+ d3 = d4 + ( a1*x ) + ( b1*y );
+ d4 = ( a2*x ) + ( b2*y );
+
+ *smp = y;
+}
diff --git a/synth/filter.h b/synth/filter.h
new file mode 100644
index 0000000..10db5d7
--- /dev/null
+++ b/synth/filter.h
@@ -0,0 +1,27 @@
+#ifndef __FILTER_H__
+#define __FILTER_H__
+
+#include "fixed.h"
+
+/**
+ * A 24 dB/octave resonant low-pass filter.
+ **/
+class LowPassFilter
+{
+public:
+ LowPassFilter();
+
+ /**
+ * Reset the filter - clear anything in the delay units of the filter.
+ */
+ void reset();
+ void set_params(float fc, float res);
+ void process_sample(fixed_t* smp);
+private:
+ float rate;
+ float nyquist;
+ double d1, d2, d3, d4;
+ double a0, a1, a2, b1, b2;
+};
+
+#endif
diff --git a/synth/fixed.h b/synth/fixed.h
new file mode 100644
index 0000000..41c1b78
--- /dev/null
+++ b/synth/fixed.h
@@ -0,0 +1,10 @@
+#ifndef __FIXED_H__
+#define __FIXED_H__
+
+
+#define SCALE 20
+#define ONE ((fixed_t)(1<<SCALE))
+
+typedef long long int fixed_t;
+
+#endif
diff --git a/synth/globals.cpp b/synth/globals.cpp
new file mode 100644
index 0000000..5b256b3
--- /dev/null
+++ b/synth/globals.cpp
@@ -0,0 +1,60 @@
+#include "globals.h"
+
+fixed_t **lfo[N_LFOS];
+fixed_t *curr_lfo[N_LFOS+1];
+
+fixed_t wave[N_WAVEFORMS][WAVE_RES];
+
+fixed_t sample_and_hold[N_LFO_LEVELS];
+
+int sample_and_hold_frames=0;
+int lfo_res[N_LFOS];
+int lfo_phase[N_LFOS];
+float lfo_freq_hz[N_LFOS];
+
+bool verbose=false;
+bool fatal_warnings=false;
+bool quiet=false;
+
+bool connect_audio=true, connect_midi=true;
+
+
+float cleanup_interval_sec=0;
+float snh_freq_hz=0;
+float max_port_time_sec=0;
+
+float filter_update_freq_hz;
+float lfo_update_freq_hz;
+
+int filter_update_frames;
+int lfo_update_frames;
+
+float xrun_time=0;
+int xrun_n=0;
+
+#ifndef FRAMESKIP
+ int samp_rate;
+#else
+ int frameskip=-1;
+ int samp_rate;
+
+ jack_default_audio_sample_t outtemp[N_CHANNELS];
+ #ifdef STEREO
+ jack_default_audio_sample_t outtemp2[N_CHANNELS];
+ #endif
+
+ jack_nframes_t outtemp_nframes_left=0;
+#endif
+
+
+string programfile[128];
+
+
+
+program_t *program_settings;
+
+
+Channel *channel[N_CHANNELS];
+
+
+jack_nframes_t cleanup_interval; //in jack frames
diff --git a/synth/globals.h b/synth/globals.h
new file mode 100644
index 0000000..b7c9109
--- /dev/null
+++ b/synth/globals.h
@@ -0,0 +1,73 @@
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#include <jack/jack.h>
+
+#include <string>
+
+#include "programs.h"
+#include "channel.h"
+
+using namespace std;
+
+
+extern fixed_t **lfo[N_LFOS];
+extern fixed_t *curr_lfo[N_LFOS+1];
+
+extern fixed_t wave[N_WAVEFORMS][WAVE_RES];
+
+extern fixed_t sample_and_hold[N_LFO_LEVELS];
+
+extern int sample_and_hold_frames;
+extern int lfo_res[N_LFOS];
+extern int lfo_phase[N_LFOS];
+extern float lfo_freq_hz[N_LFOS];
+
+extern bool verbose;
+extern bool fatal_warnings;
+extern bool quiet;
+
+extern bool connect_audio, connect_midi;
+
+
+extern float cleanup_interval_sec;
+extern float snh_freq_hz;
+extern float max_port_time_sec;
+
+extern float filter_update_freq_hz;
+extern float lfo_update_freq_hz;
+
+extern int filter_update_frames;
+extern int lfo_update_frames;
+
+extern float xrun_time;
+extern int xrun_n;
+
+#ifndef FRAMESKIP
+ extern int samp_rate;
+#else
+ extern int frameskip;
+ extern int samp_rate;
+
+ extern jack_default_audio_sample_t outtemp[N_CHANNELS];
+ #ifdef STEREO
+ extern jack_default_audio_sample_t outtemp2[N_CHANNELS];
+ #endif
+
+ extern jack_nframes_t outtemp_nframes_left;
+#endif
+
+
+extern string programfile[128];
+
+
+
+extern program_t *program_settings;
+
+extern Channel *channel[N_CHANNELS];
+
+
+extern jack_nframes_t cleanup_interval; //in jack frames
+
+
+#endif
diff --git a/synth/jack.cpp b/synth/jack.cpp
new file mode 100644
index 0000000..093e9b5
--- /dev/null
+++ b/synth/jack.cpp
@@ -0,0 +1,467 @@
+#include <string>
+#include <list>
+#include <iostream>
+#include <cstdlib>
+#include <jack/jack.h>
+#include <jack/midiport.h>
+
+#include "defines.h"
+#include "globals.h"
+
+#include "jack.h"
+
+using namespace std;
+
+//#define DO_DEBUGGING_EVENTS
+
+jack_port_t *midi_in;
+jack_port_t *out_port[N_CHANNELS];
+#ifdef STEREO
+jack_port_t *out_port2[N_CHANNELS];
+#endif
+
+jack_client_t *jack_client = NULL;
+
+
+void maybe_calc_stuff() //TODO woandershinschieben? lfo.cpp oder so?
+{
+ static int lfocnt=0;
+ static int snhcnt=0;
+
+ if (lfocnt==0)
+ {
+ lfocnt=lfo_update_frames;
+
+ for (int i=0;i<N_LFOS;i++)
+ {
+ lfo_phase[i]=(lfo_phase[i]+1)%lfo_res[i];
+ curr_lfo[i]=lfo[i][lfo_phase[i]];
+ }
+ }
+
+ if (snhcnt==0)
+ {
+ snhcnt=sample_and_hold_frames;
+
+ //temp ranges between -ONE and ONE
+ fixed_t temp = (float(rand())/(RAND_MAX/2) - 1.0) * ONE;
+
+ for (int i=0;i<N_LFO_LEVELS;i++)
+ sample_and_hold[i]= temp*i/(N_LFO_LEVELS-1) + ONE;
+
+ curr_lfo[SNH_LFO]=sample_and_hold;
+ // could be moved to some init function, but looks clearer and
+ // does not eat up the cpu too much ;)
+ }
+
+ lfocnt--;
+ snhcnt--;
+}
+
+
+
+//connect to jack, init some stuff and get information
+void init_jack()
+{
+ jack_client = jack_client_open("flosoftsynth", JackNullOption, NULL);
+ if (jack_client == NULL)
+ throw string("Registering client failed");
+
+ if (jack_set_process_callback(jack_client, process_callback, 0))
+ throw string("Registering callback failed");
+
+ if (jack_set_xrun_callback(jack_client, xrun_callback, 0))
+ throw string("Registering xrun-callback failed");
+
+ midi_in = jack_port_register(jack_client, MIDI_IN_NAME,
+ JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
+
+ if (midi_in == NULL)
+ throw string ("Registering MIDI IN failed");
+
+ for (int i=0;i<N_CHANNELS;i++)
+ {
+ #ifndef STEREO
+ out_port[i]=jack_port_register(jack_client, (OUT_NAME+IntToStr(i)).c_str(),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (out_port[i]==NULL)
+ throw string ("Registering some output port failed");
+ #else
+ out_port[i]=jack_port_register(jack_client, (OUT_NAME+IntToStr(i)+"L").c_str(),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ out_port2[i]=jack_port_register(jack_client, (OUT_NAME+IntToStr(i)+"R").c_str(),
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+ if ((out_port[i]==NULL) || (out_port2[i]==NULL))
+ throw string ("Registering some output port failed");
+ #endif
+ }
+
+ samp_rate=jack_get_sample_rate(jack_client);
+}
+
+
+//activate client and connect ports
+void start_jack(bool connect_audio_out, bool connect_midi_in)
+{
+ const char **ports;
+
+ if (jack_activate(jack_client))
+ throw string("Activating client failed");
+
+ if (connect_audio_out)
+ {
+ if ((ports = jack_get_ports (jack_client, NULL, JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsPhysical|JackPortIsInput)) == NULL)
+ {
+ output_warning("WARNING: Could not find any physical playback ports. Leaving my ports\n"
+ " unconnected. You will not hear anything, but you can connect\n"
+ " them on your own. proceeding...");
+ }
+ else
+ {
+ #ifndef STEREO
+ int i=0;
+ while(ports[i]!=NULL)
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ if (jack_connect (jack_client, jack_port_name (out_port[j]), ports[i]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. proceeding...");
+ i++;
+ }
+ #else
+ if (ports[1]==NULL)
+ {
+ output_note("NOTE: could not find two output ports. connecting to one, making everything\n"
+ " mono. this is not fatal, proceeding...");
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ if (jack_connect (jack_client, jack_port_name (out_port[j]), ports[0]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. this is not fatal, proceeding...");
+
+ if (jack_connect (jack_client, jack_port_name (out_port2[j]), ports[0]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. this is not fatal, proceeding...");
+ }
+ }
+ else
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ if (jack_connect (jack_client, jack_port_name (out_port[j]), ports[0]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. proceeding...");
+
+ if (jack_connect (jack_client, jack_port_name (out_port2[j]), ports[1]))
+ output_warning("WARNING: could not connect some output port. this may or may not result\n"
+ " in being unable to produce sound. you can still connect them\n"
+ " manually. proceeding...");
+ }
+ }
+ #endif
+
+ free (ports);
+ }
+ }
+
+ if (connect_midi_in)
+ {
+ if ((ports = jack_get_ports (jack_client, NULL, JACK_DEFAULT_MIDI_TYPE,
+ JackPortIsOutput)) == NULL)
+ {
+ output_warning("WARNING: Could not find any MIDI OUT ports. Leaving my MIDI IN port\n"
+ " unconnected. I cannot do anything unless you connect it to\n"
+ " some MIDI OUT port. proceeding...");
+ }
+ else
+ {
+ int i=0;
+ while(ports[i]!=NULL)
+ {
+ if (jack_connect (jack_client, ports[i],jack_port_name(midi_in)))
+ output_warning("WARNING: could not connect some MIDI OUT to my MIDI IN. this may or may not\n"
+ " result in being unable to receive any notes. you can still connect\n"
+ " the port manually. proceeding...");
+ i++;
+ }
+ free(ports);
+ }
+ }
+}
+
+void exit_jack()
+{
+ jack_deactivate(jack_client);
+ jack_client_close(jack_client);
+
+ jack_client=NULL;
+}
+
+int xrun_callback(void *notused)
+{
+ static list<float> history;
+
+ list<float>::iterator it;
+
+ float now=float(jack_get_time())/1000000;
+
+ cout << "got an XRUN! if this happens too often, consider reducing CPU usage, for\n example by setting a voice limit or by quitting other programs"<<endl<<endl;
+
+ history.push_back(now);
+
+ //erase all entries older than xrun_time
+ it=history.begin();
+ while (it!=history.end())
+ {
+ if (*it < now-xrun_time)
+ it=history.erase(it);
+ else
+ break;
+ }
+
+ if (history.size() >= xrun_n)
+ {
+ cout << "PANIC -- TOO MANY XRUNs! killing all voices" << endl<<endl;
+ for (int i=0;i<N_CHANNELS;i++)
+ channel[i]->panic();
+
+ history.clear();
+ }
+
+ return 0;
+}
+
+#define IGNORE_MIDI_OFFSET
+int process_callback(jack_nframes_t nframes, void *notused)
+{
+ #ifdef DO_DEBUGGING_EVENTS
+ static jack_nframes_t tmp=0, tmp2=0;
+ #endif
+
+ static jack_nframes_t next_cleanup=0;
+
+ size_t curr_event=0, n_events, i, chan;
+ void *inport;
+
+ jack_default_audio_sample_t *outbuf[N_CHANNELS];
+ #ifdef STEREO
+ jack_default_audio_sample_t *outbuf2[N_CHANNELS];
+ #endif
+
+ jack_midi_event_t event;
+ jack_nframes_t lastframe;
+ lastframe=jack_last_frame_time(jack_client);
+
+ if (nframes <= 0) {
+ output_note ("NOTE: Process callback called with nframes = 0; bug in JACK?");
+ return 0;
+ }
+
+ for (i=0;i<N_CHANNELS;i++)
+ {
+ outbuf[i]=(jack_default_audio_sample_t*) jack_port_get_buffer(out_port[i], nframes);
+ #ifdef STEREO
+ outbuf2[i]=(jack_default_audio_sample_t*) jack_port_get_buffer(out_port2[i], nframes);
+ #endif
+
+ if ( (outbuf[i]==NULL)
+ #ifdef STEREO
+ || (outbuf2[i]==NULL)
+ #endif
+ )
+ {
+ output_warning("WARNING: jack_port_get_buffer failed, cannot output anything.");
+ return 0;
+ }
+ }
+
+ inport = jack_port_get_buffer(midi_in, nframes);
+ if (inport == NULL)
+ {
+ output_warning("WARNING: jack_port_get_buffer failed, cannot receive anything.");
+ return 0;
+ }
+
+ n_events = jack_midi_get_event_count(inport /*, nframes */);
+
+ //as long as there are some events left and getting one fails, get the next
+ while ((n_events) && (jack_midi_event_get(&event, inport, curr_event /*, nframes */)))
+ {
+ output_note("NOTE: lost a note :(");
+ n_events--;
+ curr_event++;
+ }
+
+ if (lastframe>=next_cleanup)
+ {
+ next_cleanup=lastframe+cleanup_interval;
+ for (i=0;i<N_CHANNELS;i++)
+ channel[i]->cleanup();
+ }
+#ifdef DO_DEBUGGING_EVENTS
+ if (tmp==0) //DEBUG !!!
+ {
+ tmp=lastframe;
+ channel[0]->set_controller(5,10);
+ channel[0]->set_controller(65,127);
+
+ channel[0]->event(0x90,80,64);
+
+// channel[0]->event(0x90,84,64);
+ }
+ else if (tmp2==0)
+ {
+ if (lastframe>tmp+44100*2)
+ {
+ tmp2=1;
+ cout << "BÄÄM" << endl;
+ channel[0]->event(0x90,84,64);
+ }
+ }
+ else if (tmp2==1)
+ {
+ if (lastframe>tmp+44100*4)
+ {
+ tmp2=2;
+ channel[0]->event(0x90,87,5);
+ channel[0]->set_controller(57, 127);
+ cout << "BÄÄM2" << endl;
+ }
+ }
+ else
+ {
+ if (lastframe>tmp+44100*10)
+ {
+ cout << "finished" << endl;
+ exit(0);
+ }
+ }
+#endif
+
+#ifdef FRAMESKIP
+ if (outtemp_nframes_left)
+ {
+ jack_nframes_t real_nframes;
+ if (outtemp_nframes_left > nframes)
+ {
+ real_nframes=nframes;
+ outtemp_nframes_left-=nframes;
+ }
+ else
+ {
+ real_nframes=outtemp_nframes_left;
+ outtemp_nframes_left=0;
+ }
+
+ for (i=0;i<real_nframes;i++)
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ outbuf[j][i]=outtemp[j];
+ #ifdef STEREO
+ outbuf2[j][i]=outtemp2[j];
+ #endif
+ }
+ }
+ }
+ else
+ i=0;
+
+ //begin where the above loop has stopped, at 0 if the loop wasn't
+ //executed
+ int upperbound=nframes-frameskip+1;
+ if (upperbound<0) upperbound=0;
+ for (i=i;i<upperbound;i+=frameskip)
+#else
+ for (i=0;i<nframes;i++)
+#endif
+ {
+ while ((n_events) && (i>=event.time))
+ {
+ output_verbose("processing event #"+IntToStr(curr_event)+" of "+IntToStr(n_events)+" events");
+ if (event.size > 3)
+ {
+ output_verbose(" Ignoring MIDI message longer than three bytes, probably a SysEx.");
+ }
+ else
+ {
+ chan=event.buffer[0] & 0x0F;
+ output_verbose(" channel="+IntToStr(chan)+", data is "+IntToStrHex(event.buffer[0])+" "+IntToStrHex(event.buffer[1])+" "+IntToStrHex(event.buffer[2]));
+
+ channel[chan]->event(event.buffer[0], event.buffer[1], event.buffer[2]);
+ }
+
+ n_events--;
+ curr_event++;
+
+ //as long as there are some events left and getting one fails, get the next
+ while ((n_events) && (jack_midi_event_get(&event, inport, curr_event /*, nframes */)))
+ {
+ output_note("NOTE: lost a note :(");
+ n_events--;
+ curr_event++;
+ }
+ }
+
+ maybe_calc_stuff();
+
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ #ifndef STEREO
+ outbuf[j][i]=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ #else
+ jack_default_audio_sample_t sample=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ outbuf[j][i]=channel[j]->balL*sample;
+ outbuf2[j][i]=channel[j]->balR*sample;
+ #endif // if the above changes, (1) must also change
+
+ #ifdef FRAMESKIP
+ for (size_t k=i+frameskip-1;k>i;k--)
+ {
+ outbuf[j][k]=outbuf[j][i];
+ #ifdef STEREO
+ outbuf2[j][k]=outbuf2[j][i];
+ #endif
+ }
+ #endif
+ }
+ }
+
+#ifdef FRAMESKIP
+ if (i!=nframes) // nicht aufgegangen?
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ { // (1)
+ #ifndef STEREO
+ outtemp[j]=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ #else
+ jack_default_audio_sample_t sample=jack_default_audio_sample_t(channel[j]->get_sample())/ONE*VOL_FACTOR;
+ outtemp[j]=channel[j]->balL*sample;
+ outtemp2[j]=channel[j]->balR*sample;
+ #endif
+ }
+
+ outtemp_nframes_left=frameskip-nframes+i;
+
+ for (i=i; i<nframes; i++)
+ {
+ for (int j=0;j<N_CHANNELS;j++)
+ {
+ outbuf[j][i]=outtemp[j];
+ #ifdef STEREO
+ outbuf2[j][i]=outtemp2[j];
+ #endif
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+
diff --git a/synth/jack.h b/synth/jack.h
new file mode 100644
index 0000000..c0b4baa
--- /dev/null
+++ b/synth/jack.h
@@ -0,0 +1,12 @@
+#ifndef __JACK_H__FLO
+#define __JACK_H__FLO
+
+#include <jack/jack.h>
+
+int process_callback(jack_nframes_t nframes, void *notused);
+int xrun_callback(void *notused);
+void init_jack();
+void start_jack(bool connect_audio_out=true, bool connect_midi_in=true);
+void exit_jack();
+
+#endif
diff --git a/synth/load.cpp b/synth/load.cpp
new file mode 100644
index 0000000..d88ec3e
--- /dev/null
+++ b/synth/load.cpp
@@ -0,0 +1,191 @@
+#include <string>
+#include <fstream>
+#include <cstdlib>
+#include <sys/types.h>
+#include <dirent.h>
+
+
+#include "util.h"
+#include "globals.h"
+
+
+using namespace std;
+
+void add_dir(string directory, bool complain=true)
+{
+ DIR *dir;
+ dir=opendir(directory.c_str());
+ if (dir)
+ {
+ string n, snum;
+ int num;
+ dirent *entry=NULL;
+
+ while ( (entry=readdir(dir)) != NULL )
+ {
+ n=entry->d_name;
+ if (fileext(n)=="prog")
+ {
+ snum=n.substr(0,3);
+ if (isnum(snum))
+ {
+ num=atoi(snum.c_str());
+ if ((num>=0) && (num<=127))
+ {
+ if (programfile[num]=="")
+ programfile[num]=n;
+ else
+ output_verbose ("NOTE: found two or more .prog files with same number. ignoring '"+n+"'");
+ }
+ else
+ {
+ output_note ("NOTE: found .prog file with invalid number, ignoring it... ('"+n+"')");
+ }
+ }
+ else
+ {
+ output_note ("NOTE: found .prog file which does not start with a number, ignoring it... ('"+n+"')");
+ }
+ }
+ }
+ closedir(dir);
+ }
+ else
+ {
+ if (complain)
+ output_warning("WARNING: could not open directory '"+directory+"'!\n"
+ " this is not fatal, ignoring and proceeding...");
+ }
+}
+
+void read_config(const char *cfg, bool complain=true)
+{
+ char buf[2000];
+
+ ifstream f;
+ f.open(cfg);
+
+ if (f.good())
+ {
+ string line;
+ while (!f.eof())
+ {
+ f.getline(buf,sizeof(buf)/sizeof(*buf)-1);
+ line=buf;
+ line=trim_spaces(line);
+
+ if ((line!="") && (line[0]!='#')) //ignore comments and empty lines
+ {
+ if (line.substr(0,string("include ").length())=="include ")
+ {
+ add_dir(trim_spaces(line.substr(string("include ").length())));
+ }
+ else
+ {
+ string var,val;
+ var=trim_spaces(extract_var(line));
+ val=trim_spaces(extract_val(line));
+
+ if (isnum(var)) //programmzuweisung
+ {
+ int num=atoi(var.c_str());
+ if ((num>=0) && (num<=127))
+ {
+ if (programfile[num]=="")
+ programfile[num]=val;
+ else
+ output_verbose ("NOTE: program #"+IntToStr(num)+" has already been defined. ignoring it...");
+ }
+ else
+ {
+ output_warning("WARNING: number out of range (0..127) in program assignment. ignoring it...");
+ }
+ }
+ else
+ {
+ float valf=atof(val.c_str());
+
+ if (var=="frameskip")
+ {
+ if (valf<0)
+ output_warning("WARNING: invalid value for '"+var+"' ("+val+"). ignoring it...");
+
+ if (frameskip==-1)
+ frameskip=valf;
+ else
+ output_verbose("NOTE: ignoring value for frameskip, another setting overrides this.");
+ }
+ else
+ {
+ if (valf<=0)
+ output_warning("WARNING: invalid value for '"+var+"' ("+val+"). ignoring it...");
+
+ if ((var=="snh_freq") || (var=="sample_and_hold_freq"))
+ {
+ if (snh_freq_hz==0)
+ snh_freq_hz=valf;
+ else
+ output_verbose("NOTE: ignoring value for sample_and_hold_freq, another setting overrides this.");
+ }
+ if (var=="lfo0_freq") //FINDLFO
+ {
+ if (lfo_freq_hz[0]==0)
+ lfo_freq_hz[0]=valf;
+ else
+ output_verbose("NOTE: ignoring value for "+var+", another setting overrides this.");
+ }
+ if (var=="lfo1_freq")
+ {
+ if (lfo_freq_hz[1]==0)
+ lfo_freq_hz[1]=valf;
+ else
+ output_verbose("NOTE: ignoring value for "+var+", another setting overrides this.");
+ }
+ if (var=="lfo2_freq")
+ {
+ if (lfo_freq_hz[2]==0)
+ lfo_freq_hz[2]=valf;
+ else
+ output_verbose("NOTE: ignoring value for "+var+", another setting overrides this.");
+ }
+ else if ((var=="cleanup-interval") || (var=="clean"))
+ {
+ if (cleanup_interval_sec==0)
+ cleanup_interval_sec=valf;
+ else
+ output_verbose("NOTE: ignoring value for cleanup-interval, another setting overrides this.");
+ }
+ else if ((var=="max_port") || (var=="max_port_time") || (var=="max_portamento_time"))
+ {
+ if (max_port_time_sec==0)
+ max_port_time_sec=valf;
+ else
+ output_verbose("NOTE: ignoring value for max-portamento-time, another setting overrides this.");
+ }
+ else if (var=="lfo_update_freq")
+ {
+ if (lfo_update_freq_hz==0)
+ lfo_update_freq_hz=valf;
+ else
+ output_verbose("NOTE: ignoring value for lfo_update_freq, another setting overrides this.");
+ }
+ else if (var=="filter_update_freq")
+ {
+ if (filter_update_freq_hz==0)
+ filter_update_freq_hz=valf;
+ else
+ output_verbose("NOTE: ignoring value for filter_update_freq, another setting overrides this.");
+ }
+ else
+ output_warning("WARNING: unknown variable '"+var+"'. ignoring it...");
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ output_warning("WARNING: could not open config file '"+string(cfg)+"'.\nignoring this file...");
+ }
+}
diff --git a/synth/load.h b/synth/load.h
new file mode 100644
index 0000000..9724e92
--- /dev/null
+++ b/synth/load.h
@@ -0,0 +1,11 @@
+#ifndef __LOAD_H__
+#define __LOAD_H__
+
+#include <string>
+
+using namespace std;
+
+void add_dir(string directory, bool complain=true);
+void read_config(const char *cfg, bool complain=true);
+
+#endif
diff --git a/synth/main.cpp b/synth/main.cpp
new file mode 100644
index 0000000..deebf06
--- /dev/null
+++ b/synth/main.cpp
@@ -0,0 +1,190 @@
+#include <string>
+#include <iostream>
+#include <cmath>
+#include <cstdlib>
+
+#include "jack.h"
+#include "load.h"
+#include "cli.h"
+#include "parser.h"
+#include "channel.h"
+#include "fixed.h"
+#include "programs.h"
+#include "defines.h"
+#include "globals.h"
+
+using namespace std;
+
+
+void cleanup();
+void dump_options();
+
+
+int main(int argc, char** argv)
+{
+ for (int i=0;i<N_LFOS;i++)
+ lfo_freq_hz[i]=0;
+
+ try
+ {
+ parse_args(argc, argv);
+
+ add_dir("~/.flosynth", false);
+ add_dir("/etc/flosynth", false);
+
+ if (cleanup_interval_sec<=0) cleanup_interval_sec=CLEANUP_INTERVAL_SEC;
+ for (int i=0;i<N_LFOS;i++)
+ if (lfo_freq_hz[i]<=0) lfo_freq_hz[i]=LFO_FREQ_HZ[i];
+
+ if (snh_freq_hz<=0) snh_freq_hz=SNH_FREQ_HZ;
+ if (frameskip<=-1) frameskip=0;
+ if (max_port_time_sec<=0) max_port_time_sec=MAX_PORTAMENTO_TIME;
+ if (filter_update_freq_hz<=0) filter_update_freq_hz=FILTER_UPDATE_FREQ_HZ;
+ if (lfo_update_freq_hz<=0) lfo_update_freq_hz=LFO_UPDATE_FREQ_HZ;
+ if (xrun_n<=0) xrun_n=XRUN_N;
+ if (xrun_time<=0) xrun_time=XRUN_TIME;
+
+ dump_options();
+
+ frameskip++; //because a value of 0 means using each frame,
+ //a value of 1 means using each 2nd frame and so on
+
+ init_jack();
+
+ //this calculation needs the real sampling rate. others don't
+ cleanup_interval=cleanup_interval_sec*samp_rate;
+
+
+ #ifdef FRAMESKIP
+ samp_rate/=frameskip;
+ #endif
+
+
+ filter_update_frames=samp_rate/filter_update_freq_hz;
+ lfo_update_frames=samp_rate/lfo_update_freq_hz;
+ if (filter_update_frames<1) filter_update_frames=1;
+ if (lfo_update_frames<1) lfo_update_frames=1;
+
+
+ int i,j;
+
+
+ program_t default_program;
+ init_default_program(default_program);
+
+ //two possible divisions by zero are avoided, because
+ //values <= 0 will make the program use the default
+ //(nonzero) values.
+ for (i=0;i<N_LFOS;i++)
+ lfo_res[i]=samp_rate/lfo_freq_hz[i]/lfo_update_frames;
+
+ sample_and_hold_frames=samp_rate/snh_freq_hz;
+
+ for (i=0;i<N_LFOS;i++)
+ {
+ lfo[i]=new fixed_t* [lfo_res[i]];
+ for (j=0;j<lfo_res[i];j++)
+ lfo[i][j]=new fixed_t [N_LFO_LEVELS];
+ }
+
+ for (i=0;i<N_LFOS;i++)
+ for (j=0;j<lfo_res[i];j++)
+ {
+ float temp=sin(j*2.0*3.141592654/lfo_res[i]);
+ for (int k=0;k<N_LFO_LEVELS;k++)
+ lfo[i][j][k]= (1.0 + temp*(float(LFO_MAX)*k/N_LFO_LEVELS)) * ONE;
+ }
+
+ Parser parser;
+ program_settings=new program_t[128];
+
+ for (i=0;i<128;i++)
+ {
+ if (programfile[i]!="")
+ {
+ try
+ {
+ parser.parse(programfile[i]);
+ program_settings[i]=parser.get_results();
+ }
+ catch (string err)
+ {
+ output_warning("WARNING: error parsing '"+programfile[i]+"': "+err+"\n"
+ " this is not fatal, but the program has NOT been loaded! defaulting to a\n"
+ " simple program and going on...");
+ program_settings[i]=default_program;
+ }
+ }
+ else
+ {
+ program_settings[i]=default_program;
+ }
+ }
+
+
+ for (i=0;i<WAVE_RES;i++)
+ {
+ wave[0][i]=sin(i*2.0*3.141592654/WAVE_RES)*ONE;
+ wave[1][i]=abs(wave[0][i]);
+ wave[2][i]=(wave[0][i]>=0) ? wave[0][i] : 0;
+ wave[3][i]=(i<=WAVE_RES/4) ? wave[0][i] : 0;
+ wave[4][i]=(i<WAVE_RES/2) ? ONE : -ONE;
+ }
+
+ for (int i=0;i<N_CHANNELS;i++)
+ channel[i]=new Channel;
+
+ srand (time(NULL));
+
+ start_jack(connect_audio, connect_midi);
+
+ char tmp[10];
+ gets(tmp);
+ cout << "end."<< endl;
+
+ cleanup();
+ }
+ catch(string err)
+ {
+ cout << endl<<endl<< "FATAL: caught an exception: "<<endl<<err<<endl<<"exiting..." << endl;
+ }
+/* catch(...)
+ {
+ cout << "FATAL: caught an unknown exception... exiting..." << endl;
+ }*/
+ return 0;
+}
+
+void cleanup()
+{
+ exit_jack();
+
+ for (int i=0;i<N_CHANNELS;i++)
+ {
+ delete channel[i];
+ channel[i]=NULL;
+ }
+
+ delete [] program_settings;
+}
+
+void dump_options()
+{
+ for (int i=0;i<128;i++)
+ if (programfile[i]!="")
+ cout << "program #"<<i<<":\t'"<<programfile[i]<<"'"<<endl;
+
+ cout << endl;
+
+ cout << "frameskip:\t\t"<<frameskip<<endl;
+ cout << "cleanup-interval:\t"<<cleanup_interval_sec<<endl;
+ for (int i=0;i<N_LFOS;i++)
+ cout << "lfo"<<i<<" freq:\t\t"<<lfo_freq_hz[i]<<endl;
+
+ cout << "sample and hold freq:\t"<<snh_freq_hz<<endl;
+ cout << "max portamento time:\t"<<max_port_time_sec<<endl;
+ cout << "xrun n/time:\t\t"<<xrun_n<<"/"<<xrun_time<<"s"<<endl;
+ cout << "lfo update freq:\t"<<lfo_update_freq_hz<<endl;
+ cout << "filter update freq:\t"<<filter_update_freq_hz<<endl;
+
+}
diff --git a/synth/note.cpp b/synth/note.cpp
new file mode 100644
index 0000000..87f1806
--- /dev/null
+++ b/synth/note.cpp
@@ -0,0 +1,437 @@
+#include <string>
+#include <cmath>
+
+#include "note.h"
+#include "globals.h"
+#include "defines.h"
+
+using namespace std;
+
+//this function returns the smallest phase_init possible for a
+//given custom_wave which is greater or equal than PHASE_INIT
+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);
+}
+
+
+Note::Note(int n, float v, program_t &prg, jack_nframes_t pf, fixed_t pb, int prg_no)
+{
+ curr_prg=&prg;
+
+ n_oscillators=prg.n_osc;
+
+
+ pfactor.out=new fixed_t [n_oscillators];
+ pfactor.fm=new fixed_t* [n_oscillators];
+ for (int i=0;i<n_oscillators;i++)
+ pfactor.fm[i]=new fixed_t [n_oscillators];
+
+ oscval=new fixed_t[n_oscillators];
+ old_oscval=new fixed_t[n_oscillators];
+
+ for (int i=0;i<n_oscillators;i++)
+ oscval[i]=old_oscval[i]=0;
+
+ envelope=new Envelope*[n_oscillators];
+
+ for (int i=0;i<n_oscillators;i++)
+ envelope[i]=new Envelope(prg.env_settings[i]);
+
+ oscillator=new oscillator_t[n_oscillators];
+ orig.oscillator=new oscillator_t[n_oscillators];
+ copy(&prg.osc_settings[0],&prg.osc_settings[n_oscillators],oscillator);
+ copy(&prg.osc_settings[0],&prg.osc_settings[n_oscillators],orig.oscillator);
+
+
+ //initalize oscillator.phase to multiples of their wave resolution
+ //this has the following effect: the actual phase, i.e. the index
+ //in the wave-array (wave[phase]) doesn't change, because
+ // (n * wave_res) % wave_res is always zero.
+ //however, if doing phase modulation, it's very unlikely now that
+ //phase ever becomes negative (which would cause the program to
+ //segfault, or at least to produce noise). this saves an additional
+ //(slow) sanity check for the phase.
+ for (int i=0;i<n_oscillators;i++)
+ {
+ if (oscillator[i].custom_wave)
+ oscillator[i].phase=init_custom_osc_phase(oscillator[i].custom_wave->wave_len, oscillator[i].custom_wave->samp_rate);
+ else
+ oscillator[i].phase=ONE * PHASE_INIT;
+ }
+
+
+ portamento_frames=0;
+ set_portamento_frames(pf);
+
+ set_note(n);
+ freq=dest_freq;
+ set_vel(v);
+ do_ksl();
+
+ pitchbend=pb;
+
+ program=prg_no;
+
+ filter_params=prg.filter_settings;
+ orig.filter_params=prg.filter_settings;
+
+ if (filter_params.enabled)
+ {
+ filter_envelope=new Envelope(
+ filter_params.env_settings.attack,
+ filter_params.env_settings.decay,
+ filter_params.env_settings.sustain,
+ filter_params.env_settings.release,
+ filter_params.env_settings.hold );
+
+ filter_update_counter=filter_update_frames;
+ }
+
+ sync_factor=prg.sync_factor;
+ sync_phase=0;
+}
+
+Note::~Note()
+{
+ int i;
+
+ for (i=0;i<n_oscillators;i++)
+ {
+ delete [] oscillator[i].fm_strength;
+ delete envelope[i];
+
+ delete [] pfactor.fm[i];
+ }
+
+ delete [] oscillator;
+ delete [] envelope;
+
+ delete [] oscval;
+ delete [] old_oscval;
+
+ delete [] pfactor.out;
+ delete [] pfactor.fm;
+
+}
+
+void Note::recalc_factors()
+{
+ pfactor.filter_env=calc_pfactor(curr_prg->pfactor.filter_env, vel);
+ pfactor.filter_res=calc_pfactor(curr_prg->pfactor.filter_res, vel);
+ pfactor.filter_offset=calc_pfactor(curr_prg->pfactor.filter_offset, vel);
+
+ for (int i=0;i<n_oscillators;i++)
+ {
+ pfactor.out[i]=calc_pfactor(curr_prg->pfactor.out[i], vel);
+
+ for (int j=0;j<n_oscillators;j++)
+ pfactor.fm[i][j]=calc_pfactor(curr_prg->pfactor.fm[i][j], vel);
+ }
+}
+
+void Note::apply_pfactor()
+{
+ //apply pfactor to all necessary parameters
+ for (int i=0;i<n_oscillators;i++)
+ {
+ oscillator[i].output=orig.oscillator[i].output*pfactor.out[i] >>SCALE;
+
+ for (int j=0;j<n_oscillators;j++)
+ oscillator[i].fm_strength[j]=orig.oscillator[i].fm_strength[j]*pfactor.fm[i][j] >>SCALE;
+ }
+ filter_params.env_amount=orig.filter_params.env_amount*pfactor.filter_env /ONE;
+ filter_params.freqfactor_offset=orig.filter_params.freqfactor_offset*pfactor.filter_offset /ONE;
+ filter_params.resonance=orig.filter_params.resonance*pfactor.filter_res /ONE;
+}
+
+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");
+
+ }
+}
+
+bool Note::still_active()
+{
+ for (int i=0; i<n_oscillators; i++)
+ if ((oscillator[i].output>0) && (envelope[i]->still_active()))
+ return true;
+
+ return false;
+}
+
+
+//this function must still work properly if called multiple times
+//when called a second time, there shall be no effect
+void Note::release_quickly(jack_nframes_t maxt)
+{
+ for (int i=0;i<n_oscillators;i++)
+ {
+ if (envelope[i]->get_release() > maxt)
+ envelope[i]->set_release(maxt);
+
+ envelope[i]->release_key();
+
+ // i don't release the filter-env because lacking to do so
+ // does not generate a hearable difference (or would you hear
+ // when in the last half second a tone is filtered or not?)
+ }
+}
+
+void Note::release()
+{
+ for (int i=0;i<n_oscillators;i++)
+ envelope[i]->release_key();
+
+ if (filter_params.enabled)
+ filter_envelope->release_key();
+}
+
+void Note::reattack()
+{
+ for (int i=0;i<n_oscillators;i++)
+ envelope[i]->reattack();
+}
+
+void Note::set_pitchbend(fixed_t pb)
+{
+ pitchbend=pb;
+}
+
+void Note::set_freq(float f)
+{
+ old_freq=freq;
+ dest_freq=f*ONE;
+ portamento_t=0;
+
+ do_ksr();
+}
+
+void Note::set_freq(float f, bool do_port)
+{
+ set_freq(f);
+
+ if (!do_port)
+ old_freq=dest_freq;
+}
+
+void Note::set_note(int n)
+{
+ note=n;
+ set_freq(440.0*pow(2.0,(float)(n-69)/12.0));
+}
+
+void Note::set_note(int n, bool do_port)
+{
+ note=n;
+ set_freq(440.0*pow(2.0,(float)(n-69)/12.0), do_port);
+}
+
+int Note::get_note()
+{
+ return note;
+}
+
+void Note::set_vel(float v)
+{
+ vel=v*ONE;
+
+ recalc_factors();
+ apply_pfactor();
+}
+
+void Note::do_ksl()
+{ //osc.ksl is in Bel/octave (i.e. dB/10)
+ //if ksl=1, this means that for each octave the loudness
+ //decreases by half
+ for (int i=0;i<n_oscillators;i++)
+ {
+ if (oscillator[i].ksl==0)
+ envelope[i]->set_max(ONE);
+ else
+ envelope[i]->set_max( fixed_t(double(ONE) / pow(freq>>SCALE, oscillator[i].ksl)) );
+ }
+}
+
+void Note::do_ksr()
+{
+ for (int i=0;i<n_oscillators;i++)
+ envelope[i]->set_ratefactor(1.0 / pow(freq>>SCALE, oscillator[i].ksr));
+}
+
+void Note::set_portamento_frames(jack_nframes_t t)
+{
+ portamento_frames=t;
+ portamento_t=0;
+}
+
+fixed_t Note::get_sample()
+{
+ if (freq!=dest_freq)
+ {
+ // the div.by.zero if p_frames=0 is avoided because then the
+ // if-condition below is always true
+ if (portamento_t>=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;
+
+ fixed_t fm=0;
+ fixed_t out=0;
+
+ int i,j;
+
+ if (sync_factor)
+ {
+ sync_phase+=(actual_freq*sync_factor/samp_rate) >> SCALE;
+
+ if (sync_phase >= ONE)
+ {
+ sync_phase-=ONE;
+
+ for (i=0;i<n_oscillators;i++)
+ if (oscillator[i].sync)
+ {
+ if (oscillator[i].custom_wave)
+ oscillator[i].phase=init_custom_osc_phase(oscillator[i].custom_wave->wave_len, oscillator[i].custom_wave->samp_rate);
+ else
+ oscillator[i].phase=ONE * PHASE_INIT;
+ }
+ }
+ }
+
+ for (i=0;i<n_oscillators;i++)
+ {
+ fm=0;
+ oscval[i]=0;
+
+ for (j=0;j<n_oscillators;j++)
+ if (oscillator[i].fm_strength[j]!=0) //osc_j affects osc_i (FM)
+ fm+=(old_oscval[j]*oscillator[i].fm_strength[j])>>SCALE;
+
+ //phase increases in one second, i.e. in samp_rate frames, by the osc's freq
+ if (oscillator[i].vibrato_depth!=0)
+ oscillator[i].phase+=( (curr_lfo[oscillator[i].vibrato_lfo][oscillator[i].vibrato_depth]*actual_freq >>SCALE)*oscillator[i].factor/samp_rate)>>SCALE;
+ else
+ oscillator[i].phase+=(actual_freq*oscillator[i].factor/samp_rate)>>SCALE;
+
+ if (oscillator[i].custom_wave)
+ {
+ //sampler
+ custom_wave_t *cw=oscillator[i].custom_wave;
+ oscval[i]=cw->wave[ ((oscillator[i].phase + fm) * cw->samp_rate >>(2*SCALE)) % cw->wave_len ] * envelope[i]->get_level() >> (SCALE);
+ }
+ else
+ {
+ //normal oscillator
+ oscval[i]=wave[oscillator[i].waveform][ ((oscillator[i].phase + fm) * WAVE_RES >>SCALE) % WAVE_RES ] * envelope[i]->get_level() >> (SCALE);
+ }
+
+ if (oscillator[i].tremolo_depth!=0)
+ oscval[i]=oscval[i]* curr_lfo[oscillator[i].tremolo_lfo][oscillator[i].tremolo_depth] >> SCALE;
+
+ if (oscillator[i].output!=0)
+ out+=oscillator[i].output*oscval[i] >>SCALE;
+ }
+
+ if (filter_params.enabled)
+ {
+ filter_update_counter++;
+ if (filter_update_counter>=filter_update_frames)
+ {
+ filter_update_counter=0;
+
+ float cutoff= float(actual_freq)/ONE *
+ float(curr_lfo[filter_params.trem_lfo][filter_params.trem_strength])/ONE *
+ ( filter_params.freqfactor_offset + filter_envelope->get_level() * filter_params.env_amount / float(ONE) );
+ filter.set_params( cutoff, filter_params.resonance );
+ }
+
+ fixed_t tmp=out;
+ filter.process_sample(&tmp);
+ return tmp;
+ }
+ else
+ {
+ return out;
+ }
+}
diff --git a/synth/note.h b/synth/note.h
new file mode 100644
index 0000000..5035bd2
--- /dev/null
+++ b/synth/note.h
@@ -0,0 +1,80 @@
+#ifndef __NOTE_H__
+#define __NOTE_H__
+
+#include <jack/jack.h>
+
+#include "programs.h"
+#include "envelope.h"
+#include "fixed.h"
+#include "filter.h"
+
+class Note
+{
+ public:
+ Note(int n, float v,program_t &prg, jack_nframes_t pf, fixed_t pb, int prg_no);
+ ~Note();
+ fixed_t get_sample();
+ int get_note();
+ void set_note(int n);
+ void set_note(int n, bool do_port);
+ void set_freq(float f);
+ void set_freq(float f, bool do_port);
+ void set_pitchbend(fixed_t pb);
+ void set_vel(float v);
+ void set_portamento_frames(jack_nframes_t f);
+ void release_quickly(jack_nframes_t maxt);
+ void release();
+ void reattack();
+ bool still_active();
+ void set_param(const parameter_t &p, fixed_t v);
+ int get_program(){return program;}
+
+ private:
+ void do_ksl();
+ void do_ksr();
+
+ void recalc_factors();
+ void apply_pfactor();
+
+ Envelope **envelope;
+ fixed_t freq, dest_freq, old_freq;
+ fixed_t vel;
+ jack_nframes_t portamento_t, portamento_frames;
+
+ fixed_t *oscval;
+ fixed_t *old_oscval;
+ int n_oscillators;
+ oscillator_t *oscillator;
+
+ fixed_t sync_factor;
+ fixed_t sync_phase;
+
+ pfactor_value_t pfactor;
+
+ int note;
+ int program;
+ program_t *curr_prg;
+
+ fixed_t pitchbend;
+
+ LowPassFilter filter;
+ Envelope *filter_envelope;
+ filter_params_t filter_params;
+ int filter_update_counter;
+
+ struct
+ {
+ oscillator_t *oscillator;
+ filter_params_t filter_params;
+ } orig;
+
+/* *einstellungen: oszillatoren, deren lautstärke etc.
+ * note
+ * lautstärke
+ * *pitchbend
+ * *portamento time
+ */
+};
+
+
+#endif
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;
+}
diff --git a/synth/parser.h b/synth/parser.h
new file mode 100644
index 0000000..192226e
--- /dev/null
+++ b/synth/parser.h
@@ -0,0 +1,47 @@
+#ifndef __PARSER_H__
+#define __PARSET_H__
+
+
+#include <set>
+#include <map>
+#include <list>
+#include <string>
+
+#include "fixed.h"
+#include "programs.h"
+
+using namespace std;
+
+class Parser
+{
+ public:
+ Parser();
+ ~Parser();
+ void parse(string fn);
+ program_t get_results() const;
+
+ private:
+ void init_stuff();
+ void uninit_stuff();
+ static string extract_array_name(string s);
+ static list<string> extract_terms(string s);
+ static list<string> extract_factors(string s);
+ static list<term_t> extract_formula(string s);
+ static param_factor_t parse_pfactor(string s);
+ static int extract_array_index(string s, int dim);
+
+ int n_osc;
+ oscillator_t *osc;
+ env_settings_t *env;
+ set<parameter_t> affect[128];
+ map< parameter_t, list<term_t> > formula;
+ int controller_default[128];
+ filter_params_t filter;
+
+ pfactor_formula_t pfactor;
+
+ fixed_t sync_factor;
+};
+
+
+#endif
diff --git a/synth/programs.cpp b/synth/programs.cpp
new file mode 100644
index 0000000..be82fed
--- /dev/null
+++ b/synth/programs.cpp
@@ -0,0 +1,224 @@
+#include <string>
+#include <cstring>
+
+#include "programs.h"
+#include "globals.h"
+
+using namespace std;
+
+oscillator_t::oscillator_t()
+{
+ phase=0;
+ fm_strength=NULL;
+
+ ksl=0;
+ ksr=0;
+
+ sync=false;
+
+ custom_wave=NULL;
+}
+
+oscillator_t& oscillator_t::operator=(const oscillator_t &that)
+{
+ if (this!=&that)
+ {
+ if (this->fm_strength)
+ delete [] this->fm_strength;
+
+ memcpy(this, &that, sizeof(*this));
+
+ this->fm_strength=new fixed_t[n_osc];
+ memcpy(this->fm_strength, that.fm_strength, sizeof(*that.fm_strength)*n_osc);
+
+ this->custom_wave=that.custom_wave;
+
+ return *this;
+ }
+ else
+ return *this;
+}
+
+program_t::program_t()
+{
+ osc_settings=NULL;
+ env_settings=NULL;
+ filter_settings.enabled=false;
+ sync_factor=0;
+ n_osc=0;
+
+ pfactor.fm=NULL;
+ pfactor.out=NULL;
+}
+
+program_t::~program_t()
+{
+ cleanup();
+}
+
+void program_t::cleanup()
+{
+ if (osc_settings)
+ {
+ for (unsigned int i=0;i<n_osc;i++)
+ delete [] osc_settings[i].fm_strength;
+ delete [] osc_settings;
+ }
+ if (env_settings)
+ delete [] env_settings;
+
+ if (pfactor.out)
+ delete [] pfactor.out;
+ if (pfactor.fm)
+ {
+ for (unsigned int i=0;i<n_osc;i++)
+ delete [] pfactor.fm[i];
+ delete [] pfactor.fm;
+ }
+}
+
+program_t& program_t::operator=(const program_t &that)
+{
+ if (this!=&that)
+ {
+ unsigned int i;
+
+ this->cleanup();
+
+ for (i=0;i<(sizeof(controller_affects)/sizeof(*controller_affects));i++)
+ this->controller_affects[i]=that.controller_affects[i];
+ this->formula=that.formula;
+ this->n_osc=that.n_osc;
+
+ this->osc_settings=new oscillator_t[n_osc];
+ this->env_settings=new env_settings_t[n_osc];
+
+ memcpy(this->controller, that.controller, sizeof(that.controller));
+ copy(&that.osc_settings[0], &that.osc_settings[n_osc], this->osc_settings);
+ copy(&that.env_settings[0], &that.env_settings[n_osc], this->env_settings);
+
+ this->filter_settings=that.filter_settings;
+
+ this->sync_factor=that.sync_factor;
+
+
+
+ this->pfactor=that.pfactor;
+
+ this->pfactor.out=new param_factor_t [n_osc];
+ memcpy(this->pfactor.out, that.pfactor.out, sizeof(param_factor_t)*n_osc);
+
+ this->pfactor.fm=new param_factor_t* [n_osc];
+ for (i=0;i<n_osc;i++)
+ {
+ this->pfactor.fm[i]=new param_factor_t [n_osc];
+ memcpy(this->pfactor.fm[i], that.pfactor.fm[i], sizeof(param_factor_t)*n_osc);
+ }
+
+ return *this;
+ }
+ else
+ return *this;
+}
+
+void program_t::set_param(const parameter_t &p, fixed_t v) //ACHTUNG:
+{ //wenn das verändert wird, muss auch Note::set_param verändert werden!
+ switch(p.par)
+ {
+ case ATTACK: env_settings[p.osc].attack=v*samp_rate >>SCALE; break;
+ case DECAY: env_settings[p.osc].decay=v*samp_rate >>SCALE; break;
+ case SUSTAIN: env_settings[p.osc].sustain=v; break;
+ case RELEASE: env_settings[p.osc].release=v*samp_rate >>SCALE; break;
+ case HOLD: env_settings[p.osc].hold=(v!=0); break;
+
+ case KSR: osc_settings[p.osc].ksr=float(v)/ONE; break;
+ case KSL: osc_settings[p.osc].ksl=float(v)/ONE; break;
+
+ case FACTOR: osc_settings[p.osc].factor=v; break;
+ case MODULATION: osc_settings[p.osc].fm_strength[p.index]=v; break;
+ case OUTPUT: osc_settings[p.osc].output=v; break;
+ case TREMOLO: osc_settings[p.osc].tremolo_depth=v; break;
+ case TREM_LFO: osc_settings[p.osc].tremolo_lfo=v; break;
+ case VIBRATO: osc_settings[p.osc].vibrato_depth=v; break;
+ case VIB_LFO: osc_settings[p.osc].vibrato_lfo=v; break;
+ case WAVEFORM: osc_settings[p.osc].waveform=v; break;
+ case SYNC: osc_settings[p.osc].sync=(v!=0); break;
+
+ case FILTER_ENABLED: filter_settings.enabled=(v!=0); break;
+ case FILTER_ENV_AMOUNT: filter_settings.env_amount=float(v)/ONE; break;
+ case FILTER_ATTACK: filter_settings.env_settings.attack=v*samp_rate/filter_update_frames >>SCALE; break;
+ case FILTER_DECAY: filter_settings.env_settings.decay=v*samp_rate/filter_update_frames >>SCALE; break;
+ case FILTER_SUSTAIN: filter_settings.env_settings.sustain=v; break;
+ case FILTER_RELEASE: filter_settings.env_settings.release=v*samp_rate/filter_update_frames >>SCALE; break;
+ case FILTER_HOLD: filter_settings.env_settings.hold=(v!=0); break;
+ case FILTER_OFFSET: filter_settings.freqfactor_offset=float(v)/ONE; break;
+ case FILTER_RESONANCE: filter_settings.resonance=float(v)/ONE; break;
+ case FILTER_TREMOLO: filter_settings.trem_strength=v; break;
+ case FILTER_TREM_LFO: filter_settings.trem_lfo=v; break;
+
+ case SYNC_FACTOR: sync_factor=v; break;
+
+ default: throw string("trying to set an unknown parameter");
+ }
+}
+
+
+bool parameter_t::operator< (const struct parameter_t &b) const
+{
+ if (par!=b.par) return (par < b.par);
+ else if (osc!=b.osc) return (osc < b.osc);
+ else if (index!=b.index) return (index < b.index);
+ else return false; //they're equal
+}
+
+
+
+
+fixed_t calc_pfactor(param_factor_t &formula, fixed_t vel)
+{
+ return formula.offset + (formula.vel_amount * vel >>SCALE);
+}
+
+void init_default_program(program_t &p)
+{
+ p.n_osc=1;
+ p.osc_settings=new oscillator_t[1];
+ p.env_settings=new env_settings_t[1];
+ p.osc_settings[0].n_osc=1;
+ p.osc_settings[0].fm_strength=new fixed_t[1];
+ p.osc_settings[0].factor=ONE;
+ p.osc_settings[0].fm_strength[0]=0;
+ p.osc_settings[0].ksl=0;
+ p.osc_settings[0].ksr=0;
+ p.osc_settings[0].output=ONE;
+ p.osc_settings[0].tremolo_depth=0;
+ p.osc_settings[0].vibrato_depth=0;
+ p.osc_settings[0].waveform=0;
+ p.env_settings[0].attack=0;
+ p.env_settings[0].decay=0;
+ p.env_settings[0].sustain=ONE;
+ p.env_settings[0].release=3*samp_rate;
+ p.env_settings[0].hold=true;
+ p.filter_settings.enabled=false;
+
+
+ p.pfactor.out=new param_factor_t [1];
+ p.pfactor.fm=new param_factor_t* [1];
+
+ p.pfactor.filter_env.offset=ONE;
+ p.pfactor.filter_env.vel_amount=0;
+
+ p.pfactor.filter_res.offset=ONE;
+ p.pfactor.filter_res.vel_amount=0;
+ p.pfactor.filter_offset.offset=ONE;
+ p.pfactor.filter_offset.vel_amount=0;
+
+ p.pfactor.out[0].offset=0;
+ p.pfactor.out[0].vel_amount=ONE;
+
+ p.pfactor.fm[0]=new param_factor_t [1];
+ p.pfactor.fm[0][0].offset=ONE;
+ p.pfactor.fm[0][0].vel_amount=0;
+
+}
+
diff --git a/synth/programs.h b/synth/programs.h
new file mode 100644
index 0000000..e0b409c
--- /dev/null
+++ b/synth/programs.h
@@ -0,0 +1,193 @@
+#ifndef __PROGRAMS_H__
+#define __PROGRAMS_H__
+
+
+#include <map>
+#include <set>
+#include <list>
+
+#include "fixed.h"
+#include <jack/jack.h>
+
+using namespace std;
+
+struct term_t
+{
+ int c;
+ fixed_t f;
+};
+
+enum parameter_enum
+{
+ MODULATION=0,
+ OUTPUT,
+ WAVEFORM,
+ FACTOR,
+ TREMOLO,
+ TREM_LFO,
+ VIBRATO,
+ VIB_LFO,
+ ATTACK,
+ DECAY,
+ SUSTAIN,
+ RELEASE,
+ HOLD,
+ KSR,
+ KSL,
+ SYNC,
+ FILTER_ENABLED,
+ FILTER_ENV_AMOUNT,
+ FILTER_ATTACK,
+ FILTER_DECAY,
+ FILTER_SUSTAIN,
+ FILTER_RELEASE,
+ FILTER_HOLD,
+ FILTER_OFFSET,
+ FILTER_RESONANCE,
+ FILTER_TREMOLO,
+ FILTER_TREM_LFO,
+ SYNC_FACTOR,
+
+
+ PARAMETER_N_ENTRIES,
+ UNKNOWN=-1
+};
+
+struct parameter_t
+{
+ int osc;
+ int index;
+ enum parameter_enum par;
+
+ bool operator< (const struct parameter_t &b) const;
+};
+
+
+
+struct param_factor_t
+{
+ fixed_t offset;
+ fixed_t vel_amount;
+};
+
+struct pfactor_formula_t
+{
+ param_factor_t **fm;
+ param_factor_t *out;
+ param_factor_t filter_env;
+ param_factor_t filter_res;
+ param_factor_t filter_offset;
+};
+
+struct pfactor_value_t
+{
+ fixed_t **fm;
+ fixed_t *out;
+ fixed_t filter_env;
+ fixed_t filter_res;
+ fixed_t filter_offset;
+};
+
+fixed_t calc_pfactor(param_factor_t &formula, fixed_t vel);
+
+
+
+struct custom_wave_t
+{
+ fixed_t samp_rate;
+ int wave_len;
+ fixed_t* wave;
+
+ custom_wave_t()
+ {
+ wave=NULL;
+ wave_len=0;
+ samp_rate=0;
+ }
+
+ ~custom_wave_t()
+ {
+ if (wave) delete [] wave;
+ }
+};
+
+
+struct oscillator_t
+{
+ fixed_t *fm_strength; //this osc gets modulated by osc #i by fm_strength[i].
+ fixed_t output; //NOT: osc #i gets modulated by this osc!
+ int waveform;
+ fixed_t factor;
+ fixed_t phase;
+
+ fixed_t tremolo_depth;
+ int tremolo_lfo;
+ fixed_t vibrato_depth;
+ int vibrato_lfo;
+
+ custom_wave_t *custom_wave;
+
+ int n_osc;
+
+ float ksl;
+ float ksr;
+
+ bool sync;
+
+ oscillator_t();
+ oscillator_t& operator=(const oscillator_t &that);
+};
+
+struct env_settings_t
+{
+ jack_nframes_t attack;
+ jack_nframes_t decay;
+ fixed_t sustain;
+ jack_nframes_t release;
+ bool hold;
+};
+
+struct filter_params_t
+{
+ bool enabled;
+ float resonance;
+ float freqfactor_offset;
+ float env_amount;
+ int trem_strength;
+ int trem_lfo;
+ env_settings_t env_settings;
+};
+
+
+
+struct program_t
+{
+ unsigned int n_osc;
+ oscillator_t *osc_settings;
+ env_settings_t *env_settings;
+ set<parameter_t> controller_affects[128];
+ map< parameter_t, list<term_t> > formula;
+ int controller[128+1];
+ filter_params_t filter_settings;
+ fixed_t sync_factor;
+
+ pfactor_formula_t pfactor;
+
+
+
+ program_t();
+ ~program_t();
+
+ void set_param(const parameter_t &p, fixed_t v);
+
+ void cleanup();
+
+ program_t& operator=(const program_t &that);
+};
+
+
+
+
+void init_default_program(program_t &p);
+
+#endif
diff --git a/synth/readwave.cpp b/synth/readwave.cpp
new file mode 100644
index 0000000..08106ca
--- /dev/null
+++ b/synth/readwave.cpp
@@ -0,0 +1,137 @@
+#include <string>
+#include <cstring>
+
+#include "readwave.h"
+#include "util.h"
+#include "fixed.h"
+
+using namespace std;
+
+
+unsigned long int le_dword(unsigned char *b)
+{
+ return b[0]+256*b[1]+256*256*b[2]+256*256*256*b[3];
+}
+unsigned int le_word(unsigned char *b)
+{
+ return b[0]+256*b[1];
+}
+
+signed int le_sword(unsigned char *b)
+{
+ int x=le_word(b);
+ if (x & (1<<15) )
+ return - ((~(x-1))&0xFFFF);
+ else
+ return x;
+}
+
+void safe_fread(void* buf, int size, int n, FILE* f)
+{
+ int x=fread(buf,size,n,f);
+
+ if (x!=n)
+ throw string("got end-of-file or error while reading from file");
+}
+
+void read_wave(const char *fn, custom_wave_t *result)
+{
+ try
+ {
+ int fmt=0, chans=0, sr=0, bits=0, bytes=0, filelen=0;
+ unsigned char buf[100];
+
+ FILE *f=fopen(fn,"r");
+ if (f==NULL)
+ throw string("could not open file");
+
+ safe_fread(buf, 1, 12, f);
+
+ if ((memcmp(buf,"RIFF",4)==0) && (memcmp(buf+8,"WAVE",4)==0))
+ {
+ filelen=le_dword(buf+4);
+
+ while (!feof(f))
+ {
+ int chunklen;
+
+ safe_fread(buf,1,8,f); //read chunk name and chunk size
+
+ if (memcmp(buf,"fmt ",4)==0) //it's the fmt-chunk!
+ {
+ chunklen=le_dword(buf+4);
+ safe_fread(buf,1,chunklen,f);
+
+ fmt=le_word(buf); //should be 1 for PCM
+ chans=le_word(buf+2); //number of channels
+ sr=le_dword(buf+4); //sampling rate
+ bits=le_word(buf+14); //bits per sample (8 or 16)
+
+ if (fmt!=1)
+ throw string("invalid format, expected PCM");
+
+ if ((bits!=8) && (bits!=16))
+ throw string("invalid format, expected 8 or 16 bits");
+
+ if (chans==0)
+ throw string("invalid format, n_channels may not be zero");
+
+ if (chans>=2)
+ output_note("NOTE: wavefile '"+string(fn)+"' is multichannel, using the left\nchannel and ignoring the rest...");
+
+ if (sr==0)
+ throw string("sampling rate may not be zero");
+
+ }
+ else if (memcmp(buf,"data",4)==0) //it's the data-chunk!
+ {
+ chunklen=le_dword(buf+4);
+
+ if (sr==0)
+ throw string("found data chunk before the fmt chunk");
+
+ if (bits==8)
+ bytes=1;
+ else if (bits==16)
+ bytes=2;
+
+ int n_samples=chunklen/(bytes * chans);
+
+ result->samp_rate=fixed_t(sr)<<SCALE;
+ result->wave_len=n_samples;
+ result->wave=new fixed_t[n_samples];
+
+ double sample;
+ for (int i=0;i<n_samples;i++)
+ {
+ if (feof(f))
+ throw string("unexpected end-of-file");
+
+ safe_fread(buf,1,bytes*chans,f);
+ if (bits==8)
+ sample=(buf[0]-128)/128.0;
+ else
+ sample=le_sword(buf)/double(1<<15);
+
+ result->wave[i]=sample*ONE;
+ }
+ break;
+ }
+ else //unknown chunk, skip it
+ {
+ chunklen=le_dword(buf+4);
+ safe_fread(buf,1,chunklen,f);
+ }
+ }
+ }
+ else
+ {
+ //not a valid wave file!
+ throw string("not a valid RIFF-WAVE file");
+ }
+ }
+ catch (string err)
+ {
+ output_warning("ERROR: could not read '"+string(fn)+"': "+err+"\n a default waveform (sine) will be loaded");
+ }
+}
diff --git a/synth/readwave.h b/synth/readwave.h
new file mode 100644
index 0000000..48738bf
--- /dev/null
+++ b/synth/readwave.h
@@ -0,0 +1,9 @@
+#ifndef __READWAVE_H__
+#define __READWAVE_H__
+
+
+#include "programs.h"
+
+void read_wave(const char *fn, custom_wave_t *result);
+
+#endif
diff --git a/synth/util.cpp b/synth/util.cpp
new file mode 100644
index 0000000..ca213fd
--- /dev/null
+++ b/synth/util.cpp
@@ -0,0 +1,206 @@
+#include <iostream>
+#include <sstream>
+
+#include "util.h"
+#include "globals.h"
+
+string IntToStr(int i)
+{
+ ostringstream s;
+ s<<i;
+ return s.str();
+}
+
+string IntToStrHex(int i)
+{
+ ostringstream s;
+ s<<std::hex << i;
+ return s.str();
+}
+
+bool isnum(string s)
+{
+ for (size_t i=0;i<s.length();i++)
+ if (!isdigit(s[i]))
+ return false;
+
+ return true;
+}
+
+bool isfloat(string s)
+{
+ return (s.find_first_not_of("0123456789.")==string::npos);
+}
+
+
+string remove_all_spaces(string s)
+{
+ string result;
+
+ for (size_t i=0; i<s.length(); i++)
+ if ((s[i]!=' ') && (s[i]!='\t'))
+ result+=s[i];
+
+ return result;
+}
+
+string trim_spaces(string s)
+{
+ string result;
+ int i;
+
+ for (i=0;i<s.length();i++)
+ if ((s[i]!=' ') && (s[i]!='\t'))
+ break;
+
+ if (i!=s.length())
+ {
+ result=s.substr(i);
+ for (i=result.length()-1;i>=0;i--)
+ if ((result[i]!=' ') && (result[i]!='\t'))
+ break;
+
+ if (i>=0)
+ return result.substr(0,i+1);
+ else
+ return "";
+ }
+ else
+ {
+ return "";
+ }
+}
+
+void output_warning(string s)
+{
+ cout << s << endl;
+ if (fatal_warnings) throw string(s);
+}
+
+void output_note(string s)
+{
+ if (!quiet) cout << s << endl;
+}
+
+void output_verbose(string s)
+{
+ if (verbose) cout << s << endl;
+}
+
+bool param_needs_index(parameter_enum p)
+{
+ switch (p)
+ {
+ case FILTER_ENABLED:
+
+ case FILTER_ATTACK:
+ case FILTER_DECAY:
+ case FILTER_SUSTAIN:
+ case FILTER_RELEASE:
+ case FILTER_HOLD:
+ case FILTER_ENV_AMOUNT:
+
+ case FILTER_OFFSET:
+ case FILTER_RESONANCE:
+
+ case FILTER_TREMOLO:
+ case FILTER_TREM_LFO:
+
+ case SYNC_FACTOR:
+ return false;
+
+ default: return true;
+ }
+}
+
+parameter_enum param_to_enum(string param)
+{
+ if (param=="mod")
+ return MODULATION;
+ else if (param=="out")
+ return OUTPUT;
+ else if (param=="waveform")
+ return WAVEFORM;
+ else if (param=="sync")
+ return SYNC;
+ else if (param=="factor")
+ return FACTOR;
+ else if (param=="trem")
+ return TREMOLO;
+ else if (param=="trem_lfo")
+ return TREM_LFO;
+ else if (param=="vib")
+ return VIBRATO;
+ else if (param=="vib_lfo")
+ return VIB_LFO;
+ else if (param=="attack")
+ return ATTACK;
+ else if (param=="decay")
+ return DECAY;
+ else if (param=="sustain")
+ return SUSTAIN;
+ else if (param=="release")
+ return RELEASE;
+ else if (param=="hold")
+ return HOLD;
+ else if (param=="ksl")
+ return KSL;
+ else if (param=="ksr")
+ return KSR;
+ else if (param=="filter.enabled")
+ return FILTER_ENABLED;
+ else if (param=="filter.env_amount")
+ return FILTER_ENV_AMOUNT;
+ else if (param=="filter.attack")
+ return FILTER_ATTACK;
+ else if (param=="filter.decay")
+ return FILTER_DECAY;
+ else if (param=="filter.sustain")
+ return FILTER_SUSTAIN;
+ else if (param=="filter.release")
+ return FILTER_RELEASE;
+ else if (param=="filter.hold")
+ return FILTER_HOLD;
+ else if (param=="filter.offset")
+ return FILTER_OFFSET;
+ else if (param=="filter.resonance")
+ return FILTER_RESONANCE;
+ else if (param=="filter.trem")
+ return FILTER_TREMOLO;
+ else if (param=="filter.trem_lfo")
+ return FILTER_TREM_LFO;
+ else if (param=="sync_factor")
+ return SYNC_FACTOR;
+ else
+ return UNKNOWN;
+}
+
+string extract_var(string s)
+{
+ size_t p;
+ p=s.find('=');
+ if (p!=string::npos)
+ return s.substr(0,p);
+ else
+ return "";
+}
+
+string extract_val(string s)
+{
+ size_t p;
+ p=s.find('=');
+ if (p!=string::npos)
+ return s.substr(p+1);
+ else
+ return "";
+}
+
+string fileext(string f)
+{
+ size_t pos;
+ pos=f.rfind('.');
+ if (pos!=string::npos)
+ return f.substr(pos+1);
+ else
+ return "";
+}
diff --git a/synth/util.h b/synth/util.h
new file mode 100644
index 0000000..e571c7d
--- /dev/null
+++ b/synth/util.h
@@ -0,0 +1,31 @@
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include <string>
+#include "programs.h"
+
+using namespace std;
+
+string IntToStr(int i);
+string IntToStrHex(int i);
+
+bool isnum(string s);
+bool isfloat(string s);
+
+string remove_all_spaces(string s);
+string trim_spaces(string s);
+
+void output_warning(string s);
+void output_note(string s);
+void output_verbose(string s);
+
+bool param_needs_index(parameter_enum p);
+parameter_enum param_to_enum(string param);
+
+string extract_var(string s);
+string extract_val(string s);
+
+string fileext(string f);
+
+
+#endif