[SCM] zynjacku packaging branch, master, updated. upstream/4-4-gb252f84

mira-guest at users.alioth.debian.org mira-guest at users.alioth.debian.org
Mon Jul 20 14:08:01 UTC 2009


The following commit has been merged in the master branch:
commit 344fac04fe0f5ececd2245d7b89103e24f94f8ec
Author: miramikes <mira at 64studio.64studio>
Date:   Mon Jul 20 15:45:05 2009 +0200

    Imported Upstream version 5

diff --git a/Makefile.am b/Makefile.am
index 9018e33..19bb10b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,19 +32,18 @@ endif
 
 zynjacku_c_CFLAGS += @GTK_CFLAGS@
 zynjacku_c_CFLAGS += @PYGTK_CFLAGS@
-zynjacku_c_CFLAGS += @SLV2_CFLAGS@
 zynjacku_c_CFLAGS += @JACK_CFLAGS@
 zynjacku_c_CFLAGS += @LV2DYNPARAMHOST1_CFLAGS@
 
 INCLUDES = $(PYTHON_INCLUDES) $(zynjacku_c_CFLAGS)
 
-pkgpyexecdir = $(pythondir)
+pkgpyexecdir = $(pythondir)/zynworld/
 
-pkgpyexec_LTLIBRARIES = zynjacku_c.la
+pkgpyexec_LTLIBRARIES = zynjacku_c.la zynjacku_ttl.la
+pkgpyexec_SCRIPTS = zynworld/lv2.py zynworld/__init__.py
 
 ZYNJACKU_LDFLAGS = @GTK_LIBS@
 ZYNJACKU_LDFLAGS += @PYGTK_LIBS@
-ZYNJACKU_LDFLAGS += @SLV2_LIBS@
 ZYNJACKU_LDFLAGS += @JACK_LIBS@
 ZYNJACKU_LDFLAGS += @LV2DYNPARAMHOST1_LIBS@
 
@@ -60,12 +59,8 @@ zynjacku_c_la_SOURCES += jack_compat.c
 zynjacku_c_la_SOURCES += plugin.c
 zynjacku_c_la_SOURCES += plugin.h
 zynjacku_c_la_SOURCES += plugin_internal.h
-zynjacku_c_la_SOURCES += synth.h
-zynjacku_c_la_SOURCES += effect.h
 zynjacku_c_la_SOURCES += lv2.c
 zynjacku_c_la_SOURCES += lv2.h
-zynjacku_c_la_SOURCES += plugin_repo.c
-zynjacku_c_la_SOURCES += plugin_repo.h
 zynjacku_c_la_SOURCES += log.c
 zynjacku_c_la_SOURCES += log.h
 zynjacku_c_la_SOURCES += zynjacku_wrap.c
@@ -77,6 +72,7 @@ zynjacku_c_la_SOURCES += gtk2gui.h
 zynjacku_c_la_SOURCES += hints.c
 zynjacku_c_la_SOURCES += hints.h
 zynjacku_c_la_SOURCES += list.h
+zynjacku_c_la_SOURCES += lv2_ui.h
 zynjacku_c_la_SOURCES += lv2-miditype.h
 zynjacku_c_la_SOURCES += lv2_contexts.h
 zynjacku_c_la_SOURCES += lv2_data_access.h
@@ -100,10 +96,14 @@ zynjacku_c_la_SOURCES += midi_cc_map_internal.h
 PYGTK_DEFS=`pkg-config --variable=defsdir pygtk-2.0`
 PYGTK_CODEGEN=`pkg-config --variable=codegendir pygtk-2.0`
 
-zynjacku.defs: engine.h rack.h plugin.h enum.h hints.h midi_cc_map.h
+zynjacku.defs: engine.h rack.h plugin.h enum.h hints.h midi_cc_map.h lv2.h
 	python $(PYGTK_CODEGEN)/h2def.py $^ | \
 	sed -e '/define-method set_parameter/,/^$$/ { s/\("midi_cc_map_obj_ptr"\)/\1 (null-ok) (default "NULL")/g; }' \
 	    -e '/define-method set_midi_cc_map/,/^$$/ { s/\("midi_cc_map_obj_ptr"\)/\1 (null-ok)/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_uri"\)/\1 (null-ok) (default "NULL")/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_type_uri"\)/\1 (null-ok) (default "NULL")/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_binary_path"\)/\1 (null-ok) (default "NULL")/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_bundle_path"\)/\1 (null-ok) (default "NULL")/g; }' \
 	> $@
 
 # Generate the C wrapper from the defs and our override file
@@ -114,17 +114,28 @@ zynjacku_wrap.c: zynjacku.defs zynjacku.override
 	--override zynjacku.override \
 	zynjacku.defs > $@
 
+zynjacku_ttl_la_LDFLAGS = -module -avoid-version
+
+zynjacku_ttl_la_SOURCES  = zynjacku_ttl.c
+zynjacku_ttl_la_SOURCES += ttl_lexer.h
+zynjacku_ttl_la_SOURCES += flex_ttl.c
+zynjacku_ttl_la_SOURCES += flex_ttl.h
+
 CLEANFILES = zynjacku_wrap.c *.pyc zynjacku.defs
-EXTRA_DIST = zynjacku.override zynjacku.defs zynjacku.py lv2rack.py
+EXTRA_DIST = zynjacku.override zynjacku.defs zynjacku.py lv2rack.py zynspect.py zynworld/__init__.py  zynworld/lv2.py
 
-bin_SCRIPTS = zynjacku.py lv2rack.py
+bin_SCRIPTS = zynjacku.py lv2rack.py zynspect.py
 
 zynjackudir = $(pkgdatadir)
-dist_zynjacku_DATA = gpl.txt zynjacku.glade logo.png
+dist_zynjacku_DATA = gpl.txt zynjacku.glade art/logo/logo.png
 
 install-exec-hook:
 	ln -vfs zynjacku.py $(DESTDIR)$(bindir)/zynjacku
 	ln -vfs lv2rack.py $(DESTDIR)$(bindir)/lv2rack
+	ln -vfs zynspect.py $(DESTDIR)$(bindir)/zynspect
 
 uninstall-hook:
 	rm -vf $(DESTDIR)$(bindir)/zynjacku
+
+ttl_flex_regenerate:
+	flex --batch --nodefault -o flex_ttl.c --header-file=flex_ttl.h ttl.l
diff --git a/Makefile.in b/Makefile.in
index 69f28d2..ddbc542 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -81,30 +81,35 @@ am__vpath_adj = case $$p in \
   esac;
 am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
 am__installdirs = "$(DESTDIR)$(pkgpyexecdir)" "$(DESTDIR)$(bindir)" \
-	"$(DESTDIR)$(zynjackudir)"
+	"$(DESTDIR)$(pkgpyexecdir)" "$(DESTDIR)$(zynjackudir)"
 pkgpyexecLTLIBRARIES_INSTALL = $(INSTALL)
 LTLIBRARIES = $(pkgpyexec_LTLIBRARIES)
 zynjacku_c_la_DEPENDENCIES =
 am__zynjacku_c_la_SOURCES_DIST = zynjacku.h engine.c engine.h rack.c \
-	rack.h jack_compat.c plugin.c plugin.h plugin_internal.h \
-	synth.h effect.h lv2.c lv2.h plugin_repo.c plugin_repo.h log.c \
-	log.h zynjacku_wrap.c zynjackumodule.c enum.c enum.h gtk2gui.c \
-	gtk2gui.h hints.c hints.h list.h lv2-miditype.h lv2_contexts.h \
-	lv2_data_access.h lv2_event.h lv2_event_helpers.h \
-	lv2_uri_map.h lv2_string_port.h lv2_progress.h \
-	lv2_external_ui.h rtmempool.c rtmempool.h midi_cc_map.c \
-	midi_cc_map.h midi_cc_map_internal.h
+	rack.h jack_compat.c plugin.c plugin.h plugin_internal.h lv2.c \
+	lv2.h log.c log.h zynjacku_wrap.c zynjackumodule.c enum.c \
+	enum.h gtk2gui.c gtk2gui.h hints.c hints.h list.h lv2_ui.h \
+	lv2-miditype.h lv2_contexts.h lv2_data_access.h lv2_event.h \
+	lv2_event_helpers.h lv2_uri_map.h lv2_string_port.h \
+	lv2_progress.h lv2_external_ui.h rtmempool.c rtmempool.h \
+	midi_cc_map.c midi_cc_map.h midi_cc_map_internal.h
 @HAVE_DYNPARAMS_TRUE at am__objects_1 = rtmempool.lo
 am_zynjacku_c_la_OBJECTS = engine.lo rack.lo jack_compat.lo plugin.lo \
-	lv2.lo plugin_repo.lo log.lo zynjacku_wrap.lo \
-	zynjackumodule.lo enum.lo gtk2gui.lo hints.lo $(am__objects_1) \
-	midi_cc_map.lo
+	lv2.lo log.lo zynjacku_wrap.lo zynjackumodule.lo enum.lo \
+	gtk2gui.lo hints.lo $(am__objects_1) midi_cc_map.lo
 zynjacku_c_la_OBJECTS = $(am_zynjacku_c_la_OBJECTS)
 zynjacku_c_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
 	$(zynjacku_c_la_LDFLAGS) $(LDFLAGS) -o $@
+zynjacku_ttl_la_LIBADD =
+am_zynjacku_ttl_la_OBJECTS = zynjacku_ttl.lo flex_ttl.lo
+zynjacku_ttl_la_OBJECTS = $(am_zynjacku_ttl_la_OBJECTS)
+zynjacku_ttl_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(zynjacku_ttl_la_LDFLAGS) $(LDFLAGS) -o $@
 binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
-SCRIPTS = $(bin_SCRIPTS)
+pkgpyexecSCRIPT_INSTALL = $(INSTALL_SCRIPT)
+SCRIPTS = $(bin_SCRIPTS) $(pkgpyexec_SCRIPTS)
 depcomp = $(SHELL) $(top_srcdir)/config/depcomp
 am__depfiles_maybe = depfiles
 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
@@ -116,8 +121,9 @@ CCLD = $(CC)
 LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
 	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
 	$(LDFLAGS) -o $@
-SOURCES = $(zynjacku_c_la_SOURCES)
-DIST_SOURCES = $(am__zynjacku_c_la_SOURCES_DIST)
+SOURCES = $(zynjacku_c_la_SOURCES) $(zynjacku_ttl_la_SOURCES)
+DIST_SOURCES = $(am__zynjacku_c_la_SOURCES_DIST) \
+	$(zynjacku_ttl_la_SOURCES)
 dist_zynjackuDATA_INSTALL = $(INSTALL_DATA)
 DATA = $(dist_zynjacku_DATA)
 ETAGS = etags
@@ -198,6 +204,7 @@ PATH_SEPARATOR = @PATH_SEPARATOR@
 PKG_CONFIG = @PKG_CONFIG@
 PYGTK_CFLAGS = @PYGTK_CFLAGS@
 PYGTK_LIBS = @PYGTK_LIBS@
+PYLIBDIR = @PYLIBDIR@
 PYTHON = @PYTHON@
 PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
 PYTHON_INCLUDES = @PYTHON_INCLUDES@
@@ -208,8 +215,6 @@ RANLIB = @RANLIB@
 SED = @SED@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
-SLV2_CFLAGS = @SLV2_CFLAGS@
-SLV2_LIBS = @SLV2_LIBS@
 STRIP = @STRIP@
 VERSION = @VERSION@
 abs_builddir = @abs_builddir@
@@ -253,7 +258,7 @@ mandir = @mandir@
 mkdir_p = @mkdir_p@
 oldincludedir = @oldincludedir@
 pdfdir = @pdfdir@
-pkgpyexecdir = $(pythondir)
+pkgpyexecdir = $(pythondir)/zynworld/
 pkgpythondir = @pkgpythondir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
@@ -269,34 +274,37 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 DEFAULT_INCLUDES = 
 zynjacku_c_CFLAGS = -DDATA_DIR='"$(pkgdatadir)"' -Wall -g \
-	$(am__append_2) @GTK_CFLAGS@ @PYGTK_CFLAGS@ @SLV2_CFLAGS@ \
-	@JACK_CFLAGS@ @LV2DYNPARAMHOST1_CFLAGS@ $(am__empty)
+	$(am__append_2) @GTK_CFLAGS@ @PYGTK_CFLAGS@ @JACK_CFLAGS@ \
+	@LV2DYNPARAMHOST1_CFLAGS@ $(am__empty)
 INCLUDES = $(PYTHON_INCLUDES) $(zynjacku_c_CFLAGS)
-pkgpyexec_LTLIBRARIES = zynjacku_c.la
-ZYNJACKU_LDFLAGS = @GTK_LIBS@ @PYGTK_LIBS@ @SLV2_LIBS@ @JACK_LIBS@ \
+pkgpyexec_LTLIBRARIES = zynjacku_c.la zynjacku_ttl.la
+pkgpyexec_SCRIPTS = zynworld/lv2.py zynworld/__init__.py
+ZYNJACKU_LDFLAGS = @GTK_LIBS@ @PYGTK_LIBS@ @JACK_LIBS@ \
 	@LV2DYNPARAMHOST1_LIBS@ $(am__empty)
 zynjacku_c_la_LDFLAGS = -module -avoid-version $(ZYNJACKU_LDFLAGS)
 zynjacku_c_la_LIBADD = $(ZYNJACKU_LIBS)
 zynjacku_c_la_SOURCES = zynjacku.h engine.c engine.h rack.c rack.h \
-	jack_compat.c plugin.c plugin.h plugin_internal.h synth.h \
-	effect.h lv2.c lv2.h plugin_repo.c plugin_repo.h log.c log.h \
-	zynjacku_wrap.c zynjackumodule.c enum.c enum.h gtk2gui.c \
-	gtk2gui.h hints.c hints.h list.h lv2-miditype.h lv2_contexts.h \
-	lv2_data_access.h lv2_event.h lv2_event_helpers.h \
-	lv2_uri_map.h lv2_string_port.h lv2_progress.h \
-	lv2_external_ui.h $(am__append_3) midi_cc_map.c midi_cc_map.h \
-	midi_cc_map_internal.h
+	jack_compat.c plugin.c plugin.h plugin_internal.h lv2.c lv2.h \
+	log.c log.h zynjacku_wrap.c zynjackumodule.c enum.c enum.h \
+	gtk2gui.c gtk2gui.h hints.c hints.h list.h lv2_ui.h \
+	lv2-miditype.h lv2_contexts.h lv2_data_access.h lv2_event.h \
+	lv2_event_helpers.h lv2_uri_map.h lv2_string_port.h \
+	lv2_progress.h lv2_external_ui.h $(am__append_3) midi_cc_map.c \
+	midi_cc_map.h midi_cc_map_internal.h
 
 #nodist_zynjacku_la_SOURCES = 
 
 # The path to the GTK+ python types (FIXME: use configure result)
 PYGTK_DEFS = `pkg-config --variable=defsdir pygtk-2.0`
 PYGTK_CODEGEN = `pkg-config --variable=codegendir pygtk-2.0`
+zynjacku_ttl_la_LDFLAGS = -module -avoid-version
+zynjacku_ttl_la_SOURCES = zynjacku_ttl.c ttl_lexer.h flex_ttl.c \
+	flex_ttl.h
 CLEANFILES = zynjacku_wrap.c *.pyc zynjacku.defs
-EXTRA_DIST = zynjacku.override zynjacku.defs zynjacku.py lv2rack.py
-bin_SCRIPTS = zynjacku.py lv2rack.py
+EXTRA_DIST = zynjacku.override zynjacku.defs zynjacku.py lv2rack.py zynspect.py zynworld/__init__.py  zynworld/lv2.py
+bin_SCRIPTS = zynjacku.py lv2rack.py zynspect.py
 zynjackudir = $(pkgdatadir)
-dist_zynjacku_DATA = gpl.txt zynjacku.glade logo.png
+dist_zynjacku_DATA = gpl.txt zynjacku.glade art/logo/logo.png
 all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
@@ -381,6 +389,8 @@ clean-pkgpyexecLTLIBRARIES:
 	done
 zynjacku_c.la: $(zynjacku_c_la_OBJECTS) $(zynjacku_c_la_DEPENDENCIES) 
 	$(zynjacku_c_la_LINK) -rpath $(pkgpyexecdir) $(zynjacku_c_la_OBJECTS) $(zynjacku_c_la_LIBADD) $(LIBS)
+zynjacku_ttl.la: $(zynjacku_ttl_la_OBJECTS) $(zynjacku_ttl_la_DEPENDENCIES) 
+	$(zynjacku_ttl_la_LINK) -rpath $(pkgpyexecdir) $(zynjacku_ttl_la_OBJECTS) $(zynjacku_ttl_la_LIBADD) $(LIBS)
 install-binSCRIPTS: $(bin_SCRIPTS)
 	@$(NORMAL_INSTALL)
 	test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@@ -400,6 +410,25 @@ uninstall-binSCRIPTS:
 	  echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
 	  rm -f "$(DESTDIR)$(bindir)/$$f"; \
 	done
+install-pkgpyexecSCRIPTS: $(pkgpyexec_SCRIPTS)
+	@$(NORMAL_INSTALL)
+	test -z "$(pkgpyexecdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgpyexecdir)"
+	@list='$(pkgpyexec_SCRIPTS)'; for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  if test -f $$d$$p; then \
+	    f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+	    echo " $(pkgpyexecSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgpyexecdir)/$$f'"; \
+	    $(pkgpyexecSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgpyexecdir)/$$f"; \
+	  else :; fi; \
+	done
+
+uninstall-pkgpyexecSCRIPTS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkgpyexec_SCRIPTS)'; for p in $$list; do \
+	  f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+	  echo " rm -f '$(DESTDIR)$(pkgpyexecdir)/$$f'"; \
+	  rm -f "$(DESTDIR)$(pkgpyexecdir)/$$f"; \
+	done
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -409,6 +438,7 @@ distclean-compile:
 
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/engine.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/enum.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/flex_ttl.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/gtk2gui.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/hints.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/jack_compat.Plo at am__quote@
@@ -416,9 +446,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lv2.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/midi_cc_map.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/plugin.Plo at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/plugin_repo.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rack.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/rtmempool.Plo at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/zynjacku_ttl.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/zynjacku_wrap.Plo at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/zynjackumodule.Plo at am__quote@
 
@@ -646,7 +676,7 @@ check-am: all-am
 check: check-am
 all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) $(DATA) config.h
 installdirs:
-	for dir in "$(DESTDIR)$(pkgpyexecdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(zynjackudir)"; do \
+	for dir in "$(DESTDIR)$(pkgpyexecdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgpyexecdir)" "$(DESTDIR)$(zynjackudir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
 install: install-am
@@ -700,7 +730,8 @@ install-data-am: install-dist_zynjackuDATA
 
 install-dvi: install-dvi-am
 
-install-exec-am: install-binSCRIPTS install-pkgpyexecLTLIBRARIES
+install-exec-am: install-binSCRIPTS install-pkgpyexecLTLIBRARIES \
+	install-pkgpyexecSCRIPTS
 	@$(NORMAL_INSTALL)
 	$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
 
@@ -737,7 +768,7 @@ ps: ps-am
 ps-am:
 
 uninstall-am: uninstall-binSCRIPTS uninstall-dist_zynjackuDATA \
-	uninstall-pkgpyexecLTLIBRARIES
+	uninstall-pkgpyexecLTLIBRARIES uninstall-pkgpyexecSCRIPTS
 	@$(NORMAL_INSTALL)
 	$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
 
@@ -755,19 +786,24 @@ uninstall-am: uninstall-binSCRIPTS uninstall-dist_zynjackuDATA \
 	install-exec install-exec-am install-exec-hook install-html \
 	install-html-am install-info install-info-am install-man \
 	install-pdf install-pdf-am install-pkgpyexecLTLIBRARIES \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags uninstall uninstall-am uninstall-binSCRIPTS \
-	uninstall-dist_zynjackuDATA uninstall-hook \
-	uninstall-pkgpyexecLTLIBRARIES
+	install-pkgpyexecSCRIPTS install-ps install-ps-am \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am tags uninstall uninstall-am \
+	uninstall-binSCRIPTS uninstall-dist_zynjackuDATA \
+	uninstall-hook uninstall-pkgpyexecLTLIBRARIES \
+	uninstall-pkgpyexecSCRIPTS
 
 
-zynjacku.defs: engine.h rack.h plugin.h enum.h hints.h midi_cc_map.h
+zynjacku.defs: engine.h rack.h plugin.h enum.h hints.h midi_cc_map.h lv2.h
 	python $(PYGTK_CODEGEN)/h2def.py $^ | \
 	sed -e '/define-method set_parameter/,/^$$/ { s/\("midi_cc_map_obj_ptr"\)/\1 (null-ok) (default "NULL")/g; }' \
 	    -e '/define-method set_midi_cc_map/,/^$$/ { s/\("midi_cc_map_obj_ptr"\)/\1 (null-ok)/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_uri"\)/\1 (null-ok) (default "NULL")/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_type_uri"\)/\1 (null-ok) (default "NULL")/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_binary_path"\)/\1 (null-ok) (default "NULL")/g; }' \
+	    -e '/define-method ui_on/,/^$$/ { s/\("ui_bundle_path"\)/\1 (null-ok) (default "NULL")/g; }' \
 	> $@
 
 # Generate the C wrapper from the defs and our override file
@@ -781,9 +817,13 @@ zynjacku_wrap.c: zynjacku.defs zynjacku.override
 install-exec-hook:
 	ln -vfs zynjacku.py $(DESTDIR)$(bindir)/zynjacku
 	ln -vfs lv2rack.py $(DESTDIR)$(bindir)/lv2rack
+	ln -vfs zynspect.py $(DESTDIR)$(bindir)/zynspect
 
 uninstall-hook:
 	rm -vf $(DESTDIR)$(bindir)/zynjacku
+
+ttl_flex_regenerate:
+	flex --batch --nodefault -o flex_ttl.c --header-file=flex_ttl.h ttl.l
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/NEWS b/NEWS
index 31433ef..c135346 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,17 @@
+= Version 5 on 2009-06-13 =
+ * slv2 is no longer required
+ * cache list of suitable plugins
+ * speedup plugin list window
+ * new tool, zynspect, that can be used to list and inspect available
+   lv2 plguins.
+ * Fix assert when restoring rack presets
+ * By default, sort plugins by name
+ * Experimental support for dynmanifest extension. Combined with
+   NASPRO allows loading ladspa plugins in lv2rack.
+ * Set plugin GUI window's role to "plugin_ui" (for WM kludges etc)
+ * single plugin mode for lv2rack
+ * Hide external UIs when zynjacku/lv2rack quits
+
 = Version 4 on 2009-01-28 =
  * lv2rack does no longer require PHAT (it was not really using it even
    in zynjacku-3 release)
diff --git a/art/logo/logo.png b/art/logo/logo.png
new file mode 100644
index 0000000..338df3b
Binary files /dev/null and b/art/logo/logo.png differ
diff --git a/configure b/configure
index 9a4aef4..b3c58d5 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for zynjacku 4.
+# Generated by GNU Autoconf 2.61 for zynjacku 5.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='zynjacku'
 PACKAGE_TARNAME='zynjacku'
-PACKAGE_VERSION='4'
-PACKAGE_STRING='zynjacku 4'
+PACKAGE_VERSION='5'
+PACKAGE_STRING='zynjacku 5'
 PACKAGE_BUGREPORT=''
 
 # Factoring default headers for most tests.
@@ -886,8 +886,6 @@ LV2DYNPARAMHOST1_CFLAGS
 LV2DYNPARAMHOST1_LIBS
 HAVE_DYNPARAMS_TRUE
 HAVE_DYNPARAMS_FALSE
-SLV2_CFLAGS
-SLV2_LIBS
 JACK_MIDI_CFLAGS
 JACK_MIDI_LIBS
 OLD_JACK_MIDI_CFLAGS
@@ -904,6 +902,7 @@ pkgpythondir
 pyexecdir
 pkgpyexecdir
 PYTHON_INCLUDES
+PYLIBDIR
 LIBOBJS
 LTLIBOBJS'
 ac_subst_files=''
@@ -933,12 +932,11 @@ JACK_CFLAGS
 JACK_LIBS
 LV2DYNPARAMHOST1_CFLAGS
 LV2DYNPARAMHOST1_LIBS
-SLV2_CFLAGS
-SLV2_LIBS
 JACK_MIDI_CFLAGS
 JACK_MIDI_LIBS
 OLD_JACK_MIDI_CFLAGS
-OLD_JACK_MIDI_LIBS'
+OLD_JACK_MIDI_LIBS
+PYLIBDIR'
 
 
 # Initialize some variables set by options.
@@ -1441,7 +1439,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures zynjacku 4 to adapt to many kinds of systems.
+\`configure' configures zynjacku 5 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1511,7 +1509,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of zynjacku 4:";;
+     short | recursive ) echo "Configuration of zynjacku 5:";;
    esac
   cat <<\_ACEOF
 
@@ -1563,8 +1561,6 @@ Some influential environment variables:
               C compiler flags for LV2DYNPARAMHOST1, overriding pkg-config
   LV2DYNPARAMHOST1_LIBS
               linker flags for LV2DYNPARAMHOST1, overriding pkg-config
-  SLV2_CFLAGS C compiler flags for SLV2, overriding pkg-config
-  SLV2_LIBS   linker flags for SLV2, overriding pkg-config
   JACK_MIDI_CFLAGS
               C compiler flags for JACK_MIDI, overriding pkg-config
   JACK_MIDI_LIBS
@@ -1573,6 +1569,7 @@ Some influential environment variables:
               C compiler flags for OLD_JACK_MIDI, overriding pkg-config
   OLD_JACK_MIDI_LIBS
               linker flags for OLD_JACK_MIDI, overriding pkg-config
+  PYLIBDIR    Override default Python site-packages dir
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1637,7 +1634,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-zynjacku configure 4
+zynjacku configure 5
 generated by GNU Autoconf 2.61
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1651,7 +1648,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by zynjacku $as_me 4, which was
+It was created by zynjacku $as_me 5, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   $ $0 $@
@@ -2341,7 +2338,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='zynjacku'
- VERSION='4'
+ VERSION='5'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -4411,7 +4408,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 4414 "configure"' > conftest.$ac_ext
+  echo '#line 4411 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -7148,11 +7145,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7151: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7148: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7155: \$? = $ac_status" >&5
+   echo "$as_me:7152: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7438,11 +7435,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7441: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7438: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7445: \$? = $ac_status" >&5
+   echo "$as_me:7442: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7542,11 +7539,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7545: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7542: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7549: \$? = $ac_status" >&5
+   echo "$as_me:7546: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -9891,7 +9888,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 9894 "configure"
+#line 9891 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -9991,7 +9988,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 9994 "configure"
+#line 9991 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12411,11 +12408,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:12414: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:12411: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:12418: \$? = $ac_status" >&5
+   echo "$as_me:12415: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -12515,11 +12512,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:12518: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:12515: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:12522: \$? = $ac_status" >&5
+   echo "$as_me:12519: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -14077,11 +14074,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14080: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14077: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:14084: \$? = $ac_status" >&5
+   echo "$as_me:14081: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -14181,11 +14178,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14184: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14181: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:14188: \$? = $ac_status" >&5
+   echo "$as_me:14185: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -16368,11 +16365,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:16371: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:16368: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:16375: \$? = $ac_status" >&5
+   echo "$as_me:16372: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -16658,11 +16655,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:16661: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:16658: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:16665: \$? = $ac_status" >&5
+   echo "$as_me:16662: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -16762,11 +16759,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:16765: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:16762: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:16769: \$? = $ac_status" >&5
+   echo "$as_me:16766: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -20071,117 +20068,6 @@ else
 fi
 
 
-pkg_failed=no
-{ echo "$as_me:$LINENO: checking for SLV2" >&5
-echo $ECHO_N "checking for SLV2... $ECHO_C" >&6; }
-
-if test -n "$PKG_CONFIG"; then
-    if test -n "$SLV2_CFLAGS"; then
-        pkg_cv_SLV2_CFLAGS="$SLV2_CFLAGS"
-    else
-        if test -n "$PKG_CONFIG" && \
-    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"slv2 >= 0.6.1\"") >&5
-  ($PKG_CONFIG --exists --print-errors "slv2 >= 0.6.1") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  pkg_cv_SLV2_CFLAGS=`$PKG_CONFIG --cflags "slv2 >= 0.6.1" 2>/dev/null`
-else
-  pkg_failed=yes
-fi
-    fi
-else
-	pkg_failed=untried
-fi
-if test -n "$PKG_CONFIG"; then
-    if test -n "$SLV2_LIBS"; then
-        pkg_cv_SLV2_LIBS="$SLV2_LIBS"
-    else
-        if test -n "$PKG_CONFIG" && \
-    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"slv2 >= 0.6.1\"") >&5
-  ($PKG_CONFIG --exists --print-errors "slv2 >= 0.6.1") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  pkg_cv_SLV2_LIBS=`$PKG_CONFIG --libs "slv2 >= 0.6.1" 2>/dev/null`
-else
-  pkg_failed=yes
-fi
-    fi
-else
-	pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
-fi
-        if test $_pkg_short_errors_supported = yes; then
-	        SLV2_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "slv2 >= 0.6.1"`
-        else
-	        SLV2_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "slv2 >= 0.6.1"`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$SLV2_PKG_ERRORS" >&5
-
-	{ { echo "$as_me:$LINENO: error: Package requirements (slv2 >= 0.6.1) were not met:
-
-$SLV2_PKG_ERRORS
-
-Consider adjusting the PKG_CONFIG_PATH environment variable if you
-installed software in a non-standard prefix.
-
-Alternatively, you may set the environment variables SLV2_CFLAGS
-and SLV2_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.
-" >&5
-echo "$as_me: error: Package requirements (slv2 >= 0.6.1) were not met:
-
-$SLV2_PKG_ERRORS
-
-Consider adjusting the PKG_CONFIG_PATH environment variable if you
-installed software in a non-standard prefix.
-
-Alternatively, you may set the environment variables SLV2_CFLAGS
-and SLV2_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.
-" >&2;}
-   { (exit 1); exit 1; }; }
-elif test $pkg_failed = untried; then
-	{ { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old.  Make sure it
-is in your PATH or set the PKG_CONFIG environment variable to the full
-path to pkg-config.
-
-Alternatively, you may set the environment variables SLV2_CFLAGS
-and SLV2_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.
-
-To get pkg-config, see <http://pkg-config.freedesktop.org/>.
-See \`config.log' for more details." >&5
-echo "$as_me: error: The pkg-config script could not be found or is too old.  Make sure it
-is in your PATH or set the PKG_CONFIG environment variable to the full
-path to pkg-config.
-
-Alternatively, you may set the environment variables SLV2_CFLAGS
-and SLV2_LIBS to avoid the need to call pkg-config.
-See the pkg-config man page for more details.
-
-To get pkg-config, see <http://pkg-config.freedesktop.org/>.
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-else
-	SLV2_CFLAGS=$pkg_cv_SLV2_CFLAGS
-	SLV2_LIBS=$pkg_cv_SLV2_LIBS
-        { echo "$as_me:$LINENO: result: yes" >&5
-echo "${ECHO_T}yes" >&6; }
-	:
-fi
-
 # JACK MIDI
 have_jackmidi="unknown"
 # Check whether --enable-jackmidi was given.
@@ -20718,6 +20604,41 @@ CPPFLAGS="$save_CPPFLAGS"
 #AS_AC_EXPAND(PYTHONDIR, $pythondir)
 #AC_SUBST(PYTHONDIR)
 
+# Allow the user to override AM_PATH_PYTHON choice of PYTHON_PREFIX
+# and PYTHON_EXEC_PREFIX by setting PYLIBDIR
+
+if test "$PYLIBDIR" ; then
+   pylibdir=$PYLIBDIR
+   { echo "$as_me:$LINENO: User set PYLIBDIR... $pylibdir" >&5
+echo "$as_me: User set PYLIBDIR... $pylibdir" >&6;}
+   pythondir=$pylibdir
+
+   pyexecdir=$pylibdir
+
+fi
+
+# Warn if python does not search in the installed places
+#AC_MSG_NOTICE([Checking to see whether Python will find installed files...])
+
+# Figure out the script directory
+if test "$prefix" == 'NONE'; then
+  #AC_MSG_NOTICE([prefix not set, falling back to ac_default_prefix...])
+  pyprefix=$ac_default_prefix
+else
+  pyprefix=$prefix
+fi
+abs_pythondir=`echo $pythondir | $SED s='${prefix}'=$pyprefix=`
+#AC_MSG_NOTICE([Python script dir will be: $abs_pythondir])
+match_pythondir=no
+for p in `$PYTHON -c "import sys
+for p in sys.path:
+  print p"`
+do
+  #AC_MSG_NOTICE([Checking Python sys.path entry $p ...])
+  if test $p == $abs_pythondir ; then
+    match_pythondir=
+  fi
+done
 ac_config_files="$ac_config_files Makefile"
 
 cat >confcache <<\_ACEOF
@@ -21165,7 +21086,7 @@ exec 6>&1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by zynjacku $as_me 4, which was
+This file was extended by zynjacku $as_me 5, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -21218,7 +21139,7 @@ Report bugs to <bug-autoconf at gnu.org>."
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF
 ac_cs_version="\\
-zynjacku config.status 4
+zynjacku config.status 5
 configured by $0, generated by GNU Autoconf 2.61,
   with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
@@ -21558,8 +21479,6 @@ LV2DYNPARAMHOST1_CFLAGS!$LV2DYNPARAMHOST1_CFLAGS$ac_delim
 LV2DYNPARAMHOST1_LIBS!$LV2DYNPARAMHOST1_LIBS$ac_delim
 HAVE_DYNPARAMS_TRUE!$HAVE_DYNPARAMS_TRUE$ac_delim
 HAVE_DYNPARAMS_FALSE!$HAVE_DYNPARAMS_FALSE$ac_delim
-SLV2_CFLAGS!$SLV2_CFLAGS$ac_delim
-SLV2_LIBS!$SLV2_LIBS$ac_delim
 JACK_MIDI_CFLAGS!$JACK_MIDI_CFLAGS$ac_delim
 JACK_MIDI_LIBS!$JACK_MIDI_LIBS$ac_delim
 OLD_JACK_MIDI_CFLAGS!$OLD_JACK_MIDI_CFLAGS$ac_delim
@@ -21576,11 +21495,12 @@ pkgpythondir!$pkgpythondir$ac_delim
 pyexecdir!$pyexecdir$ac_delim
 pkgpyexecdir!$pkgpyexecdir$ac_delim
 PYTHON_INCLUDES!$PYTHON_INCLUDES$ac_delim
+PYLIBDIR!$PYLIBDIR$ac_delim
 LIBOBJS!$LIBOBJS$ac_delim
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF
 
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 43; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 42; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
@@ -22187,9 +22107,40 @@ echo "${ECHO_T}========================" >&6; }
 echo "${ECHO_T}Prefix                    : $prefix" >&6; }
 { echo "$as_me:$LINENO: result: Dynparam1 plugins support : $HAVE_DYNPARAMS" >&5
 echo "${ECHO_T}Dynparam1 plugins support : $HAVE_DYNPARAMS" >&6; }
-{ echo "$as_me:$LINENO: result: Python dir                : $PYTHON_INCLUDES" >&5
-echo "${ECHO_T}Python dir                : $PYTHON_INCLUDES" >&6; }
+{ echo "$as_me:$LINENO: result: Python include dir        : $PYTHON_INCLUDES" >&5
+echo "${ECHO_T}Python include dir        : $PYTHON_INCLUDES" >&6; }
+{ echo "$as_me:$LINENO: result: Python script dir         : $abs_pythondir" >&5
+echo "${ECHO_T}Python script dir         : $abs_pythondir" >&6; }
 { echo "$as_me:$LINENO: result: Dev version               : $dev_version" >&5
 echo "${ECHO_T}Dev version               : $dev_version" >&6; }
 { echo "$as_me:$LINENO: result: " >&5
 echo "${ECHO_T}" >&6; }
+
+if test "$match_pythondir"; then
+   { echo "$as_me:$LINENO: WARNING: ==================================================" >&5
+echo "$as_me: WARNING: ==================================================" >&2;}
+   { echo "$as_me:$LINENO: WARNING: ==                                              ==" >&5
+echo "$as_me: WARNING: ==                                              ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == pythondir is NOT in your Python's sys.path   ==" >&5
+echo "$as_me: WARNING: == pythondir is NOT in your Python's sys.path   ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == which means you should add it to PYTHONPATH  ==" >&5
+echo "$as_me: WARNING: == which means you should add it to PYTHONPATH  ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == at runtime, or add a .pth file to one of     ==" >&5
+echo "$as_me: WARNING: == at runtime, or add a .pth file to one of     ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == your Python's existing sys.path directories. ==" >&5
+echo "$as_me: WARNING: == your Python's existing sys.path directories. ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: ==                                              ==" >&5
+echo "$as_me: WARNING: ==                                              ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == Alternatively, re-run ./configure with       ==" >&5
+echo "$as_me: WARNING: == Alternatively, re-run ./configure with       ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == PYLIBDIR set to override the default         ==" >&5
+echo "$as_me: WARNING: == PYLIBDIR set to override the default         ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == pythondir, or adjust prefix to match the     ==" >&5
+echo "$as_me: WARNING: == pythondir, or adjust prefix to match the     ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: == prefix used to build your Python.            ==" >&5
+echo "$as_me: WARNING: == prefix used to build your Python.            ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: ==                                              ==" >&5
+echo "$as_me: WARNING: ==                                              ==" >&2;}
+   { echo "$as_me:$LINENO: WARNING: ==================================================" >&5
+echo "$as_me: WARNING: ==================================================" >&2;}
+fi
diff --git a/configure.ac b/configure.ac
index 95259c0..e3ae26b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@
 #
 
 AC_PREREQ(2.61)
-AC_INIT(zynjacku, 4)
+AC_INIT(zynjacku, 5)
 AC_CONFIG_AUX_DIR(config)
 AM_INIT_AUTOMAKE
 AC_CONFIG_HEADER([config.h])
@@ -50,7 +50,6 @@ then
   AC_DEFINE([HAVE_DYNPARAMS], 1, [Defined if dynparam1 is present.])
 fi
 AM_CONDITIONAL(HAVE_DYNPARAMS, test "$HAVE_DYNPARAMS" = "yes")
-PKG_CHECK_MODULES(SLV2, slv2 >= 0.6.1)
 
 # JACK MIDI
 have_jackmidi="unknown"
@@ -83,6 +82,38 @@ AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(Could not find Python headers)])
 #AS_AC_EXPAND(PYTHONDIR, $pythondir)
 #AC_SUBST(PYTHONDIR)
 
+# Allow the user to override AM_PATH_PYTHON choice of PYTHON_PREFIX
+# and PYTHON_EXEC_PREFIX by setting PYLIBDIR
+AC_ARG_VAR(PYLIBDIR, [Override default Python site-packages dir])
+if test "$PYLIBDIR" ; then
+   pylibdir=$PYLIBDIR
+   AC_MSG_NOTICE([User set PYLIBDIR... $pylibdir])
+   AC_SUBST([pythondir],$pylibdir)
+   AC_SUBST([pyexecdir],$pylibdir)
+fi
+
+# Warn if python does not search in the installed places
+#AC_MSG_NOTICE([Checking to see whether Python will find installed files...])
+
+# Figure out the script directory
+if test "$prefix" == 'NONE'; then
+  #AC_MSG_NOTICE([prefix not set, falling back to ac_default_prefix...])
+  pyprefix=$ac_default_prefix
+else
+  pyprefix=$prefix
+fi
+abs_pythondir=`echo $pythondir | $SED s='${prefix}'=$pyprefix=`
+#AC_MSG_NOTICE([Python script dir will be: $abs_pythondir])
+match_pythondir=no
+for p in `$PYTHON -c "import sys
+for p in sys.path:
+  print p"`
+do
+  #AC_MSG_NOTICE([Checking Python sys.path entry $p ...])
+  if test $p == $abs_pythondir ; then
+    match_pythondir=
+  fi
+done
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
 
@@ -90,6 +121,23 @@ AC_MSG_RESULT([])
 AC_MSG_RESULT([========================])
 AC_MSG_RESULT([Prefix                    : $prefix])
 AC_MSG_RESULT([Dynparam1 plugins support : $HAVE_DYNPARAMS])
-AC_MSG_RESULT([Python dir                : $PYTHON_INCLUDES])
+AC_MSG_RESULT([Python include dir        : $PYTHON_INCLUDES])
+AC_MSG_RESULT([Python script dir         : $abs_pythondir])
 AC_MSG_RESULT([Dev version               : $dev_version])
 AC_MSG_RESULT([])
+
+if test "$match_pythondir"; then
+   AC_MSG_WARN([==================================================])
+   AC_MSG_WARN([==                                              ==])
+   AC_MSG_WARN([== pythondir is NOT in your Python's sys.path   ==])
+   AC_MSG_WARN([== which means you should add it to PYTHONPATH  ==])
+   AC_MSG_WARN([== at runtime, or add a .pth file to one of     ==])
+   AC_MSG_WARN([== your Python's existing sys.path directories. ==])
+   AC_MSG_WARN([==                                              ==])
+   AC_MSG_WARN([== Alternatively, re-run ./configure with       ==])
+   AC_MSG_WARN([== PYLIBDIR set to override the default         ==])
+   AC_MSG_WARN([== pythondir, or adjust prefix to match the     ==])
+   AC_MSG_WARN([== prefix used to build your Python.            ==])
+   AC_MSG_WARN([==                                              ==])
+   AC_MSG_WARN([==================================================])
+fi
diff --git a/effect.h b/effect.h
deleted file mode 100644
index 8cbfd50..0000000
--- a/effect.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C ; c-basic-offset: 2 -*- */
-/*****************************************************************************
- *
- *   This file is part of zynjacku
- *
- *   Copyright (C) 2008,2009 Nedko Arnaudov <nedko at arnaudov.name>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; version 2 of the License
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *****************************************************************************/
-
-#ifndef EFFECT_H__6B544DDB_CFAC_4182_B10B_6183A3AB0933__INCLUDED
-#define EFFECT_H__6B544DDB_CFAC_4182_B10B_6183A3AB0933__INCLUDED
-
-bool
-zynjacku_plugin_construct_effect(
-  struct zynjacku_plugin * plugin_ptr,
-  ZynjackuPlugin * plugin_obj_ptr,
-  GObject * engine_object_ptr);
-
-#endif /* #ifndef EFFECT_H__6B544DDB_CFAC_4182_B10B_6183A3AB0933__INCLUDED */
diff --git a/engine.c b/engine.c
index 17f9a20..b1b205e 100644
--- a/engine.c
+++ b/engine.c
@@ -37,7 +37,6 @@
 #include <lv2dynparam/lv2_rtmempool.h>
 #include <lv2dynparam/host.h>
 #endif
-#include <slv2/lv2_ui.h>
 
 #include "lv2_contexts.h"
 #include "lv2-miditype.h"
@@ -45,6 +44,7 @@
 #include "lv2_uri_map.h"
 #include "lv2_string_port.h"
 #include "lv2_progress.h"
+#include "lv2_ui.h"
 
 #include "list.h"
 #define LOG_LEVEL LOG_LEVEL_ERROR
@@ -63,15 +63,12 @@
 #if HAVE_DYNPARAMS
 #include "rtmempool.h"
 #endif
-#include "plugin_repo.h"
 #include "lv2_event_helpers.h"
 #include "midi_cc_map.h"
 #include "midi_cc_map_internal.h"
 
-#define ZYNJACKU_ENGINE_SIGNAL_TICK      0 /* plugin iterated */
-#define ZYNJACKU_ENGINE_SIGNAL_TACK      1 /* "good" plugin found */
-#define ZYNJACKU_ENGINE_SIGNAL_PROGRESS  2 /* plugin instantiation progress */
-#define ZYNJACKU_ENGINE_SIGNALS_COUNT    3
+#define ZYNJACKU_ENGINE_SIGNAL_PROGRESS  0 /* plugin instantiation progress */
+#define ZYNJACKU_ENGINE_SIGNALS_COUNT    1
 
 /* URI map value for event MIDI type */
 #define ZYNJACKU_MIDI_EVENT_ID 1
@@ -112,8 +109,6 @@ struct zynjacku_engine
   pthread_mutex_t rt_lock;
   struct list_head plugins_pending_activation; /* protected using rt_lock */
 
-  struct list_head midi_ports;  /* PORT_TYPE_MIDI "struct zynjacku_port"s linked by port_type_siblings */
-  struct list_head audio_ports; /* PORT_TYPE_AUDIO "struct zynjacku_port"s linked by port_type_siblings */
   jack_port_t * jack_midi_in;
   LV2_MIDI lv2_midi_buffer;
   LV2_Event_Buffer lv2_midi_event_buffer;
@@ -192,7 +187,6 @@ zynjacku_engine_dispose(GObject * obj)
   if (engine_ptr->jack_client)
   {
     zynjacku_engine_stop_jack(ZYNJACKU_ENGINE(obj));
-    zynjacku_plugin_repo_uninit();
   }
 
   pthread_mutex_destroy(&engine_ptr->rt_lock);
@@ -230,38 +224,6 @@ zynjacku_engine_class_init(
 
   g_type_class_add_private(G_OBJECT_CLASS(class_ptr), sizeof(struct zynjacku_engine));
 
-  g_zynjacku_engine_signals[ZYNJACKU_ENGINE_SIGNAL_TICK] =
-    g_signal_new(
-      "tick",                   /* signal_name */
-      ZYNJACKU_ENGINE_TYPE,     /* itype */
-      G_SIGNAL_RUN_LAST |
-      G_SIGNAL_ACTION,          /* signal_flags */
-      0,                        /* class_offset */
-      NULL,                     /* accumulator */
-      NULL,                     /* accu_data */
-      NULL,                     /* c_marshaller */
-      G_TYPE_NONE,              /* return type */
-      2,                        /* n_params */
-      G_TYPE_FLOAT,             /* progress 0 .. 1 */
-      G_TYPE_STRING);           /* uri of plugin being scanned */
-
-  g_zynjacku_engine_signals[ZYNJACKU_ENGINE_SIGNAL_TACK] =
-    g_signal_new(
-      "tack",                   /* signal_name */
-      ZYNJACKU_ENGINE_TYPE,     /* itype */
-      G_SIGNAL_RUN_LAST |
-      G_SIGNAL_ACTION,          /* signal_flags */
-      0,                        /* class_offset */
-      NULL,                     /* accumulator */
-      NULL,                     /* accu_data */
-      NULL,                     /* c_marshaller */
-      G_TYPE_NONE,              /* return type */
-      4,                        /* n_params */
-      G_TYPE_STRING,            /* plugin name */
-      G_TYPE_STRING,            /* plugin uri */
-      G_TYPE_STRING,            /* plugin license */
-      G_TYPE_STRING);           /* plugin author */
-
   g_zynjacku_engine_signals[ZYNJACKU_ENGINE_SIGNAL_PROGRESS] =
     g_signal_new(
       "progress",               /* signal_name */
@@ -449,8 +411,6 @@ zynjacku_engine_init(
   assert(ZYNJACKU_ENGINE_FEATURES == count);
   engine_ptr->host_features[count] = NULL;
   /* keep in mind to update the constant when adding things here */
-
-  zynjacku_plugin_repo_init();
 }
 
 GType zynjacku_engine_get_type()
@@ -494,8 +454,6 @@ zynjacku_engine_start_jack(
   INIT_LIST_HEAD(&engine_ptr->plugins_all);
   INIT_LIST_HEAD(&engine_ptr->plugins_active);
   INIT_LIST_HEAD(&engine_ptr->plugins_pending_activation);
-  INIT_LIST_HEAD(&engine_ptr->midi_ports);
-  INIT_LIST_HEAD(&engine_ptr->audio_ports);
 
   INIT_LIST_HEAD(&engine_ptr->midicc_ui);
   INIT_LIST_HEAD(&engine_ptr->midicc_pending_activation);
@@ -574,9 +532,6 @@ fail_close_jack_client:
   engine_ptr->jack_client = NULL;
 
 fail:
-  assert(list_empty(&engine_ptr->audio_ports));
-  assert(list_empty(&engine_ptr->midi_ports));
-
   return FALSE;
 }
 
@@ -615,9 +570,6 @@ zynjacku_engine_stop_jack(
   jack_client_close(engine_ptr->jack_client);
 
   engine_ptr->jack_client = NULL;
-
-  assert(list_empty(&engine_ptr->audio_ports));
-  assert(list_empty(&engine_ptr->midi_ports));
 }
 
 /* Translate from a JACK MIDI buffer to an LV2 MIDI buffers (both old midi port and new midi event port). */
@@ -976,21 +928,21 @@ jack_process_cb(
 #endif
 
     /* Connect plugin LV2 output audio ports directly to JACK buffers */
-    if (synth_ptr->subtype.synth.audio_out_left_port.type == PORT_TYPE_AUDIO)
+    if (synth_ptr->subtype.synth.audio_out_left_port_ptr != NULL)
     {
       zynjacku_lv2_connect_port(
         synth_ptr->lv2plugin,
-        &synth_ptr->subtype.synth.audio_out_left_port,
-        jack_port_get_buffer(synth_ptr->subtype.synth.audio_out_left_port.data.audio, nframes));
+        synth_ptr->subtype.synth.audio_out_left_port_ptr,
+        jack_port_get_buffer(synth_ptr->subtype.synth.audio_out_left_port_ptr->data.audio, nframes));
     }
 
     /* Connect plugin LV2 output audio ports directly to JACK buffers */
-    if (synth_ptr->subtype.synth.audio_out_right_port.type == PORT_TYPE_AUDIO)
+    if (synth_ptr->subtype.synth.audio_out_right_port_ptr != NULL)
     {
       zynjacku_lv2_connect_port(
         synth_ptr->lv2plugin,
-        &synth_ptr->subtype.synth.audio_out_right_port,
-        jack_port_get_buffer(synth_ptr->subtype.synth.audio_out_right_port.data.audio, nframes));
+        synth_ptr->subtype.synth.audio_out_right_port_ptr,
+        jack_port_get_buffer(synth_ptr->subtype.synth.audio_out_right_port_ptr->data.audio, nframes));
     }
 
     /* Run plugin for this cycle */
@@ -1004,6 +956,7 @@ jack_process_cb(
 
 #undef engine_ptr
 
+static
 void
 zynjacku_engine_deactivate_synth(
   GObject * synth_obj_ptr)
@@ -1026,6 +979,36 @@ zynjacku_engine_deactivate_synth(
 }
 
 void
+zynjacku_engine_get_required_features(
+  GObject * engine_obj_ptr,
+  const LV2_Feature * const ** host_features,
+  unsigned int * host_feature_count)
+{
+  struct zynjacku_engine * engine_ptr;
+
+  engine_ptr = ZYNJACKU_ENGINE_GET_PRIVATE(engine_obj_ptr);
+
+  *host_features = engine_ptr->host_features;
+  *host_feature_count = ZYNJACKU_ENGINE_FEATURES;
+}
+
+static
+void
+zynjacku_engine_unregister_port(
+  GObject * engine_obj_ptr,
+  struct zynjacku_port * port_ptr)
+{
+  struct zynjacku_engine * engine_ptr;
+
+  engine_ptr = ZYNJACKU_ENGINE_GET_PRIVATE(engine_obj_ptr);
+
+  if (port_ptr->data.audio != NULL)
+  {
+    jack_port_unregister(engine_ptr->jack_client, port_ptr->data.audio);
+  }
+}
+
+void
 zynjacku_engine_ui_run(
   ZynjackuEngine * engine_obj_ptr)
 {
@@ -1096,197 +1079,23 @@ zynjacku_get_version()
   return VERSION;
 }
 
-#define engine_obj_ptr ((ZynjackuEngine *)context)
-
-bool
-zynjacku_check_plugin(
-  void * context,
-  const char * plugin_uri,
-  const char * plugin_name,
-  uint32_t audio_in_ports_count,
-  uint32_t audio_out_ports_count,
-  uint32_t midi_in_ports_count,
-  uint32_t control_ports_count,
-  uint32_t string_ports_count,
-  uint32_t event_ports_count,
-  uint32_t midi_event_in_ports_count,
-  uint32_t ports_count)
-{
-  if (midi_in_ports_count + control_ports_count + string_ports_count + event_ports_count + audio_out_ports_count != ports_count ||
-      midi_in_ports_count + midi_event_in_ports_count != 1 ||
-      audio_out_ports_count == 0)
-  {
-    LOG_DEBUG("Skipping 's' %s, [synth] plugin with unsupported port configuration", plugin_name, plugin_uri);
-    LOG_DEBUG("  midi input ports: %d", (unsigned int)midi_in_ports_count);
-    LOG_DEBUG("  control ports: %d", (unsigned int)control_ports_count);
-    LOG_DEBUG("  string ports: %d", (unsigned int)string_ports_count);
-    LOG_DEBUG("  event ports: %d", (unsigned int)event_ports_count);
-    LOG_DEBUG("  event midi input ports: %d", (unsigned int)midi_event_in_ports_count);
-    LOG_DEBUG("  audio input ports: %d", (unsigned int)audio_in_ports_count);
-    LOG_DEBUG("  audio output ports: %d", (unsigned int)audio_out_ports_count);
-    LOG_DEBUG("  total ports %d", (unsigned int)ports_count);
-    return false;
-  }
-
-  LOG_DEBUG("Found \"simple\" synth plugin '%s' %s", plugin_name, plugin_uri);
-  LOG_DEBUG("  midi input ports: %d", (unsigned int)midi_in_ports_count);
-  LOG_DEBUG("  control ports: %d", (unsigned int)control_ports_count);
-  LOG_DEBUG("  event ports: %d", (unsigned int)event_ports_count);
-  LOG_DEBUG("  event midi input ports: %d", (unsigned int)midi_event_in_ports_count);
-  LOG_DEBUG("  audio input ports: %d", (unsigned int)audio_in_ports_count);
-  LOG_DEBUG("  audio output ports: %d", (unsigned int)audio_out_ports_count);
-  LOG_DEBUG("  total ports %d", (unsigned int)ports_count);
-  return true;
-}
-
-void
-zynjacku_engine_tick(
-  void *context,
-  float progress,               /* 0..1 */
-  const char *message)
-{
-  g_signal_emit(
-    engine_obj_ptr,
-    g_zynjacku_engine_signals[ZYNJACKU_ENGINE_SIGNAL_TICK],
-    0,
-    progress,
-    message);
-}
-
-void
-zynjacku_engine_tack(
-  void *context,
-  const char *uri)
-{
-  const char * name;
-  const char * license;
-  const char * author;
-
-  name = zynjacku_plugin_repo_get_name(uri);
-  license = zynjacku_plugin_repo_get_license(uri);
-  author = zynjacku_plugin_repo_get_author(uri);
-
-  g_signal_emit(
-    engine_obj_ptr,
-    g_zynjacku_engine_signals[ZYNJACKU_ENGINE_SIGNAL_TACK],
-    0,
-    name,
-    uri,
-    license,
-    author);
-}
-
-#undef engine_obj_ptr
-
-void
-zynjacku_engine_iterate_plugins(
+const gchar *
+zynjacku_engine_get_supported_feature(
   ZynjackuEngine * engine_obj_ptr,
-  gboolean force)
-{
-  struct zynjacku_engine * engine_ptr;
-
-  engine_ptr = ZYNJACKU_ENGINE_GET_PRIVATE(engine_obj_ptr);
-
-  zynjacku_plugin_repo_iterate(
-    force,
-    engine_ptr->host_features,
-    engine_obj_ptr,
-    zynjacku_check_plugin,
-    zynjacku_engine_tick,
-    zynjacku_engine_tack);
-}
-
-void
-zynjacku_free_synth_ports(
-  GObject * plugin_object_ptr)
+  guint index)
 {
   struct zynjacku_engine * engine_ptr;
-  struct zynjacku_plugin * plugin_ptr;
 
-  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_object_ptr);
-  engine_ptr = ZYNJACKU_ENGINE_GET_PRIVATE(plugin_ptr->engine_object_ptr);
-
-  zynjacku_free_plugin_ports(plugin_ptr);
-
-  if (plugin_ptr->type == PLUGIN_TYPE_SYNTH)
+  if (index >= ZYNJACKU_ENGINE_FEATURES)
   {
-    if (plugin_ptr->subtype.synth.audio_out_left_port.type == PORT_TYPE_AUDIO)
-    {
-      jack_port_unregister(engine_ptr->jack_client, plugin_ptr->subtype.synth.audio_out_left_port.data.audio);
-      list_del(&plugin_ptr->subtype.synth.audio_out_left_port.port_type_siblings);
-    }
-
-    if (plugin_ptr->subtype.synth.audio_out_right_port.type == PORT_TYPE_AUDIO) /* stereo? */
-    {
-      assert(plugin_ptr->subtype.synth.audio_out_left_port.type == PORT_TYPE_AUDIO);
-      jack_port_unregister(engine_ptr->jack_client, plugin_ptr->subtype.synth.audio_out_right_port.data.audio);
-      list_del(&plugin_ptr->subtype.synth.audio_out_right_port.port_type_siblings);
-    }
-
-    if (plugin_ptr->subtype.synth.midi_in_port.type == PORT_TYPE_MIDI ||
-        plugin_ptr->subtype.synth.midi_in_port.type == PORT_TYPE_EVENT_MIDI)
-    {
-      list_del(&plugin_ptr->subtype.synth.midi_in_port.port_type_siblings);
-    }
+    return NULL;
   }
-}
-
-#define synth_ptr (&((struct zynjacku_plugin *)context)->subtype.synth)
-
-bool
-zynjacku_synth_create_port(
-  void * context,
-  unsigned int port_type,
-  bool output,
-  uint32_t port_index)
-{
-  struct zynjacku_port * port_ptr;
-
-  LOG_NOTICE("creating synth %s port of type %u, index %u", output ? "output" : "input", (unsigned int)port_type, (unsigned int)port_index);
-
-  switch (port_type)
-  {
-  case PORT_TYPE_AUDIO:
-    if (output)
-    {
-      if (synth_ptr->audio_out_left_port.type == PORT_TYPE_INVALID)
-      {
-        port_ptr = &synth_ptr->audio_out_left_port;
-      }
-      else if (synth_ptr->audio_out_right_port.type == PORT_TYPE_INVALID)
-      {
-        port_ptr = &synth_ptr->audio_out_right_port;
-      }
-      else
-      {
-        /* ignore, we dont support more than two audio ports yet */
-        return true;
-      }
 
-      port_ptr->type = PORT_TYPE_AUDIO;
-      port_ptr->flags = 0;
-      port_ptr->index = port_index;
-
-      return true;
-    }
-    break;
-  case PORT_TYPE_MIDI:
-  case PORT_TYPE_EVENT_MIDI:
-    if (!output)
-    {
-      port_ptr = &synth_ptr->midi_in_port;
-      port_ptr->type = port_type;
-      port_ptr->index = port_index;
-      return true;
-    }
-    break;
-  }
+  engine_ptr = ZYNJACKU_ENGINE_GET_PRIVATE(engine_obj_ptr);
 
-  return false;
+  return engine_ptr->host_features[index]->URI;
 }
 
-#undef synth_ptr
-
 static
 bool
 zynjacku_set_midi_cc_map(
@@ -1424,28 +1233,93 @@ zynjacku_midi_cc_map_cc_no_assign(
 
 #define synth_ptr (&plugin_ptr->subtype.synth)
 
-bool
-zynjacku_plugin_construct_synth(
-  struct zynjacku_plugin * plugin_ptr,
-  ZynjackuPlugin * plugin_obj_ptr,
-  GObject * engine_object_ptr)
+gboolean
+zynjacku_engine_construct_plugin(
+  ZynjackuEngine * engine_object_ptr,
+  ZynjackuPlugin * plugin_object_ptr)
 {
   static unsigned int id;
   char * port_name;
   size_t size_name;
   size_t size_id;
   struct zynjacku_engine * engine_ptr;
+  struct list_head * node_ptr;
+  struct zynjacku_port * port_ptr;
+  struct zynjacku_port * midi_port_ptr;
+  struct zynjacku_port * audio_left_port_ptr;
+  struct zynjacku_port * audio_right_port_ptr;
+  struct zynjacku_plugin * plugin_ptr;
 
   engine_ptr = ZYNJACKU_ENGINE_GET_PRIVATE(engine_object_ptr);
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_object_ptr);
 
-  plugin_ptr->type = PLUGIN_TYPE_SYNTH;
-  synth_ptr->midi_in_port.type = PORT_TYPE_INVALID;
-  synth_ptr->audio_out_left_port.type = PORT_TYPE_INVALID;
-  synth_ptr->audio_out_right_port.type = PORT_TYPE_INVALID;
+  if (plugin_ptr->uri == NULL)
+  {
+    LOG_ERROR("\"uri\" property needs to be set before constructing plugin");
+    goto fail;
+  }
+
+  if (plugin_ptr->name == NULL)
+  {
+    LOG_ERROR("\"name\" property needs to be set before constructing plugin");
+    goto fail;
+  }
+
+  if (plugin_ptr->dlpath == NULL)
+  {
+    LOG_ERROR("Plugin %s has no dlpath set", plugin_ptr->uri);
+    goto fail;
+  }
+
+  if (plugin_ptr->bundle_path == NULL)
+  {
+    LOG_ERROR("Plugin %s has no bundle path set", plugin_ptr->uri);
+    goto fail;
+  }
+
+  if (list_empty(&plugin_ptr->midi_ports))
+  {
+    LOG_ERROR("Cannot construct synth plugin without MIDI port. %s", plugin_ptr->uri);
+    goto fail;
+  }
+
+  midi_port_ptr = list_entry(plugin_ptr->midi_ports.next, struct zynjacku_port, plugin_siblings);
+  if (!PORT_IS_INPUT(midi_port_ptr))
+  {
+    LOG_ERROR("Cannot construct synth plugin without MIDI inpu port. %s", plugin_ptr->uri);
+    goto fail;
+  }
 
-  if (!zynjacku_plugin_repo_load_plugin(plugin_ptr, plugin_ptr, zynjacku_synth_create_port, zynjacku_check_plugin, engine_ptr->host_features))
+  if (plugin_ptr->midi_ports.next != plugin_ptr->midi_ports.prev)
   {
-    LOG_ERROR("Failed to load LV2 info for plugin %s", plugin_ptr->uri);
+    LOG_ERROR("Cannot construct synth plugin with more than one MIDI input port. %s", plugin_ptr->uri);
+    goto fail;
+  }
+
+  audio_left_port_ptr = NULL;
+  audio_right_port_ptr = NULL;
+
+  list_for_each(node_ptr, &plugin_ptr->audio_ports)
+  {
+    port_ptr = list_entry(node_ptr, struct zynjacku_port, plugin_siblings);
+    assert(port_ptr->type == PORT_TYPE_AUDIO);
+    if (PORT_IS_OUTPUT(port_ptr))
+    {
+      if (audio_left_port_ptr == NULL)
+      {
+        audio_left_port_ptr = port_ptr;
+        continue;
+      }
+
+      assert(audio_right_port_ptr == NULL);
+      audio_right_port_ptr = port_ptr;
+      break;
+    }
+  }
+
+  if (audio_left_port_ptr == NULL)
+  {
+    LOG_ERROR("Cannot construct synth plugin without audio output port(s). %s", plugin_ptr->uri);
     goto fail;
   }
 
@@ -1455,6 +1329,8 @@ zynjacku_plugin_construct_synth(
 
   plugin_ptr->lv2plugin = zynjacku_lv2_load(
     plugin_ptr->uri,
+    plugin_ptr->dlpath,
+    plugin_ptr->bundle_path,
     zynjacku_engine_get_sample_rate(ZYNJACKU_ENGINE(engine_object_ptr)),
     engine_ptr->host_features);
 
@@ -1475,8 +1351,8 @@ zynjacku_plugin_construct_synth(
   /* connect parameter/measure ports */
   if (!zynjacku_connect_plugin_ports(
         plugin_ptr,
-        plugin_obj_ptr,
-        engine_object_ptr
+        plugin_object_ptr,
+        G_OBJECT(engine_object_ptr)
 #if HAVE_DYNPARAMS
         , &engine_ptr->mempool_allocator
 #endif
@@ -1485,52 +1361,61 @@ zynjacku_plugin_construct_synth(
     goto fail_unload;
   }
 
+  synth_ptr->midi_in_port_ptr = midi_port_ptr;
+
   /* connect midi port */
-  switch (synth_ptr->midi_in_port.type)
+  switch (midi_port_ptr->type)
   {
   case PORT_TYPE_MIDI:
-    zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, &synth_ptr->midi_in_port, &engine_ptr->lv2_midi_buffer);
+    zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, midi_port_ptr, &engine_ptr->lv2_midi_buffer);
     break;
   case PORT_TYPE_EVENT_MIDI:
-    zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, &synth_ptr->midi_in_port, &engine_ptr->lv2_midi_event_buffer);
+    zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, midi_port_ptr, &engine_ptr->lv2_midi_event_buffer);
     break;
   default:
-    LOG_ERROR("don't know how to connect midi port of type %u", synth_ptr->midi_in_port.type);
+    LOG_ERROR("don't know how to connect midi port of type %u", midi_port_ptr->type);
     goto fail_detach_dynparams;
   }
 
-  list_add_tail(&synth_ptr->midi_in_port.port_type_siblings, &engine_ptr->midi_ports);
-
   /* setup audio ports (they are connected in jack process callback */
 
+  synth_ptr->audio_out_left_port_ptr = audio_left_port_ptr;
+  synth_ptr->audio_out_right_port_ptr = audio_right_port_ptr;
+
   size_name = strlen(plugin_ptr->name);
   port_name = malloc(size_name + 1024);
   if (port_name == NULL)
   {
     LOG_ERROR("Failed to allocate memory for port name");
-    goto fail_free_ports;
+    goto fail_detach_dynparams;
   }
 
   size_id = sprintf(port_name, "%u:", id);
   memcpy(port_name + size_id, plugin_ptr->name, size_name);
 
-  if (synth_ptr->audio_out_left_port.type == PORT_TYPE_AUDIO &&
-      synth_ptr->audio_out_right_port.type == PORT_TYPE_AUDIO)
+  if (audio_left_port_ptr != NULL &&
+      audio_right_port_ptr != NULL)
   {
+    assert(audio_left_port_ptr->type == PORT_TYPE_AUDIO);
+    assert(PORT_IS_OUTPUT(audio_left_port_ptr));
+
     strcpy(port_name + size_id + size_name, " L");
-    synth_ptr->audio_out_left_port.data.audio = jack_port_register(engine_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
-    list_add_tail(&synth_ptr->audio_out_left_port.port_type_siblings, &engine_ptr->audio_ports);
+    audio_left_port_ptr->data.audio = jack_port_register(engine_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+    assert(audio_right_port_ptr->type == PORT_TYPE_AUDIO);
+    assert(PORT_IS_OUTPUT(audio_right_port_ptr));
 
     strcpy(port_name + size_id + size_name, " R");
-    synth_ptr->audio_out_right_port.data.audio = jack_port_register(engine_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
-    list_add_tail(&synth_ptr->audio_out_right_port.port_type_siblings, &engine_ptr->audio_ports);
+    audio_right_port_ptr->data.audio = jack_port_register(engine_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
   }
-  else if (synth_ptr->audio_out_left_port.type == PORT_TYPE_AUDIO &&
-           synth_ptr->audio_out_right_port.type == PORT_TYPE_INVALID)
+  else if (audio_left_port_ptr != NULL &&
+           audio_right_port_ptr == NULL)
   {
+    assert(audio_left_port_ptr->type == PORT_TYPE_AUDIO);
+    assert(PORT_IS_OUTPUT(audio_left_port_ptr));
+
     port_name[size_id + size_name] = 0;
-    synth_ptr->audio_out_left_port.data.audio = jack_port_register(engine_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
-    list_add_tail(&synth_ptr->audio_out_left_port.port_type_siblings, &engine_ptr->audio_ports);
+    audio_left_port_ptr->data.audio = jack_port_register(engine_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
   }
 
   port_name[size_id + size_name] = 0;
@@ -1551,12 +1436,9 @@ zynjacku_plugin_construct_synth(
 
   g_object_ref(plugin_ptr->engine_object_ptr);
 
-  /* no plugins to test gtk2gui */
-  plugin_ptr->gtk2gui = zynjacku_gtk2gui_create(engine_ptr->host_features, ZYNJACKU_ENGINE_FEATURES, plugin_ptr->lv2plugin, 
-    plugin_ptr, plugin_obj_ptr, plugin_ptr->uri, plugin_ptr->id, &plugin_ptr->parameter_ports);
-
   plugin_ptr->deactivate = zynjacku_engine_deactivate_synth;
-  plugin_ptr->free_ports = zynjacku_free_synth_ports;
+  plugin_ptr->get_required_features = zynjacku_engine_get_required_features;
+  plugin_ptr->unregister_port = zynjacku_engine_unregister_port;
   plugin_ptr->set_midi_cc_map = zynjacku_set_midi_cc_map;
   plugin_ptr->midi_cc_map_cc_no_assign = zynjacku_midi_cc_map_cc_no_assign;
 
@@ -1564,10 +1446,6 @@ zynjacku_plugin_construct_synth(
 
   return true;
 
-fail_free_ports:
-  zynjacku_free_synth_ports(G_OBJECT(plugin_obj_ptr));
-  plugin_ptr->engine_object_ptr = NULL;
-
 fail_detach_dynparams:
 #if HAVE_DYNPARAMS
   if (plugin_ptr->dynparams != NULL)
diff --git a/engine.h b/engine.h
index ec06045..fb4e1da 100644
--- a/engine.h
+++ b/engine.h
@@ -74,10 +74,15 @@ zynjacku_engine_get_midi_activity(
 const gchar *
 zynjacku_get_version();
 
-void
-zynjacku_engine_iterate_plugins(
+const gchar *
+zynjacku_engine_get_supported_feature(
+  ZynjackuEngine * engine_obj_ptr,
+  guint index);
+
+gboolean
+zynjacku_engine_construct_plugin(
   ZynjackuEngine * engine_obj_ptr,
-  gboolean force);
+  ZynjackuPlugin * plugin_obj_ptr);
 
 G_END_DECLS
 
diff --git a/flex_ttl.c b/flex_ttl.c
new file mode 100644
index 0000000..afa56b3
--- /dev/null
+++ b/flex_ttl.c
@@ -0,0 +1,2054 @@
+#line 2 "flex_ttl.c"
+
+#line 4 "flex_ttl.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = yyg->yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+                          ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+static void yyensure_buffer_stack (yyscan_t yyscanner );
+static void yy_load_buffer_state (yyscan_t yyscanner );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (yyscanner); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yyg->yytext_ptr = yy_bp; \
+	yyleng = (size_t) (yy_cp - yy_bp); \
+	yyg->yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 21
+#define YY_END_OF_BUFFER 22
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[61] =
+    {   0,
+        0,    0,    0,    0,    0,    0,    0,    0,   22,   12,
+       11,   11,    4,    2,   10,   12,    9,   10,    7,    8,
+       12,   12,    9,   20,   19,   14,   15,   18,   21,   16,
+       21,    0,    0,    7,    9,    7,    8,    6,    6,    8,
+        0,    5,    0,   14,    0,   18,   17,    3,    0,    6,
+        8,    0,   13,    0,    6,    0,    0,    0,    1,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    2,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    4,    5,    1,    1,    1,    1,    6,
+        6,    1,    7,    6,    8,    9,    1,   10,   10,   10,
+       10,   10,   10,   10,   10,   10,   10,   11,    6,   12,
+        1,   13,    1,   14,   15,   15,   15,   15,   16,   15,
+       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
+       15,   15,   15,   15,   15,   15,   15,   15,   15,   15,
+        6,   17,    6,    1,   15,    1,   15,   15,   15,   15,
+
+       18,   19,   15,   15,   20,   15,   15,   15,   15,   15,
+       15,   21,   15,   22,   15,   15,   15,   15,   15,   23,
+       15,   15,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[24] =
+    {   0,
+        1,    1,    2,    3,    1,    1,    1,    4,    5,    6,
+        5,    1,    1,    1,    4,    4,    2,    4,    4,    4,
+        4,    4,    4
+    } ;
+
+static yyconst flex_int16_t yy_base[70] =
+    {   0,
+        0,    0,  115,  114,  112,  111,   21,   23,  114,  119,
+      119,  119,  109,  119,  119,   19,   21,  102,   24,    0,
+       98,   89,   26,  119,  119,    0,  105,    0,  119,  119,
+      103,   98,   91,   32,   34,   37,    0,   34,   39,    0,
+       83,  119,   43,    0,   59,    0,  119,  119,   46,   48,
+        0,   44,  119,   51,   50,   40,   31,   16,  119,  119,
+       66,   72,   78,   81,   32,   87,   93,   99,  102
+    } ;
+
+static yyconst flex_int16_t yy_def[70] =
+    {   0,
+       60,    1,   61,   61,   62,   62,   63,   63,   60,   60,
+       60,   60,   60,   60,   60,   60,   64,   60,   60,   65,
+       66,   60,   64,   60,   60,   67,   60,   68,   60,   60,
+       60,   60,   60,   60,   64,   64,   65,   60,   60,   69,
+       66,   60,   60,   67,   60,   68,   60,   60,   60,   60,
+       69,   60,   60,   60,   60,   60,   60,   60,   60,    0,
+       60,   60,   60,   60,   60,   60,   60,   60,   60
+    } ;
+
+static yyconst flex_int16_t yy_nxt[143] =
+    {   0,
+       10,   11,   12,   13,   14,   15,   16,   17,   18,   19,
+       20,   21,   10,   22,   23,   23,   10,   23,   23,   23,
+       23,   23,   23,   29,   30,   29,   30,   33,   34,   33,
+       36,   37,   39,   34,   60,   40,   37,   31,   59,   31,
+       39,   34,   60,   38,   37,   39,   36,   37,   50,   49,
+       58,   49,   54,   54,   49,   55,   49,   50,   57,   55,
+       55,   56,   53,   49,   52,   49,   24,   24,   24,   24,
+       24,   24,   26,   26,   26,   26,   26,   26,   28,   28,
+       28,   28,   28,   28,   35,   35,   35,   41,   41,   41,
+       41,   41,   41,   44,   44,   42,   44,   44,   44,   46,
+
+       38,   48,   46,   46,   46,   51,   47,   51,   45,   43,
+       42,   38,   32,   60,   27,   27,   25,   25,    9,   60,
+       60,   60,   60,   60,   60,   60,   60,   60,   60,   60,
+       60,   60,   60,   60,   60,   60,   60,   60,   60,   60,
+       60,   60
+    } ;
+
+static yyconst flex_int16_t yy_chk[143] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    7,    7,    8,    8,   16,   16,   17,
+       17,   17,   19,   19,   23,   65,   23,    7,   58,    8,
+       34,   34,   35,   38,   35,   36,   36,   36,   39,   38,
+       57,   38,   49,   49,   39,   49,   39,   50,   56,   55,
+       54,   52,   45,   50,   43,   50,   61,   61,   61,   61,
+       61,   61,   62,   62,   62,   62,   62,   62,   63,   63,
+       63,   63,   63,   63,   64,   64,   64,   66,   66,   66,
+       66,   66,   66,   67,   67,   41,   67,   67,   67,   68,
+
+       33,   32,   68,   68,   68,   69,   31,   69,   27,   22,
+       21,   18,   13,    9,    6,    5,    4,    3,   60,   60,
+       60,   60,   60,   60,   60,   60,   60,   60,   60,   60,
+       60,   60,   60,   60,   60,   60,   60,   60,   60,   60,
+       60,   60
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "ttl.l"
+#line 2 "ttl.l"
+#include <Python.h>
+#include "ttl_lexer.h"
+
+
+PyObject *ttl_list = NULL;
+static PyObject *tmp_string = NULL;
+
+void yyerror(const char *str)
+{
+    PyErr_SetString(PyExc_SyntaxError, str);
+}
+
+void add_to(const char *str)
+{
+    PyString_ConcatAndDel(&tmp_string, PyString_FromString(str));
+}
+
+void add_token_str(const char *name, const char *value)
+{
+    PyList_Append(ttl_list, Py_BuildValue("(ss)", name, value));
+}
+
+void add_token(const char *name, PyObject *value)
+{
+    PyList_Append(ttl_list, Py_BuildValue("(sO)", name, value));
+}        
+
+
+#line 507 "flex_ttl.c"
+
+#define INITIAL 0
+#define C_COMMENT 1
+#define C_LONGSTRING 2
+#define C_STRING 3
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+    {
+
+    /* User-defined. Not touched by flex. */
+    YY_EXTRA_TYPE yyextra_r;
+
+    /* The rest are the same as the globals declared in the non-reentrant scanner. */
+    FILE *yyin_r, *yyout_r;
+    size_t yy_buffer_stack_top; /**< index of top of stack. */
+    size_t yy_buffer_stack_max; /**< capacity of stack. */
+    YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+    char yy_hold_char;
+    int yy_n_chars;
+    int yyleng_r;
+    char *yy_c_buf_p;
+    int yy_init;
+    int yy_start;
+    int yy_did_buffer_switch_on_eof;
+    int yy_start_stack_ptr;
+    int yy_start_stack_depth;
+    int *yy_start_stack;
+    yy_state_type yy_last_accepting_state;
+    char* yy_last_accepting_cpos;
+
+    int yylineno_r;
+    int yy_flex_debug_r;
+
+    char *yytext_r;
+    int yy_more_flag;
+    int yy_more_len;
+
+    }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+int yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		int n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 37 "ttl.l"
+
+
+#line 732 "flex_ttl.c"
+
+	if ( !yyg->yy_init )
+		{
+		yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yyg->yy_start )
+			yyg->yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack (yyscanner);
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+		}
+
+		yy_load_buffer_state(yyscanner );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yyg->yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yyg->yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yyg->yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yyg->yy_last_accepting_state = yy_current_state;
+				yyg->yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 61 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_current_state != 60 );
+		yy_cp = yyg->yy_last_accepting_cpos;
+		yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yyg->yy_hold_char;
+			yy_cp = yyg->yy_last_accepting_cpos;
+			yy_current_state = yyg->yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 39 "ttl.l"
+{ add_token_str("prefix", "@prefix"); }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 40 "ttl.l"
+BEGIN(C_COMMENT);
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 41 "ttl.l"
+{ tmp_string = PyString_FromString(""); BEGIN(C_LONGSTRING); }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 42 "ttl.l"
+{ tmp_string = PyString_FromString("");  BEGIN(C_STRING); }
+	YY_BREAK
+case 5:
+/* rule 5 can match eol */
+YY_RULE_SETUP
+#line 43 "ttl.l"
+{ add_token_str("URI_", yytext); }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 44 "ttl.l"
+{ add_token_str("float", yytext); }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 45 "ttl.l"
+{ add_token("number", PyInt_FromLong(atol(yytext))); }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 46 "ttl.l"
+{ add_token_str("prnot", yytext); } 
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 47 "ttl.l"
+{ add_token_str("symbol", yytext); } 
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 48 "ttl.l"
+{ add_token_str(yytext, yytext); } 
+	YY_BREAK
+case 11:
+/* rule 11 can match eol */
+YY_RULE_SETUP
+#line 49 "ttl.l"
+;
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 51 "ttl.l"
+{ 
+    // TODO concat with yytext
+    //printf("yytext = '%u''%u'\n", yytext[0], yytext[1]);
+    PyErr_SetString(PyExc_ValueError, "Unexpected characters");
+    yyerror("Syntax error");
+}
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 58 "ttl.l"
+{ add_token("string", tmp_string); BEGIN(INITIAL); }
+	YY_BREAK
+case 14:
+/* rule 14 can match eol */
+YY_RULE_SETUP
+#line 59 "ttl.l"
+add_to(yytext);
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 60 "ttl.l"
+add_to("\"");
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 62 "ttl.l"
+{ add_token("string", tmp_string); BEGIN(INITIAL); }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 63 "ttl.l"
+add_to("\""); 
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 64 "ttl.l"
+add_to(yytext);
+	YY_BREAK
+case 19:
+/* rule 19 can match eol */
+YY_RULE_SETUP
+#line 66 "ttl.l"
+{ BEGIN(INITIAL); }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 67 "ttl.l"
+;
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 69 "ttl.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+#line 925 "flex_ttl.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(C_COMMENT):
+case YY_STATE_EOF(C_LONGSTRING):
+case YY_STATE_EOF(C_STRING):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yyg->yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state( yyscanner );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+			yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yyg->yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yyg->yy_last_accepting_cpos;
+				yy_current_state = yyg->yy_last_accepting_state;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer( yyscanner ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yyg->yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap(yyscanner ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yyg->yy_c_buf_p =
+					yyg->yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yyg->yy_c_buf_p =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+				yy_current_state = yy_get_previous_state( yyscanner );
+
+				yy_cp = yyg->yy_c_buf_p;
+				yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = yyg->yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			yyg->yy_n_chars, (size_t) num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	if ( yyg->yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart(yyin  ,yyscanner);
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+	}
+
+	yyg->yy_n_chars += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_current_state = yyg->yy_start;
+
+	for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yyg->yy_last_accepting_state = yy_current_state;
+			yyg->yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 61 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+	register int yy_is_jam;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+	register char *yy_cp = yyg->yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yyg->yy_last_accepting_state = yy_current_state;
+		yyg->yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 61 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 60);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (yyscan_t yyscanner)
+#else
+    static int input  (yyscan_t yyscanner)
+#endif
+
+{
+	int c;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	*yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+	if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+			/* This was really a NUL. */
+			*yyg->yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			++yyg->yy_c_buf_p;
+
+			switch ( yy_get_next_buffer( yyscanner ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart(yyin ,yyscanner);
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap(yyscanner ) )
+						return EOF;
+
+					if ( ! yyg->yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput(yyscanner);
+#else
+					return input(yyscanner);
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yyg->yy_c_buf_p;	/* cast for 8-bit char's */
+	*yyg->yy_c_buf_p = '\0';	/* preserve yytext */
+	yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack (yyscanner);
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+	}
+
+	yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+	yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack (yyscanner);
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state(yyscanner );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer(b,file ,yyscanner);
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+	yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file , yyscan_t yyscanner)
+
+{
+	int oerrno = errno;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	yy_flush_buffer(b ,yyscanner);
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack(yyscanner);
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*yyg->yy_c_buf_p = yyg->yy_hold_char;
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		yyg->yy_buffer_stack_top++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state(yyscanner );
+	yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if (yyg->yy_buffer_stack_top > 0)
+		--yyg->yy_buffer_stack_top;
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state(yyscanner );
+		yyg->yy_did_buffer_switch_on_eof = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+	int num_to_alloc;
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+	if (!yyg->yy_buffer_stack) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+								  
+		memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		yyg->yy_buffer_stack_max = num_to_alloc;
+		yyg->yy_buffer_stack_top = 0;
+		return;
+	}
+
+	if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+		yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+								(yyg->yy_buffer_stack,
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								, yyscanner);
+		if ( ! yyg->yy_buffer_stack )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+		yyg->yy_buffer_stack_max = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer(b ,yyscanner );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+    
+	return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, int  _yybytes_len , yyscan_t yyscanner)
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = _yybytes_len + 2;
+	buf = (char *) yyalloc(n ,yyscanner );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer(buf,n ,yyscanner);
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = yyg->yy_hold_char; \
+		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+		yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+		*yyg->yy_c_buf_p = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    
+        if (! YY_CURRENT_BUFFER)
+            return 0;
+    
+    return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int yyget_leng  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE  user_defined , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int  line_number , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* lineno is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "yyset_lineno called with no buffer" , yyscanner); 
+    
+    yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int  column_no , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+        /* column is only valid if an input buffer exists. */
+        if (! YY_CURRENT_BUFFER )
+           yy_fatal_error( "yyset_column called with no buffer" , yyscanner); 
+    
+    yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yyout = out_str ;
+}
+
+int yyget_debug  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug , yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+
+    *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+
+    /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+
+int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+    struct yyguts_t dummy_yyguts;
+
+    yyset_extra (yy_user_defined, &dummy_yyguts);
+
+    if (ptr_yy_globals == NULL){
+        errno = EINVAL;
+        return 1;
+    }
+	
+    *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+	
+    if (*ptr_yy_globals == NULL){
+        errno = ENOMEM;
+        return 1;
+    }
+    
+    /* By setting to 0xAA, we expose bugs in
+    yy_init_globals. Leave at 0x00 for releases. */
+    memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+    
+    yyset_extra (yy_user_defined, *ptr_yy_globals);
+    
+    return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    yyg->yy_buffer_stack = 0;
+    yyg->yy_buffer_stack_top = 0;
+    yyg->yy_buffer_stack_max = 0;
+    yyg->yy_c_buf_p = (char *) 0;
+    yyg->yy_init = 0;
+    yyg->yy_start = 0;
+
+    yyg->yy_start_stack_ptr = 0;
+    yyg->yy_start_stack_depth = 0;
+    yyg->yy_start_stack =  NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (yyscan_t yyscanner)
+{
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state(yyscanner);
+	}
+
+	/* Destroy the stack itself. */
+	yyfree(yyg->yy_buffer_stack ,yyscanner);
+	yyg->yy_buffer_stack = NULL;
+
+    /* Destroy the start condition stack. */
+        yyfree(yyg->yy_start_stack ,yyscanner );
+        yyg->yy_start_stack = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( yyscanner);
+
+    /* Destroy the main struct (reentrant only). */
+    yyfree ( yyscanner , yyscanner );
+    yyscanner = NULL;
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size , yyscan_t yyscanner)
+{
+	return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size , yyscan_t yyscanner)
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+	free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 69 "ttl.l"
+
+
+
+int yywrap(yyscan_t scanner)
+{
+  return 1;
+}
+
+
diff --git a/flex_ttl.h b/flex_ttl.h
new file mode 100644
index 0000000..766e23f
--- /dev/null
+++ b/flex_ttl.h
@@ -0,0 +1,335 @@
+#ifndef yyHEADER_H
+#define yyHEADER_H 1
+#define yyIN_HEADER 1
+
+#line 6 "flex_ttl.h"
+
+#line 8 "flex_ttl.h"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif	/* defined (__STDC__) */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+/* Begin user sect3 */
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define C_COMMENT 1
+#define C_LONGSTRING 2
+#define C_STRING 3
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+int yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 69 "ttl.l"
+
+
+#line 334 "flex_ttl.h"
+#undef yyIN_HEADER
+#endif /* yyHEADER_H */
diff --git a/gtk2gui.c b/gtk2gui.c
index 1bfd05b..def5697 100644
--- a/gtk2gui.c
+++ b/gtk2gui.c
@@ -34,7 +34,6 @@
 #include <lv2dynparam/lv2_rtmempool.h>
 #include <lv2dynparam/host.h>
 #endif
-#include <slv2/lv2_ui.h>
 #include <gtk/gtk.h>
 #include <jack/jack.h>
 
@@ -43,6 +42,7 @@
 #include "lv2_uri_map.h"
 #include "lv2_data_access.h"
 #include "lv2_string_port.h"
+#include "lv2_ui.h"
 #include "lv2_external_ui.h"
 
 #include "list.h"
@@ -51,7 +51,6 @@
 #include "zynjacku.h"
 #include "plugin.h"
 #include "plugin_internal.h"
-#include "plugin_repo.h"
 
 #define LOG_LEVEL LOG_LEVEL_ERROR
 #include "log.h"
@@ -65,15 +64,15 @@
 struct zynjacku_gtk2gui
 {
   const LV2_Feature ** host_features;
-  const char *plugin_uri;
-  char *bundle_path;
+  char * plugin_uri;
+  char * bundle_path;
   unsigned int ports_count;
   struct zynjacku_port ** ports;
-  struct zynjacku_plugin * plugin;
+  struct zynjacku_plugin * plugin_ptr;
   void * context_ptr;
-  const char * synth_id;
+  const char * instance_name;
   bool resizable;
-  void *dlhandle;
+  void * dlhandle;
   const LV2UI_Descriptor * lv2ui;
   LV2UI_Handle ui_handle;
   GtkWidget * widget_ptr;
@@ -106,36 +105,37 @@ zynjacku_gtk2gui_create(
   const LV2_Feature * const * host_features,
   unsigned int host_feature_count,
   zynjacku_lv2_handle plugin_handle,
-  struct zynjacku_plugin *plugin,
-  void *context_ptr,
-  const char *uri,
-  const char *synth_id,
-  const struct list_head *parameter_ports_ptr)
+  struct zynjacku_plugin * plugin_ptr,
+  void * context_ptr,
+  const char * ui_type_uri,
+  const char * plugin_uri,
+  const char * ui_uri,
+  const char * ui_binary_path,
+  const char * ui_bundle_path,
+  const char * plugin_instance_name,
+  const struct list_head * parameter_ports_ptr)
 {
   struct zynjacku_gtk2gui * ui_ptr;
   unsigned int ports_count;
-  struct list_head *node_ptr;
+  struct list_head * node_ptr;
   struct zynjacku_port * port_ptr;
   LV2UI_DescriptorFunction lookup;
   uint32_t index;
-  char * ui_uri;
-  char * ui_binary_path;
-  char * ui_bundle_path;
   unsigned int type;
 
-  if (zynjacku_plugin_repo_get_ui_info(uri, LV2_UI_GTK_URI, &ui_uri, &ui_binary_path, &ui_bundle_path))
+  if (strcmp(ui_type_uri, LV2_UI_GTK_URI) == 0)
   {
-    LOG_NOTICE("GtkUI for '%s'", uri);
+    LOG_NOTICE("GtkUI for '%s'", plugin_uri);
     type = UI_TYPE_GTK;
   }
-  else if (zynjacku_plugin_repo_get_ui_info(uri, LV2_EXTERNAL_UI_URI, &ui_uri, &ui_binary_path, &ui_bundle_path))
+  else if (strcmp(ui_type_uri, LV2_EXTERNAL_UI_URI) == 0)
   {
-    LOG_NOTICE("External UI for '%s'", uri);
+    LOG_NOTICE("External UI for '%s'", plugin_uri);
     type = UI_TYPE_EXTERNAL;
   }
   else
   {
-    LOG_DEBUG("zynjacku_plugin_repo_get_ui_info() failed for '%s'", uri);
+    LOG_DEBUG("Ignoring UI '%s' of plugin '%s', unknown type '%s'", ui_uri, plugin_uri, ui_type_uri);
     goto fail;
   }
 
@@ -143,20 +143,26 @@ zynjacku_gtk2gui_create(
   if (ui_ptr == NULL)
   {
     LOG_ERROR("malloc() failed.");
-    goto fail_free_ui_strings;
+    goto fail;
   }
 
   ui_ptr->type = type;
 
-  ui_ptr->plugin_uri = uri;
-  ui_ptr->plugin = plugin;
+  ui_ptr->plugin_uri = strdup(plugin_uri);
+  if (ui_ptr->plugin_uri == NULL)
+  {
+    LOG_ERROR("strdup(\"%s\") failed", plugin_uri);
+    goto fail_free;
+  }
+
+  ui_ptr->plugin_ptr = plugin_ptr;
   ui_ptr->context_ptr = context_ptr;
-  ui_ptr->synth_id = synth_id;
+  ui_ptr->instance_name = plugin_instance_name;
   ui_ptr->resizable = true;
   ui_ptr->lv2plugin = plugin_handle;
   ui_ptr->data_access.data_access = zynjacku_lv2_get_descriptor(plugin_handle)->extension_data;
   ui_ptr->external_ui.ui_closed = zynjacku_plugin_ui_closed;
-  ui_ptr->external_ui.plugin_human_id = synth_id;
+  ui_ptr->external_ui.plugin_human_id = plugin_instance_name;
 
   ui_ptr->gui_feature_instance_access.URI = "http://lv2plug.in/ns/ext/instance-access";
   ui_ptr->gui_feature_instance_access.data = zynjacku_lv2_get_handle(ui_ptr->lv2plugin);
@@ -180,7 +186,7 @@ zynjacku_gtk2gui_create(
   if (ui_ptr->ports == NULL)
   {
     LOG_ERROR("malloc() failed.");
-    goto fail_free;
+    goto fail_free_plugin_uri;
   }
 
   memset(ui_ptr->ports, 0, ports_count * sizeof(struct zynjacku_port *));
@@ -207,13 +213,18 @@ zynjacku_gtk2gui_create(
   ui_ptr->host_features[host_feature_count++] = &ui_ptr->gui_feature_external_ui;
   ui_ptr->host_features[host_feature_count++] = NULL;
 
-  ui_ptr->bundle_path = ui_bundle_path;
+  ui_ptr->bundle_path = strdup(ui_bundle_path);
+  if (ui_ptr->bundle_path == NULL)
+  {
+    LOG_ERROR("strdup(\"%s\") failed", ui_bundle_path);
+    goto fail_free_features;
+  }
 
   ui_ptr->dlhandle = dlopen(ui_binary_path, RTLD_NOW);
   if (ui_ptr->dlhandle == NULL)
   {
     LOG_WARNING("Cannot load \"%s\": %s", ui_binary_path, dlerror());
-    goto fail_free_features;
+    goto fail_free_bundle_path;
   }
 
   lookup = (LV2UI_DescriptorFunction)dlsym(ui_ptr->dlhandle, "lv2ui_descriptor");
@@ -230,7 +241,7 @@ zynjacku_gtk2gui_create(
     ui_ptr->lv2ui = lookup(index);
     if (ui_ptr->lv2ui == NULL)
     {
-      LOG_ERROR("Did not find UI %s in %s", uri, ui_binary_path);
+      LOG_ERROR("Did not find UI %s in %s", ui_uri, ui_binary_path);
       goto fail_dlclose;
     }
 
@@ -243,27 +254,26 @@ zynjacku_gtk2gui_create(
   ui_ptr->window_ptr = NULL;
   ui_ptr->external_ui_control = NULL;
 
-  free(ui_uri);
-
   return ui_ptr;
 
 fail_dlclose:
   dlclose(ui_ptr->dlhandle);
 
+fail_free_bundle_path:
+  free(ui_ptr->bundle_path);
+
 fail_free_features:
   free(ui_ptr->host_features);
 
 fail_free_ports:
   free(ui_ptr->ports);
 
+fail_free_plugin_uri:
+  free(ui_ptr->plugin_uri);
+
 fail_free:
   free(ui_ptr);
 
-fail_free_ui_strings:
-  free(ui_uri);
-  free(ui_bundle_path);
-  free(ui_binary_path);
-
 fail:
   return NULL;
 }
@@ -329,9 +339,17 @@ zynjacku_gtk2gui_destroy(
   zynjacku_gtk2gui_handle ui_handle)
 {
   LOG_DEBUG("zynjacku_on_gtk2gui_destroy() called");
+
+  if (ui_ptr->ui_handle != NULL &&
+      ui_ptr->type == UI_TYPE_EXTERNAL)
+  {
+    LV2_EXTERNAL_UI_HIDE(ui_ptr->external_ui_control);
+  }
+
   dlclose(ui_ptr->dlhandle);
   free(ui_ptr->ports);
   free(ui_ptr->bundle_path);
+  free(ui_ptr->plugin_uri);
   free(ui_ptr);
 }
 
@@ -353,6 +371,7 @@ zynjacku_gtk2gui_callback_write(
   }
 
   zynjacku_plugin_ui_set_port_value(ui_ptr->ports[port_index]->plugin_ptr, ui_ptr->ports[port_index], buffer, buffer_size);
+  zynjacku_gtk2gui_port_event(ui_ptr, ui_ptr->ports[port_index]);
 }
 
 bool
@@ -424,7 +443,9 @@ zynjacku_gtk2gui_ui_on(
     {
       ui_ptr->window_ptr = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
-      gtk_window_set_title(GTK_WINDOW(ui_ptr->window_ptr), ui_ptr->synth_id);
+      gtk_window_set_title(GTK_WINDOW(ui_ptr->window_ptr), ui_ptr->instance_name);
+
+      gtk_window_set_role(GTK_WINDOW(ui_ptr->window_ptr), "plugin_ui");
 
       gtk_window_set_resizable(GTK_WINDOW(ui_ptr->window_ptr), ui_ptr->resizable);
 
diff --git a/gtk2gui.h b/gtk2gui.h
index cc56bf4..7d602a0 100644
--- a/gtk2gui.h
+++ b/gtk2gui.h
@@ -35,8 +35,12 @@ zynjacku_gtk2gui_create(
   zynjacku_lv2_handle plugin_handle,
   struct zynjacku_plugin * plugin,
   void * context_ptr,
-  const char * uri,
-  const char * synth_id,
+  const char * ui_type_uri,
+  const char * plugin_uri,
+  const char * ui_uri,
+  const char * ui_binary_path,
+  const char * ui_bundle_path,
+  const char * plugin_instance_name,
   const struct list_head * parameter_ports_ptr);
 
 void
diff --git a/logo.png b/logo.png
deleted file mode 100644
index 1ae3f23..0000000
Binary files a/logo.png and /dev/null differ
diff --git a/lv2.c b/lv2.c
index b504424..92cce83 100644
--- a/lv2.c
+++ b/lv2.c
@@ -22,12 +22,14 @@
 
 #include "config.h"
 
+#include <stdio.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <assert.h>
 #include <dlfcn.h>
 #include <string.h>
 #include <stdlib.h>
+#include <errno.h>
 #include <jack/jack.h>
 #include <glib-object.h>
 #include <lv2.h>
@@ -49,7 +51,7 @@
 #include "zynjacku.h"
 //#define LOG_LEVEL LOG_LEVEL_DEBUG
 #include "log.h"
-#include "plugin_repo.h"
+#include "plugin.h"
 
 struct zynjacku_lv2_plugin
 {
@@ -59,34 +61,110 @@ struct zynjacku_lv2_plugin
   LV2_Handle lv2handle;
 };
 
+/* TODO: cleanup allocation dman_dlhandles */
+static void **dman_dlhandles = NULL;
+static size_t dman_dlhandles_count = 0;
+
+char *
+zynjacku_lv2_dman_get(
+  const char * dlpath)
+{
+  void *dlhandle;
+  FILE * (*lv2_dyn_manifest)();
+  void   (*lv2_dyn_manifest_done)(FILE *fp);
+  void *tmp;
+  FILE *fp;
+  long size;
+  char *ret;
+
+  dlhandle = dlopen(dlpath, RTLD_NOW);
+  if (dlhandle == NULL)
+  {
+    LOG_ERROR("Unable to open library %s (%s)", dlpath, dlerror());
+    return NULL;
+  }
+
+  dlerror();
+  lv2_dyn_manifest = dlsym(dlhandle, "lv2_dyn_manifest");
+  if (lv2_dyn_manifest == NULL)
+  {
+    LOG_ERROR("Cannot retrieve dynamic manifest generator function of LV2 plugin %s (%s)", dlpath, dlerror());
+    dlclose(dlhandle);
+    return NULL;
+  }
+
+  dlerror();
+  lv2_dyn_manifest_done = dlsym(dlhandle, "lv2_dyn_manifest_done");
+  if (lv2_dyn_manifest_done == NULL)
+  {
+    LOG_ERROR("Cannot retrieve dynamic manifest destructor function of LV2 plugin %s (%s)", dlpath, dlerror());
+    dlclose(dlhandle);
+    return NULL;
+  }
+
+  fp = lv2_dyn_manifest();
+  if (fp == NULL)
+  {
+    LOG_ERROR("LV2 plugin %s's lv2_dynamic_manifest() returned NULL", dlpath);
+    dlclose(dlhandle);
+    return NULL;
+  }
+
+  if (fseek(fp, 0, SEEK_END) < 0)
+  {
+    LOG_ERROR("Cannot determine the size of dynamic manifest file (%s)", strerror(errno));
+    dlclose(dlhandle);
+    return NULL;
+  }
+  size = ftell(fp);
+  if (size < 0)
+  {
+    LOG_ERROR("Cannot determine the size of dynamic manifest file (%s)", strerror(errno));
+    dlclose(dlhandle);
+    return NULL;
+  }
+  rewind(fp);
+
+  ret = malloc(size + 1);
+  if (ret == NULL)
+  {
+    LOG_ERROR("Failed to allocate memory to store the dynamically generated manifest file");
+    dlclose(dlhandle);
+    return NULL;
+  }
+
+  tmp = realloc(dman_dlhandles, (dman_dlhandles_count + 1) * sizeof(void *));
+  if (tmp == NULL)
+  {
+    LOG_ERROR("Failed to allocate memory for dman_dlhandles");
+    free(ret);
+    dlclose(dlhandle);
+    return NULL;
+  }
+  dman_dlhandles = tmp;
+  dman_dlhandles[dman_dlhandles_count] = dlhandle;
+  dman_dlhandles_count++;
+
+  size = fread(ret, 1, size, fp);
+  ret[size] = '\0';
+  lv2_dyn_manifest_done(fp);
+
+  return ret;
+}
+
 zynjacku_lv2_handle
 zynjacku_lv2_load(
   const char * uri,
+  const char * dlpath,
+  const char * bundle_path,
   double sample_rate,
   const LV2_Feature * const * features)
 {
-  const char *dlpath;
-  const char *bundle_path;
-
   struct zynjacku_lv2_plugin *plugin_ptr;
   LV2_Descriptor_Function lv2lookup;
   char *error;
   uint32_t plugin_index;
 
-  dlpath = zynjacku_plugin_repo_get_dlpath(uri);
-  if (dlpath == NULL)
-  {
-    LOG_ERROR("Failed to get path of library implementeding plugin %s", uri);
-    goto fail;
-  }
-
-  bundle_path = zynjacku_plugin_repo_get_bundle_path(uri);
-  if (bundle_path == NULL)
-  {
-    LOG_ERROR("Failed to get bundle path of plugin %s", uri);
-    goto fail;
-  }
-
   plugin_ptr = malloc(sizeof(struct zynjacku_lv2_plugin));
   if (plugin_ptr == NULL)
   {
@@ -167,8 +245,17 @@ void
 zynjacku_lv2_unload(
   zynjacku_lv2_handle lv2handle)
 {
+  size_t i;
+
   plugin_ptr->lv2->cleanup(plugin_ptr->lv2handle);
   dlclose(plugin_ptr->dlhandle);
+  for (i = 0; i < dman_dlhandles_count; i++)
+    if (dman_dlhandles[i] == plugin_ptr->dlhandle)
+    {
+      dlclose(plugin_ptr->dlhandle);
+      dman_dlhandles[i] = NULL;
+      break;
+    }
   free(plugin_ptr);
 }
 
diff --git a/lv2.h b/lv2.h
index 187694f..5b1eb33 100644
--- a/lv2.h
+++ b/lv2.h
@@ -27,9 +27,15 @@ typedef struct { int _unused; } * zynjacku_lv2_handle;
 
 struct zynjacku_port;
 
+char *
+zynjacku_lv2_dman_get(
+  const char * dlpath);
+
 zynjacku_lv2_handle
 zynjacku_lv2_load(
   const char * uri,
+  const char * dlpath,
+  const char * bundle_path,
   double sample_rate,
   const LV2_Feature * const * host_features);
 
diff --git a/lv2_ui.h b/lv2_ui.h
new file mode 100644
index 0000000..d5b813d
--- /dev/null
+++ b/lv2_ui.h
@@ -0,0 +1,372 @@
+/************************************************************************
+ *
+ * In-process UI extension for LV2
+ *
+ * Copyright (C) 2006-2008 Lars Luthman <lars.luthman at gmail.com>
+ * 
+ * Based on lv2.h, which was
+ *
+ * Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, 
+ *                         Stefan Westerfeld
+ * Copyright (C) 2006 Steve Harris, Dave Robillard.
+ *
+ * This header is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License,
+ * or (at your option) any later version.
+ *
+ * This header is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA.
+ *
+ ***********************************************************************/
+
+/** @file
+    This extension defines an interface that can be used in LV2 plugins and
+    hosts to create UIs for plugins. The UIs are plugins that reside in
+    shared object files in an LV2 bundle and are referenced in the RDF data
+    using the triples (Turtle shown)
+<pre>    
+    @@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .
+    <http://my.plugin>    uiext:ui     <http://my.pluginui> .
+    <http://my.plugin>    a            uiext:GtkUI .
+    <http://my.pluginui>  uiext:binary <myui.so> .
+</pre>
+    where <http://my.plugin> is the URI of the plugin, <http://my.pluginui> is
+    the URI of the plugin UI and <myui.so> is the relative URI to the shared 
+    object file. While it is possible to have the plugin UI and the plugin in 
+    the same shared object file it is probably a good idea to keep them 
+    separate so that hosts that don't want UIs don't have to load the UI code.
+    A UI MUST specify its class in the RDF data, in this case uiext:GtkUI. The
+    class defines what type the UI is, e.g. what graphics toolkit it uses.
+    There are no UI classes defined in this extension, those are specified
+    separately (and anyone can define their own).
+    
+    (Note: the prefix above is used throughout this file for the same URI)
+    
+    It's entirely possible to have multiple UIs for the same plugin, or to have
+    the UI for a plugin in a different bundle from the actual plugin - this
+    way people other than the plugin author can write plugin UIs independently
+    without editing the original plugin bundle.
+    
+    Note that the process that loads the shared object file containing the UI
+    code and the process that loads the shared object file containing the 
+    actual plugin implementation does not have to be the same. There are many
+    valid reasons for having the plugin and the UI in different processes, or
+    even on different machines. This means that you can _not_ use singletons
+    and global variables and expect them to refer to the same objects in the
+    UI and the actual plugin. The function callback interface defined in this
+    header is all you can expect to work.
+    
+    Since the LV2 specification itself allows for extensions that may add 
+    new types of data and configuration parameters that plugin authors may 
+    want to control with a UI, this extension allows for meta-extensions that
+    can extend the interface between the UI and the host. These extensions
+    mirror the extensions used for plugins - there are required and optional
+    "features" that you declare in the RDF data for the UI as
+<pre>    
+    <http://my.pluginui> uiext:requiredFeature <http://my.feature> .
+    <http://my.pluginui> uiext:optionalFeature <http://my.feature> .
+</pre>
+    These predicates have the same semantics as lv2:requiredFeature and 
+    lv2:optionalFeature - if a UI is declaring a feature as required, the
+    host is NOT allowed to load it unless it supports that feature, and if it
+    does support a feature (required or optional) it MUST pass that feature's
+    URI and any additional data (specified by the meta-extension that defines
+    the feature) in a LV2_Feature struct (as defined in lv2.h) to the UI's 
+    instantiate() function.
+    
+    These features may be used to specify how to pass data between the UI
+    and the plugin port buffers - see LV2UI_Write_Function for details.
+    
+    There are four features defined in this extension that hosts may want to
+    implement:
+
+<pre>
+    uiext:makeResident
+</pre>
+    If this feature is required by a UI the host MUST NEVER unload the shared
+    library containing the UI implementation during the lifetime of the host
+    process (e.g. never calling dlclose() on Linux). This feature may be 
+    needed by e.g. a Gtk UI that registers its own Glib types using 
+    g_type_register_static() - if it gets unloaded and then loaded again the 
+    type registration will break, since there is no way to unregister the 
+    types when the library is unloaded. The data pointer in the LV2_Feature
+    for this feature should always be set to NULL.
+
+<pre>
+    uiext:makeSONameResident
+</pre>
+    This feature is ELF specific - it should only be used by UIs that
+    use the ELF file format for the UI shared object files (e.g. on Linux).
+    If it is required by an UI the UI should also list a number of SO names
+    (shared object names) for libraries that the UI shared object
+    depends on and that may not be unloaded during the lifetime of the host
+    process, using the predicate @c uiext:residentSONames, like this:
+<pre>
+    <http://my.pluginui> uiext:residentSONames "libgtkmm-2.4.so.1", "libfoo.so.0"
+</pre>
+    The host MUST then make sure that the shared libraries with the given ELF
+    SO names are not unloaded when the plugin UI is, but stay loaded during
+    the entire lifetime of the host process. On Linux this can be accomplished
+    by calling dlopen() on the shared library file with that SO name and never
+    calling a matching dlclose(). However, if a plugin UI requires the 
+    @c uiext:makeSONameResident feature, it MUST ALWAYS be safe for the host to
+    just never unload the shared object containing the UI implementation, i.e.
+    act as if the UI required the @c uiext:makeResident feature instead. Thus
+    the host only needs to find the shared library files corresponding to the
+    given SO names if it wants to save RAM by unloading the UI shared object 
+    file when it is no longer needed. The data pointer for the LV2_Feature for
+    this feature should always be set to NULL.
+
+<pre>
+    uiext:noUserResize
+</pre>
+    If an UI requires this feature it indicates that it does not make sense
+    to let the user resize the main widget, and the host should prevent that.
+    This feature may not make sense for all UI types. The data pointer for the
+    LV2_Feature for this feature should always be set to NULL.
+
+<pre>
+    uiext:fixedSize
+</pre>
+    If an UI requires this feature it indicates the same thing as 
+    uiext:noUserResize, and additionally it means that the UI will not resize
+    the main widget on its own - it will always remain the same size (e.g. a
+    pixmap based GUI). This feature may not make sense for all UI types.
+    The data pointer for the LV2_Feature for this feature should always be set
+    to NULL.
+    
+    
+    UIs written to this specification do not need to be threadsafe - the 
+    functions defined below may only be called in the same thread as the UI
+    main loop is running in.
+    
+    Note that this UI extension is NOT a lv2:Feature. There is no way for a 
+    plugin to know whether the host that loads it supports UIs or not, and 
+    the plugin must ALWAYS work without the UI (although it may be rather 
+    useless unless it has been configured using the UI in a previous session).
+    
+    A UI does not have to be a graphical widget, it could just as well be a
+    server listening for OSC input or an interface to some sort of hardware
+    device, depending on the RDF class of the UI.
+*/
+
+#ifndef LV2_UI_H
+#define LV2_UI_H
+
+#include "lv2.h"
+
+#define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** A pointer to some widget or other type of UI handle.
+    The actual type is defined by the type URI of the UI.
+    All the functionality provided by this extension is toolkit 
+    independent, the host only needs to pass the necessary callbacks and 
+    display the widget, if possible. Plugins may have several UIs, in various
+    toolkits. */
+typedef void* LV2UI_Widget;
+
+
+/** This handle indicates a particular instance of a UI.
+    It is valid to compare this to NULL (0 for C++) but otherwise the 
+    host MUST not attempt to interpret it. The UI plugin may use it to 
+    reference internal instance data. */
+typedef void* LV2UI_Handle;
+
+
+/** This handle indicates a particular plugin instance, provided by the host.
+    It is valid to compare this to NULL (0 for C++) but otherwise the 
+    UI plugin MUST not attempt to interpret it. The host may use it to 
+    reference internal plugin instance data. */
+typedef void* LV2UI_Controller;
+
+
+/** This is the type of the host-provided function that the UI can use to
+    send data to a plugin's input ports. The @c buffer parameter must point
+    to a block of data, @c buffer_size bytes large. The contents of this buffer
+    and what the host should do with it depends on the value of the @c format
+    parameter.
+    
+    The @c format parameter should either be 0 or a numeric ID for a "Transfer
+    mechanism". Transfer mechanisms are Features and may be defined in 
+    meta-extensions. They specify how to translate the data buffers passed
+    to this function to input data for the plugin ports. If a UI wishes to 
+    write data to an input port, it must list a transfer mechanism Feature 
+    for that port's class as an optional or required feature (depending on 
+    whether the UI will work without being able to write to that port or not).
+    The only exception is when the UI wants to write single float values to 
+    input ports of the class lv2:ControlPort, in which case @c buffer_size 
+    should always be 4, the buffer should always contain a single IEEE-754
+    float, and @c format should be 0.
+    
+    The numeric IDs for the transfer mechanisms are provided by a
+    URI-to-integer mapping function provided by the host, using the URI Map 
+    feature <http://lv2plug.in/ns/ext/uri-map> with the map URI 
+    "http://lv2plug.in/ns/extensions/ui". Thus a UI that requires transfer
+    mechanism features also requires the URI Map feature, but this is 
+    implicit - the UI does not have to list the URI map feature as a required
+    or optional feature in it's RDF data.
+    
+    An UI MUST NOT pass a @c format parameter value (except 0) that has not
+    been returned by the host-provided URI mapping function for a 
+    host-supported transfer mechanism feature URI.
+
+    The UI MUST NOT try to write to a port for which there is no specified
+    transfer mechanism, or to an output port. The UI is responsible for 
+    allocating the buffer and deallocating it after the call.
+*/
+typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller,
+                                     uint32_t         port_index,
+                                     uint32_t         buffer_size,
+                                     uint32_t         format,
+                                     const void*      buffer);
+
+
+/** This struct contains the implementation of an UI. A pointer to an 
+    object of this type is returned by the lv2ui_descriptor() function. 
+*/
+typedef struct _LV2UI_Descriptor {
+  
+  /** The URI for this UI (not for the plugin it controls). */
+  const char* URI;
+  
+  /** Create a new UI object and return a handle to it. This function works
+      similarly to the instantiate() member in LV2_Descriptor.
+      
+      @param descriptor The descriptor for the UI that you want to instantiate.
+      @param plugin_uri The URI of the plugin that this UI will control.
+      @param bundle_path The path to the bundle containing the RDF data file
+                         that references this shared object file, including the
+                         trailing '/'.
+      @param write_function A function provided by the host that the UI can
+                            use to send data to the plugin's input ports.
+      @param controller A handle for the plugin instance that should be passed
+                        as the first parameter of @c write_function.
+      @param widget     A pointer to an LV2UI_Widget. The UI will write a
+                        widget pointer to this location (what type of widget 
+                        depends on the RDF class of the UI) that will be the
+                        main UI widget.
+      @param features   An array of LV2_Feature pointers. The host must pass
+                        all feature URIs that it and the UI supports and any
+                        additional data, just like in the LV2 plugin 
+                        instantiate() function. Note that UI features and plugin
+			features are NOT necessarily the same, they just share
+			the same data structure - this will probably not be the
+			same array as the one the plugin host passes to a 
+			plugin.
+  */
+  LV2UI_Handle (*instantiate)(const struct _LV2UI_Descriptor* descriptor,
+                              const char*                     plugin_uri,
+                              const char*                     bundle_path,
+                              LV2UI_Write_Function            write_function,
+                              LV2UI_Controller                controller,
+                              LV2UI_Widget*                   widget,
+                              const LV2_Feature* const*       features);
+
+  
+  /** Destroy the UI object and the associated widget. The host must not try
+      to access the widget after calling this function.
+   */
+  void (*cleanup)(LV2UI_Handle ui);
+  
+  /** Tell the UI that something interesting has happened at a plugin port.
+      What is interesting and how it is written to the buffer passed to this
+      function is defined by the @c format parameter, which has the same 
+      meaning as in LV2UI_Write_Function. The only exception is ports of the 
+      class lv2:ControlPort, for which this function should be called
+      when the port value changes (it does not have to be called for every 
+      single change if the host's UI thread has problems keeping up with 
+      the thread the plugin is running in), @c buffer_size should be 4 and the 
+      buffer should contain a single IEEE-754 float. In this case the @c format
+      parameter should be 0.
+      
+      By default, the host should only call this function for input ports of
+      the lv2:ControlPort class. However, the default setting can be modified
+      by using the following URIs in the UI's RDF data:
+      <pre>
+      uiext:portNotification
+      uiext:noPortNotification
+      uiext:plugin
+      uiext:portIndex
+      </pre>
+      For example, if you want the UI with uri 
+      <code><http://my.pluginui></code> for the plugin with URI 
+      <code><http://my.plugin></code> to get notified when the value of the 
+      output control port with index 4 changes, you would use the following 
+      in the RDF for your UI:
+      <pre>
+      <http://my.pluginui> uiext:portNotification [ uiext:plugin <http://my.plugin> ;
+                                                      uiext:portIndex 4 ] .
+      </pre>
+      and similarly with <code>uiext:noPortNotification</code> if you wanted
+      to prevent notifications for a port for which it would be on by default 
+      otherwise. The UI is not allowed to request notifications for ports of 
+      types for which no transfer mechanism is specified, if it does it should 
+      be considered broken and the host should not load it.
+      
+      The @c buffer is only valid during the time of this function call, so if 
+      the UI wants to keep it for later use it has to copy the contents to an
+      internal buffer.
+      
+      This member may be set to NULL if the UI is not interested in any 
+      port events.
+  */
+  void (*port_event)(LV2UI_Handle ui,
+                     uint32_t     port_index,
+                     uint32_t     buffer_size,
+                     uint32_t     format,
+                     const void*  buffer);
+  
+  /** Returns a data structure associated with an extension URI, for example
+      a struct containing additional function pointers. Avoid returning
+      function pointers directly since standard C++ has no valid way of
+      casting a void* to a function pointer. This member may be set to NULL
+      if the UI is not interested in supporting any extensions. This is similar
+      to the extension_data() member in LV2_Descriptor.
+  */
+  const void* (*extension_data)(const char*  uri);
+
+} LV2UI_Descriptor;
+
+
+
+/** A plugin UI programmer must include a function called "lv2ui_descriptor"
+    with the following function prototype within the shared object
+    file. This function will have C-style linkage (if you are using
+    C++ this is taken care of by the 'extern "C"' clause at the top of
+    the file). This function will be accessed by the UI host using the 
+    @c dlsym() function and called to get a LV2UI_UIDescriptor for the
+    wanted plugin.
+    
+    Just like lv2_descriptor(), this function takes an index parameter. The
+    index should only be used for enumeration and not as any sort of ID number -
+    the host should just iterate from 0 and upwards until the function returns
+    NULL or a descriptor with an URI matching the one the host is looking for.
+*/
+const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index);
+
+
+/** This is the type of the lv2ui_descriptor() function. */
+typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/lv2rack.py b/lv2rack.py
index f512dd1..3a4c792 100755
--- a/lv2rack.py
+++ b/lv2rack.py
@@ -24,10 +24,29 @@ import gtk.glade
 import gobject
 import re
 import time
+from distutils import sysconfig
 
 old_path = sys.path
-sys.path.insert(0, "%s/.libs" % os.path.dirname(sys.argv[0]))
-import zynjacku_c
+
+inplace_libs = os.path.join(os.path.dirname(sys.argv[0]), ".libs")
+if os.access(inplace_libs, os.R_OK):
+    sys.path.append(inplace_libs)
+else:
+    inplace_libs = None
+
+try:
+    if inplace_libs:
+        import zynjacku_c
+    else:
+        from zynworld import zynjacku_c
+except Exception, e:
+    print "Failed to import zynjacku internal python modules"
+    print repr(e)
+    print "These directories were searched"
+    for path in sys.path:
+        print "    " + path
+    sys.exit(1)
+
 sys.path = old_path
 
 import zynjacku as zynjacku
@@ -39,14 +58,20 @@ except:
     lash = None
 
 class lv2rack(zynjacku.host):
-    def __init__(self, data_dir, glade_xml, client_name, the_license, uris, lash_client):
+    def __init__(self, client_name, preset_extension=None, preset_name=None, lash_client=None):
         #print "lv2rack constructor called."
-        zynjacku.host.__init__(self, zynjacku_c.Rack(), client_name, "lv2rack", "effect stack", lash_client)
-        
-        self.data_dir = data_dir
-        self.glade_xml = glade_xml
 
-        self.main_window = glade_xml.get_widget("lv2rack")
+        zynjacku.host.__init__(self, zynjacku_c.Rack(), client_name, preset_extension, preset_name, lash_client)
+
+class lv2rack_multi(lv2rack):
+    def __init__(self, program_data, client_name, uris, lash_client):
+        #print "lv2rack_multi constructor called."
+        lv2rack.__init__(self, client_name, "lv2rack", "effect stack", lash_client)
+
+        self.program_data = program_data
+        self.glade_xml = program_data['glade_xml']
+
+        self.main_window = self.glade_xml.get_widget("lv2rack")
         self.main_window.set_title(client_name)
 
         self.statusbar = self.glade_xml.get_widget("lv2rack_statusbar")
@@ -62,15 +87,13 @@ class lv2rack(zynjacku.host):
 
         self.signal_ids = []
         for k, v in dic.items():
-            w = glade_xml.get_widget(k)
+            w = self.glade_xml.get_widget(k)
             if not w:
                 print "failed to get glade widget '%s'" % k
                 continue
             self.signal_ids.append([w, w.connect("activate", v)])
 
-        self.the_license = the_license
-
-        self.effects_widget = glade_xml.get_widget("lv2rack_treeview_effects")
+        self.effects_widget = self.glade_xml.get_widget("lv2rack_treeview_effects")
 
         self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
         text_renderer = gtk.CellRendererText()
@@ -113,16 +136,53 @@ class lv2rack(zynjacku.host):
 
         self.store.clear()
 
-        zynjacku.host.__del__(self)
+        lv2rack.__del__(self)
 
     def new_plugin(self, uri, parameters=[], maps={}):
         self.progress_window.show(uri)
-        plugin = zynjacku.host.new_plugin(self, uri, parameters, maps)
+        plugin = lv2rack.new_plugin(self, uri, parameters, maps)
         return plugin
 
     def on_plugin_progress(self, engine, name, progress, message):
         self.progress_window.progress(name, progress, message)
 
+    def check_plugin(self, plugin):
+        audio_in_ports_count = 0
+        audio_out_ports_count = 0
+
+        for port in plugin.ports:
+            if port.__dict__["isAudio"]:
+                if port.__dict__["isInput"]:
+                    audio_in_ports_count += 1
+                    continue
+                if port.__dict__["isOutput"]:
+                    audio_out_ports_count += 1
+                    continue
+                continue
+
+        if audio_in_ports_count == 0 or audio_out_ports_count == 0:
+#             print "Skipping %s (%s), [effect] plugin with unsupported port configuration" % (plugin.name, plugin.uri)
+#             #print "  midi input ports: %d" % midi_in_ports_count
+#             #print "  control ports: %d" % control_ports_count
+#             #print "  string ports: %d" % string_ports_count
+#             #print "  event ports: %d" % event_ports_count
+#             #print "  event midi input ports: %d" % midi_event_in_ports_count
+#             print "  audio input ports: %d" % audio_in_ports_count
+#             print "  audio output ports: %d" % audio_out_ports_count
+#             #print "  total ports %d" % ports_count
+            return False;
+
+#         print "Found effect plugin '%s' %s", (plugin.name, plugin.uri)
+#         #print "  midi input ports: %d" % midi_in_ports_count
+#         #print "  control ports: %d" % control_ports_count
+#         #print "  string ports: %d" % string_ports_count
+#         #print "  event ports: %d" % event_ports_count
+#         #print "  event midi input ports: %d" % midi_event_in_ports_count
+#         print "  audio input ports: %d" % audio_in_ports_count
+#         print "  audio output ports: %d" % audio_out_ports_count
+#         #print "  total ports %d" % ports_count
+        return True;
+
     def load_plugin(self, uri, parameters=[], maps={}):
         statusbar_context_id = self.statusbar.get_context_id("loading plugin")
         statusbar_id = self.statusbar.push(statusbar_context_id, "Loading %s" % uri)
@@ -140,7 +200,7 @@ class lv2rack(zynjacku.host):
     def run(self):
         toggled_connect_id = self.toggle_renderer.connect('toggled', self.on_ui_visible_toggled, self.store)
 
-        zynjacku.host.run(self)
+        lv2rack.run(self)
 
         self.toggle_renderer.disconnect(toggled_connect_id)
 
@@ -166,21 +226,7 @@ class lv2rack(zynjacku.host):
                 self.statusbar.push(statusbar_context_id, "Failed to construct show effect UI")
 
     def on_about(self, widget):
-        about = gtk.AboutDialog()
-        about.set_transient_for(self.main_window)
-        about.set_name("lv2rack")
-        if zynjacku_c.zynjacku_get_version() == "dev":
-            about.set_comments("(development snapshot)")
-        else:
-            about.set_version(zynjacku_c.zynjacku_get_version())
-        about.set_license(self.the_license)
-        about.set_website("http://home.gna.org/zynjacku/")
-        about.set_authors(["Nedko Arnaudov"])
-        #about.set_artists(["Thorsten Wilms"])
-        #about.set_logo(gtk.gdk.pixbuf_new_from_file("%s/logo.png" % self.data_dir))
-        about.show()
-        about.run()
-        about.hide()
+        zynjacku.run_about_dialog(self.main_window, self.program_data)
 
     def on_preset_load(self, widget):
         self.preset_load_ask()
@@ -205,7 +251,7 @@ class lv2rack(zynjacku.host):
 
         progressbar.show()
         progressbar.set_fraction(progress)
-        progressbar.set_text("Checking %s" % uri);
+        progressbar.set_text("Checking %s" % uri)
         while gtk.events_pending():
             gtk.main_iteration()
 
@@ -213,14 +259,57 @@ class lv2rack(zynjacku.host):
         self.plugins_load("LV2 effect plugins")
 
     def on_effect_clear(self, widget):
-        self.store.clear();
+        self.store.clear()
         self.clear_plugins()
 
+class lv2rack_single(lv2rack):
+    def __init__(self, program_data, client_name, uri):
+        #print "ZynjackuHostOne constructor called."
+        lv2rack.__init__(self, client_name, "lv2rack")
+
+        self.glade_xml = program_data['glade_xml']
+
+        self.plugin = self.new_plugin(uri)
+        if not self.plugin:
+            print"Failed to construct %s" % uri
+            return
+
+        if not lv2rack.create_plugin_ui(self, self.plugin):
+            print"Failed to create synth window"
+            return
+
+    def new_plugin(self, uri, parameters=[], maps={}):
+        self.progress_window.show(uri)
+        plugin = lv2rack.new_plugin(self, uri, parameters, maps)
+        self.progress_window.hide()
+        return plugin
+
+    def on_plugin_progress(self, engine, name, progress, message):
+        self.progress_window.progress(name, progress, message)
+
+    def on_plugin_ui_window_destroyed(self, window, synth, row):
+        gtk.main_quit()
+
+    def run(self):
+        if not self.plugin:
+            self.run_done()
+            return
+
+        self.plugin.ui_win.show()
+        lv2rack.run(self)
+
+    def __del__(self):
+        #print "lv2rack_single destructor called."
+
+        lv2rack.__del__(self)
+
 def main():
-    data_dir, glade_xml, the_license = zynjacku.file_setup()
+    program_data = zynjacku.get_program_data('lv2rack')
 
     zynjacku.register_types()
 
+    client_name = "lv2rack"
+
     if lash:                        # If LASH python bindings are available
         # sys.argv is modified by this call
         lash_client = lash.init(sys.argv, "lv2rack", lash.LASH_Config_File)
@@ -234,7 +323,12 @@ def main():
     if lash_client:
         print "Successfully connected to LASH server at " +  lash.lash_get_server_name(lash_client)
 
-    lv2rack(data_dir, glade_xml, "lv2rack", the_license, sys.argv[1:], lash_client).run()
+    if len(sys.argv) == 2 and sys.argv[1][-9:] != ".lv2rack":
+        host = lv2rack_single(program_data, client_name, sys.argv[1])
+    else:
+        host = lv2rack_multi(program_data, client_name, sys.argv[1:], lash_client)
+
+    host.run()
 
     sys.stdout.flush()
     sys.stderr.flush()
diff --git a/plugin.c b/plugin.c
index 34c61d1..2124798 100644
--- a/plugin.c
+++ b/plugin.c
@@ -23,16 +23,23 @@
 
 #include "config.h"
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
+#include <assert.h>
 #include <locale.h>
 #include <unistd.h>
-#include <slv2/slv2.h>
-//#include <slv2/query.h>
 #include <jack/jack.h>
 #include <jack/midiport.h>
 #include <glib-object.h>
 
+#include <lv2.h>
+#if HAVE_DYNPARAMS
+#include <lv2dynparam/lv2dynparam.h>
+#include <lv2dynparam/lv2_rtmempool.h>
+#include <lv2dynparam/host.h>
+#endif
+
 #include "lv2-miditype.h"
 #include "lv2_event.h"
 #include "lv2_uri_map.h"
@@ -40,11 +47,6 @@
 #include "list.h"
 //#define LOG_LEVEL LOG_LEVEL_DEBUG
 #include "log.h"
-#if HAVE_DYNPARAMS
-#include <lv2dynparam/lv2dynparam.h>
-#include <lv2dynparam/lv2_rtmempool.h>
-#include <lv2dynparam/host.h>
-#endif
 
 #include "plugin.h"
 #include "engine.h"
@@ -59,9 +61,6 @@
 
 #include "zynjacku.h"
 #include "plugin_internal.h"
-#include "plugin_repo.h"
-#include "synth.h"
-#include "effect.h"
 #include "midi_cc_map.h"
 #include "midi_cc_map_internal.h"
 
@@ -84,6 +83,9 @@
 
 /* properties */
 #define ZYNJACKU_PLUGIN_PROP_URI                1
+#define ZYNJACKU_PLUGIN_PROP_DLPATH             2
+#define ZYNJACKU_PLUGIN_PROP_BUNDLE_PATH        3
+#define ZYNJACKU_PLUGIN_PROP_NAME               4
 
 static guint g_zynjacku_plugin_signals[ZYNJACKU_PLUGIN_SIGNALS_COUNT];
 
@@ -189,6 +191,24 @@ zynjacku_plugin_dispose(GObject * obj)
     plugin_ptr->uri = NULL;
   }
 
+  if (plugin_ptr->dlpath != NULL)
+  {
+    g_free(plugin_ptr->dlpath);
+    plugin_ptr->dlpath = NULL;
+  }
+
+  if (plugin_ptr->bundle_path != NULL)
+  {
+    g_free(plugin_ptr->bundle_path);
+    plugin_ptr->bundle_path = NULL;
+  }
+
+  if (plugin_ptr->name != NULL)
+  {
+    g_free(plugin_ptr->name);
+    plugin_ptr->name = NULL;
+  }
+
   /* Chain up to the parent class */
   G_OBJECT_CLASS(g_type_class_peek_parent(G_OBJECT_GET_CLASS(obj)))->dispose(obj);
 }
@@ -233,6 +253,36 @@ zynjacku_plugin_set_property(
     plugin_ptr->uri = g_value_dup_string(value_ptr);
     LOG_DEBUG("plugin uri set to: \"%s\"", plugin_ptr->uri);
     break;
+  case ZYNJACKU_PLUGIN_PROP_DLPATH:
+    //LOG_DEBUG("setting plugin dlpath to: \"%s\"", g_value_get_string(value_ptr));
+    //break;
+    if (plugin_ptr->dlpath != NULL)
+    {
+      g_free(plugin_ptr->dlpath);
+    }
+    plugin_ptr->dlpath = g_value_dup_string(value_ptr);
+    LOG_DEBUG("plugin dlpath set to: \"%s\"", plugin_ptr->dlpath);
+    break;
+  case ZYNJACKU_PLUGIN_PROP_BUNDLE_PATH:
+    //LOG_DEBUG("setting plugin bundle path to: \"%s\"", g_value_get_string(value_ptr));
+    //break;
+    if (plugin_ptr->bundle_path != NULL)
+    {
+      g_free(plugin_ptr->bundle_path);
+    }
+    plugin_ptr->bundle_path = g_value_dup_string(value_ptr);
+    LOG_DEBUG("plugin bundle path set to: \"%s\"", plugin_ptr->bundle_path);
+    break;
+  case ZYNJACKU_PLUGIN_PROP_NAME:
+    //LOG_DEBUG("setting plugin name to: \"%s\"", g_value_get_string(value_ptr));
+    //break;
+    if (plugin_ptr->name != NULL)
+    {
+      g_free(plugin_ptr->name);
+    }
+    plugin_ptr->name = g_value_dup_string(value_ptr);
+    LOG_DEBUG("plugin name set to: \"%s\"", plugin_ptr->name);
+    break;
   default:
     /* We don't have any other property... */
     G_OBJECT_WARN_INVALID_PROPERTY_ID(object_ptr, property_id, param_spec_ptr);
@@ -263,6 +313,36 @@ zynjacku_plugin_get_property(
       g_value_set_string(value_ptr, "");
     }
     break;
+  case ZYNJACKU_PLUGIN_PROP_DLPATH:
+    if (plugin_ptr->dlpath != NULL)
+    {
+      g_value_set_string(value_ptr, plugin_ptr->dlpath);
+    }
+    else
+    {
+      g_value_set_string(value_ptr, "");
+    }
+    break;
+  case ZYNJACKU_PLUGIN_PROP_BUNDLE_PATH:
+    if (plugin_ptr->bundle_path != NULL)
+    {
+      g_value_set_string(value_ptr, plugin_ptr->bundle_path);
+    }
+    else
+    {
+      g_value_set_string(value_ptr, "");
+    }
+    break;
+  case ZYNJACKU_PLUGIN_PROP_NAME:
+    if (plugin_ptr->name != NULL)
+    {
+      g_value_set_string(value_ptr, plugin_ptr->name);
+    }
+    else
+    {
+      g_value_set_string(value_ptr, "");
+    }
+    break;
   default:
     /* We don't have any other property... */
     G_OBJECT_WARN_INVALID_PROPERTY_ID(object_ptr, property_id, param_spec_ptr);
@@ -275,7 +355,7 @@ zynjacku_plugin_class_init(
   gpointer class_ptr,
   gpointer class_data_ptr)
 {
-  GParamSpec * uri_param_spec;
+  GParamSpec * param_spec;
 
   LOG_DEBUG("zynjacku_plugin_class_init() called.");
 
@@ -509,17 +589,53 @@ zynjacku_plugin_class_init(
   G_OBJECT_CLASS(class_ptr)->get_property = zynjacku_plugin_get_property;
   G_OBJECT_CLASS(class_ptr)->set_property = zynjacku_plugin_set_property;
 
-  uri_param_spec = g_param_spec_string(
+  param_spec = g_param_spec_string(
     "uri",
-    "Plugin LV2 URI construct property",
-    "Plugin LV2 URI construct property",
+    "Plugin URI",
+    "Plugin URI",
     "" /* default value */,
     G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
 
   g_object_class_install_property(
     G_OBJECT_CLASS(class_ptr),
     ZYNJACKU_PLUGIN_PROP_URI,
-    uri_param_spec);
+    param_spec);
+
+  param_spec = g_param_spec_string(
+    "dlpath",
+    "Path to plugin binary",
+    "Path to plugin binary",
+    "" /* default value */,
+    G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
+
+  g_object_class_install_property(
+    G_OBJECT_CLASS(class_ptr),
+    ZYNJACKU_PLUGIN_PROP_DLPATH,
+    param_spec);
+
+  param_spec = g_param_spec_string(
+    "bundle_path",
+    "Path to plugin bundle",
+    "Path to plugin bundle",
+    "" /* default value */,
+    G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
+
+  g_object_class_install_property(
+    G_OBJECT_CLASS(class_ptr),
+    ZYNJACKU_PLUGIN_PROP_BUNDLE_PATH,
+    param_spec);
+
+  param_spec = g_param_spec_string(
+    "name",
+    "Plugin name",
+    "Plugin name",
+    "" /* default value */,
+    G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
+
+  g_object_class_install_property(
+    G_OBJECT_CLASS(class_ptr),
+    ZYNJACKU_PLUGIN_PROP_NAME,
+    param_spec);
 }
 
 static void
@@ -534,16 +650,21 @@ zynjacku_plugin_init(
   plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(instance);
 
   plugin_ptr->dispose_has_run = FALSE;
+  INIT_LIST_HEAD(&plugin_ptr->midi_ports);
+  INIT_LIST_HEAD(&plugin_ptr->audio_ports);
   INIT_LIST_HEAD(&plugin_ptr->parameter_ports);
   INIT_LIST_HEAD(&plugin_ptr->measure_ports);
 #if HAVE_DYNPARAMS
   INIT_LIST_HEAD(&plugin_ptr->dynparam_ports);
 #endif
 
-  plugin_ptr->type = PLUGIN_TYPE_UNKNOWN;
-
   plugin_ptr->uri = NULL;
+  plugin_ptr->name = NULL;
+  plugin_ptr->id = NULL;
+  plugin_ptr->dlpath = NULL;
+  plugin_ptr->bundle_path = NULL;
   plugin_ptr->lv2plugin = NULL;
+  plugin_ptr->gtk2gui = ZYNJACKU_GTK2GUI_HANDLE_INVALID_VALUE;
 
   plugin_ptr->root_group_ui_context = NULL;
 }
@@ -708,41 +829,42 @@ zynjacku_plugin_generic_lv2_ui_off(
 }
 
 gboolean
-zynjacku_plugin_supports_generic_ui(
-  ZynjackuPlugin * plugin_obj_ptr)
-{
-//  struct zynjacku_plugin * plugin_ptr;
-
-  LOG_DEBUG("zynjacku_plugin_supports_generic_ui() called.");
-
-//  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
-
-  /* we can generate it always */
-  return TRUE;
-}
-
-gboolean
-zynjacku_plugin_supports_custom_ui(
-  ZynjackuPlugin * plugin_obj_ptr)
+zynjacku_plugin_ui_on(
+  ZynjackuPlugin * plugin_obj_ptr,
+  const char * ui_uri,
+  const char * ui_type_uri,
+  const char * ui_binary_path,
+  const char * ui_bundle_path)
 {
   struct zynjacku_plugin * plugin_ptr;
+  const LV2_Feature * const * host_features;
+  unsigned int host_feature_count;
 
-  LOG_DEBUG("zynjacku_plugin_supports_custom_ui() called.");
+  LOG_DEBUG("zynjacku_plugin_ui_on(%s, %s, %s, %s) called.", ui_uri, ui_type_uri, ui_binary_path, ui_bundle_path);
 
   plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
 
-  return (plugin_ptr->gtk2gui != ZYNJACKU_GTK2GUI_HANDLE_INVALID_VALUE) ? TRUE : FALSE;
-}
-
-gboolean
-zynjacku_plugin_ui_on(
-  ZynjackuPlugin * plugin_obj_ptr)
-{
-  struct zynjacku_plugin * plugin_ptr;
-
-  LOG_DEBUG("zynjacku_plugin_ui_on() called.");
+  if (ui_uri != NULL &&
+      ui_type_uri != NULL &&
+      ui_binary_path != NULL &&
+      ui_bundle_path != NULL)
+  {
+    plugin_ptr->get_required_features(plugin_ptr->engine_object_ptr, &host_features, &host_feature_count);
 
-  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+    plugin_ptr->gtk2gui = zynjacku_gtk2gui_create(
+      host_features,
+      host_feature_count,
+      plugin_ptr->lv2plugin,
+      plugin_ptr,
+      plugin_obj_ptr,
+      ui_type_uri,
+      plugin_ptr->uri,
+      ui_uri,
+      ui_binary_path,
+      ui_bundle_path,
+      plugin_ptr->id,
+      &plugin_ptr->parameter_ports);
+  }
 
   if (plugin_ptr->gtk2gui != ZYNJACKU_GTK2GUI_HANDLE_INVALID_VALUE)
   {
@@ -750,6 +872,7 @@ zynjacku_plugin_ui_on(
   }
 
 #if HAVE_DYNPARAMS
+  LOG_DEBUG("dynparams is %s by plugin.", plugin_ptr->dynparams ? "supported" : "unsupported");
   if (plugin_ptr->dynparams)
   {
     lv2dynparam_host_ui_on(plugin_ptr->dynparams);
@@ -872,8 +995,8 @@ zynjacku_connect_plugin_ports(
       switch (port_ptr->type)
       {
       case PORT_TYPE_LV2_FLOAT:
-        zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, port_ptr, &port_ptr->data.lv2float);
-        LOG_INFO("Set %s to %f", port_ptr->symbol, port_ptr->data.lv2float);
+        zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, port_ptr, &port_ptr->data.lv2float.value);
+        LOG_INFO("Set %s to %f", port_ptr->symbol, port_ptr->data.lv2float.value);
         break;
       case PORT_TYPE_LV2_STRING:
         zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, port_ptr, &port_ptr->data.lv2string);
@@ -898,7 +1021,7 @@ zynjacku_connect_plugin_ports(
       switch (port_ptr->type)
       {
       case PORT_TYPE_LV2_FLOAT:
-        zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, port_ptr, &port_ptr->data.lv2float);
+        zynjacku_lv2_connect_port(plugin_ptr->lv2plugin, port_ptr, &port_ptr->data.lv2float.value);
         break;
       case PORT_TYPE_LV2_STRING:
         /* TODO measure string ports are broken for now */
@@ -917,7 +1040,7 @@ zynjacku_connect_plugin_ports(
       switch (port_ptr->type)
       {
       case PORT_TYPE_LV2_FLOAT:
-        send_message(plugin_ptr, port_ptr, &port_ptr->data.lv2float);
+        send_message(plugin_ptr, port_ptr, &port_ptr->data.lv2float.value);
         break;
       case PORT_TYPE_LV2_STRING:
         send_message(plugin_ptr, port_ptr, &port_ptr->data.lv2string);
@@ -934,25 +1057,63 @@ void
 zynjacku_free_port(
   struct zynjacku_port * port_ptr)
 {
-  assert(port_ptr->type == PORT_TYPE_LV2_FLOAT || port_ptr->type == PORT_TYPE_LV2_STRING);
-
   if (port_ptr->type == PORT_TYPE_LV2_STRING)
   {
     free(port_ptr->data.lv2string.data);
   }
 
+  if (port_ptr->name != NULL)
+  {
+    free(port_ptr->name);
+  }
+
   free(port_ptr->symbol);
-  free(port_ptr->name);
   free(port_ptr);
 }
 
 void
-zynjacku_free_plugin_ports(
-  struct zynjacku_plugin * plugin_ptr)
+zynjacku_plugin_destruct(
+  ZynjackuPlugin * plugin_obj_ptr)
 {
+  struct zynjacku_plugin * plugin_ptr;
   struct list_head * node_ptr;
   struct zynjacku_port * port_ptr;
 
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  LOG_DEBUG("Destructing plugin <%s>", plugin_ptr->uri);
+
+  plugin_ptr->deactivate(G_OBJECT(plugin_obj_ptr));
+
+  if (plugin_ptr->gtk2gui != ZYNJACKU_GTK2GUI_HANDLE_INVALID_VALUE)
+  {
+    zynjacku_gtk2gui_destroy(plugin_ptr->gtk2gui);
+  }
+
+  while (!list_empty(&plugin_ptr->midi_ports))
+  {
+    node_ptr = plugin_ptr->midi_ports.next;
+    port_ptr = list_entry(node_ptr, struct zynjacku_port, plugin_siblings);
+
+    list_del(node_ptr);
+
+    zynjacku_free_port(port_ptr);
+  }
+
+  while (!list_empty(&plugin_ptr->audio_ports))
+  {
+    node_ptr = plugin_ptr->audio_ports.next;
+    port_ptr = list_entry(node_ptr, struct zynjacku_port, plugin_siblings);
+
+    assert(port_ptr->type == PORT_TYPE_AUDIO);
+
+    plugin_ptr->unregister_port(plugin_ptr->engine_object_ptr, port_ptr);
+
+    list_del(node_ptr);
+
+    zynjacku_free_port(port_ptr);
+  }
+
   while (!list_empty(&plugin_ptr->parameter_ports))
   {
     node_ptr = plugin_ptr->parameter_ports.next;
@@ -990,62 +1151,6 @@ zynjacku_free_plugin_ports(
     free(port_ptr);
   }
 #endif
-}
-
-gboolean
-zynjacku_plugin_construct(
-  ZynjackuPlugin * plugin_obj_ptr,
-  GObject * engine_object_ptr)
-{
-  struct zynjacku_plugin * plugin_ptr;
-
-  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
-
-  if (plugin_ptr->uri == NULL)
-  {
-    LOG_ERROR("\"uri\" property needs to be set before constructing plugin");
-    return false;
-  }
-
-  if (ZYNJACKU_IS_ENGINE(engine_object_ptr))
-  {
-    return zynjacku_plugin_construct_synth(
-      plugin_ptr,
-      plugin_obj_ptr,
-      engine_object_ptr);
-  }
-
-  if (ZYNJACKU_IS_RACK(engine_object_ptr))
-  {
-    return zynjacku_plugin_construct_effect(
-      plugin_ptr,
-      plugin_obj_ptr,
-      engine_object_ptr);
-  }
-
-  LOG_ERROR("Cannot construct plugin for unknown engine type");
-
-  return false;
-}
-
-void
-zynjacku_plugin_destruct(
-  ZynjackuPlugin * plugin_obj_ptr)
-{
-  struct zynjacku_plugin * plugin_ptr;
-
-  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
-
-  LOG_DEBUG("Destructing plugin <%s>", plugin_ptr->uri);
-
-  plugin_ptr->deactivate(G_OBJECT(plugin_obj_ptr));
-
-  if (plugin_ptr->gtk2gui != ZYNJACKU_GTK2GUI_HANDLE_INVALID_VALUE)
-  {
-    zynjacku_gtk2gui_destroy(plugin_ptr->gtk2gui);
-  }
-
-  plugin_ptr->free_ports(G_OBJECT(plugin_obj_ptr));
 
 #if HAVE_DYNPARAMS
   if (plugin_ptr->dynparams != NULL)
@@ -1181,8 +1286,12 @@ zynjacku_plugin_set_midi_cc_map_internal(
 
   if (plugin_ptr->set_midi_cc_map == NULL)
   {
-    LOG_ERROR("Cannot set midi cc map for plugin without engine");
-    assert(0);
+    if (midi_cc_map_obj_ptr != NULL)
+    {
+      LOG_ERROR("Cannot set midi cc map for plugin without engine");
+      assert(0);
+    }
+
     return false;
   }
 
@@ -1749,6 +1858,341 @@ zynjacku_plugin_set_midi_cc_map(
   return zynjacku_plugin_set_midi_cc_map_internal(port_ptr, midi_cc_map_obj_ptr);
 }
 
+void
+zynjacku_plugin_add_supported_feature(
+  ZynjackuPlugin * plugin_obj_ptr,
+  const gchar * feature_uri)
+{
+  struct zynjacku_plugin * plugin_ptr;
+
+  LOG_DEBUG("Plugin supports feature '%s'", feature_uri);
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+#if HAVE_DYNPARAMS
+  if (strcmp(feature_uri, LV2DYNPARAM_URI) == 0)
+  {
+    plugin_ptr->dynparams_supported = true;
+  }
+#endif
+}
+
+static
+struct zynjacku_port *
+new_lv2parameter_port(
+  uint32_t index,
+  const char * symbol,
+  const char * name,
+  unsigned int type,
+  bool input,
+  bool msgcontext,
+  struct zynjacku_plugin * plugin_ptr)
+{
+  struct zynjacku_port * port_ptr;
+
+  port_ptr = malloc(sizeof(struct zynjacku_port));
+  if (port_ptr == NULL)
+  {
+    LOG_ERROR("malloc() failed to allocate memory for struct zynjacku_port.");
+    goto fail;
+  }
+
+  port_ptr->index = index;
+  port_ptr->type = type;
+  port_ptr->flags = 0;
+  port_ptr->ui_context = NULL;
+  port_ptr->plugin_ptr = plugin_ptr;
+  port_ptr->midi_cc_map_obj_ptr = NULL;
+
+  port_ptr->symbol = strdup(symbol);
+  if (port_ptr->symbol == NULL)
+  {
+    LOG_ERROR("strdup() failed.");
+    goto fail_free_port;
+  }
+
+  if (name != NULL)
+  {
+    port_ptr->name = strdup(name);
+    if (port_ptr->name == NULL)
+    {
+      LOG_ERROR("strdup() failed.");
+      goto fail_free_symbol;
+    }
+  }
+  else
+  {
+    port_ptr->name = NULL;
+  }
+
+  if (!input)
+  {
+    port_ptr->flags |= PORT_FLAGS_OUTPUT;
+  }
+
+  if (msgcontext)
+  {
+    port_ptr->flags |= PORT_FLAGS_MSGCONTEXT;
+  }
+
+  return port_ptr;
+      
+fail_free_symbol:
+  free(port_ptr->symbol);
+
+fail_free_port:
+  free(port_ptr);
+
+fail:
+  return NULL;
+}
+
+gboolean
+zynjacku_plugin_create_oldmidi_input_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol)
+{
+  struct zynjacku_plugin * plugin_ptr;
+  struct zynjacku_port * port_ptr;
+
+  LOG_DEBUG("zynjacku_plugin_create_oldmidi_input_port(%u, %s).", (unsigned int)port_index, symbol);
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  port_ptr = new_lv2parameter_port(
+    port_index,
+    symbol,
+    NULL,
+    PORT_TYPE_MIDI,
+    true, /* input port */
+    false,
+    plugin_ptr);
+  if (port_ptr == NULL)
+  {
+    return false;
+  }
+
+  port_ptr->data.audio = NULL;
+
+  list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->midi_ports);
+
+  return true;
+}
+
+gboolean
+zynjacku_plugin_create_eventmidi_input_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol)
+{
+  struct zynjacku_plugin * plugin_ptr;
+  struct zynjacku_port * port_ptr;
+
+  LOG_DEBUG("zynjacku_plugin_create_eventmidi_input_port(%u, %s).", (unsigned int)port_index, symbol);
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  port_ptr = new_lv2parameter_port(
+    port_index,
+    symbol,
+    NULL,
+    PORT_TYPE_EVENT_MIDI,
+    true, /* input port */
+    false,
+    plugin_ptr);
+  if (port_ptr == NULL)
+  {
+    return false;
+  }
+
+  list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->midi_ports);
+
+  return true;
+}
+
+gboolean
+zynjacku_plugin_create_audio_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  gboolean input)
+{
+  struct zynjacku_plugin * plugin_ptr;
+  struct zynjacku_port * port_ptr;
+
+  LOG_DEBUG("zynjacku_plugin_create_audio_port(%u, %s, %s).", (unsigned int)port_index, symbol, input ? "input" : "output");
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  port_ptr = new_lv2parameter_port(
+    port_index,
+    symbol,
+    NULL,
+    PORT_TYPE_AUDIO,
+    input,
+    false,
+    plugin_ptr);
+  if (port_ptr == NULL)
+  {
+    return false;
+  }
+
+  port_ptr->data.audio = NULL;
+
+  list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->audio_ports);
+
+  return true;
+}
+
+gboolean
+zynjacku_plugin_create_float_parameter_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  const gchar * name,
+  gboolean msgcontext,
+  gboolean default_provided,
+  gfloat default_value,
+  gboolean min_provided,
+  gfloat min_value,
+  gboolean max_provided,
+  gfloat max_value)
+{
+  struct zynjacku_plugin * plugin_ptr;
+  struct zynjacku_port * port_ptr;
+
+  LOG_DEBUG("zynjacku_plugin_create_float_parameter_port(%u, %s, %s).", (unsigned int)port_index, symbol, name);
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  port_ptr = new_lv2parameter_port(
+    port_index,
+    symbol,
+    name,
+    PORT_TYPE_LV2_FLOAT,
+    true, /* input port */
+    msgcontext,
+    plugin_ptr);
+  if (port_ptr == NULL)
+  {
+    return false;
+  }
+
+  if (default_provided)
+  {
+    port_ptr->data.lv2float.value = default_value;
+  }
+  else if (min_provided)
+  {
+    port_ptr->data.lv2float.value = min_value;
+  }
+  else if (max_provided)
+  {
+    port_ptr->data.lv2float.value = min_value;
+  }
+  else
+  {
+    port_ptr->data.lv2float.value = 0.0;
+  }
+
+  port_ptr->data.lv2float.min_provided = min_provided;
+  port_ptr->data.lv2float.min = min_value;
+  port_ptr->data.lv2float.max_provided = max_provided;
+  port_ptr->data.lv2float.max = max_value;
+
+  list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->parameter_ports);
+
+  return true;
+}
+
+gboolean
+zynjacku_plugin_create_float_measure_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  gboolean msgcontext)
+{
+  struct zynjacku_plugin * plugin_ptr;
+  struct zynjacku_port * port_ptr;
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  port_ptr = new_lv2parameter_port(
+    port_index,
+    symbol,
+    NULL,
+    PORT_TYPE_LV2_FLOAT,
+    false, /* output port */
+    msgcontext,
+    plugin_ptr);
+  if (port_ptr == NULL)
+  {
+    return false;
+  }
+
+  list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->measure_ports);
+
+  return true;
+}
+
+gboolean
+zynjacku_plugin_create_string_parameter_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  const gchar * name,
+  gboolean msgcontext,
+  const gchar * default_value,
+  gsize maxlen)
+{
+  struct zynjacku_plugin * plugin_ptr;
+  struct zynjacku_port * port_ptr;
+  size_t defval_len;
+
+  LOG_DEBUG("zynjacku_plugin_create_string_parameter_port(%u, %s, %s, %s, %s).", (unsigned int)port_index, symbol, name, msgcontext ? "true" : "false", default_value);
+
+  plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_obj_ptr);
+
+  port_ptr = new_lv2parameter_port(
+    port_index,
+    symbol,
+    name,
+    PORT_TYPE_LV2_STRING,
+    true, /* input port */
+    msgcontext,
+    plugin_ptr);
+  if (port_ptr == NULL)
+  {
+    return false;
+  }
+
+  port_ptr->data.lv2string.storage = maxlen;
+
+  if (default_value == NULL)
+  {
+    default_value = "";
+  }
+
+  defval_len = strlen(default_value) + 1;
+
+  if (defval_len > port_ptr->data.lv2string.storage)
+  {
+    port_ptr->data.lv2string.storage = defval_len;
+  }
+
+  port_ptr->data.lv2string.data = malloc(port_ptr->data.lv2string.storage);
+  memcpy(port_ptr->data.lv2string.data, default_value, defval_len);
+
+  port_ptr->data.lv2string.len = defval_len - 1;
+  port_ptr->data.lv2string.flags = LV2_STRING_DATA_CHANGED_FLAG;
+  port_ptr->data.lv2string.pad = 0;
+
+  list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->parameter_ports);
+
+  return true;
+}
+
 gboolean
 zynjacku_plugin_midi_cc_map_cc_no_assign(
   GObject * plugin_obj_ptr,
diff --git a/plugin.h b/plugin.h
index ec276b3..91c943c 100644
--- a/plugin.h
+++ b/plugin.h
@@ -50,23 +50,10 @@ struct _ZynjackuPluginClass {
 /* used by ZYNJACKU_TYPE_PLUGIN */
 GType zynjacku_plugin_get_type();
 
-gboolean
-zynjacku_plugin_construct(
-  ZynjackuPlugin * plugin_obj_ptr,
-  GObject * engine_obj_ptr);
-
 void
 zynjacku_plugin_destruct(
   ZynjackuPlugin * plugin_obj_ptr);
 
-gboolean
-zynjacku_plugin_supports_generic_ui(
-  ZynjackuPlugin * plugin_obj_ptr);
-
-gboolean
-zynjacku_plugin_supports_custom_ui(
-  ZynjackuPlugin * plugin_obj_ptr);
-
 const char *
 zynjacku_plugin_get_instance_name(
   ZynjackuPlugin * obj_ptr);
@@ -81,7 +68,11 @@ zynjacku_plugin_get_uri(
 
 gboolean
 zynjacku_plugin_ui_on(
-  ZynjackuPlugin * plugin_obj_ptr);
+  ZynjackuPlugin * plugin_obj_ptr,
+  const char * ui_uri,
+  const char * ui_type_uri,
+  const char * ui_binary_path,
+  const char * ui_bundle_path);
 
 void
 zynjacku_plugin_ui_off(
@@ -133,6 +124,61 @@ zynjacku_plugin_set_midi_cc_map(
   gchar * string_context,
   GObject * midi_cc_map_obj_ptr);
 
+void
+zynjacku_plugin_add_supported_feature(
+  ZynjackuPlugin * plugin_obj_ptr,
+  const gchar * feature_uri);
+
+gboolean
+zynjacku_plugin_create_oldmidi_input_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol);
+
+gboolean
+zynjacku_plugin_create_eventmidi_input_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol);
+
+gboolean
+zynjacku_plugin_create_audio_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  gboolean input);
+
+gboolean
+zynjacku_plugin_create_float_parameter_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  const gchar * name,
+  gboolean msgcontext,
+  gboolean default_provided,
+  gfloat default_value,
+  gboolean min_provided,
+  gfloat min_value,
+  gboolean max_provided,
+  gfloat max_value);
+
+gboolean
+zynjacku_plugin_create_float_measure_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  gboolean msgcontext);
+
+gboolean
+zynjacku_plugin_create_string_parameter_port(
+  ZynjackuPlugin * plugin_obj_ptr,
+  guint port_index,
+  const gchar * symbol,
+  const gchar * name,
+  gboolean msgcontext,
+  const gchar * default_value,
+  gsize maxlen);
+
 G_END_DECLS
 
 #endif /* #ifndef PLUGIN_H__0C38A6AD_527B_4795_8711_3606AC3A16BD__INCLUDED */
diff --git a/plugin_internal.h b/plugin_internal.h
index e452cb6..33d3e79 100644
--- a/plugin_internal.h
+++ b/plugin_internal.h
@@ -25,10 +25,6 @@
 
 #ifdef LV2_H_INCLUDED
 
-#define PLUGIN_TYPE_UNKNOWN  0
-#define PLUGIN_TYPE_SYNTH    1
-#define PLUGIN_TYPE_EFFECT   2
-
 struct zynjacku_rt_plugin_command
 {
   struct zynjacku_port * port; /* port to set data for */
@@ -42,6 +38,8 @@ struct zynjacku_plugin
   GObject * root_group_ui_context;
   GObject * engine_object_ptr;
   gchar * uri;
+  gchar * dlpath;
+  gchar * bundle_path;
 
   struct list_head siblings_all;
   struct list_head siblings_active;
@@ -49,6 +47,8 @@ struct zynjacku_plugin
 #if HAVE_DYNPARAMS
   bool dynparams_supported;
 #endif
+  struct list_head midi_ports;
+  struct list_head audio_ports;
   struct list_head parameter_ports;
   struct list_head measure_ports;
 #if HAVE_DYNPARAMS
@@ -61,22 +61,20 @@ struct zynjacku_plugin
 
   bool recycle;
 
-  unsigned int type;
-
   union
   {
     struct
     {
-      struct zynjacku_port midi_in_port;
-      struct zynjacku_port audio_out_left_port;
-      struct zynjacku_port audio_out_right_port;
+      struct zynjacku_port * midi_in_port_ptr;
+      struct zynjacku_port * audio_out_left_port_ptr;
+      struct zynjacku_port * audio_out_right_port_ptr;
     } synth;
     struct
     {
-      struct zynjacku_port audio_in_left_port;
-      struct zynjacku_port audio_in_right_port;
-      struct zynjacku_port audio_out_left_port;
-      struct zynjacku_port audio_out_right_port;
+      struct zynjacku_port * audio_in_left_port_ptr;
+      struct zynjacku_port * audio_in_right_port_ptr;
+      struct zynjacku_port * audio_out_left_port_ptr;
+      struct zynjacku_port * audio_out_right_port_ptr;
     } effect;
   } subtype;
   
@@ -84,7 +82,8 @@ struct zynjacku_plugin
   struct zynjacku_rt_plugin_command * volatile command_result; /* command that has been executed */
 
   void (* deactivate)(GObject * synth_obj_ptr);
-  void (* free_ports)(GObject * synth_obj_ptr);
+  void (* get_required_features)(GObject * engine_obj_ptr, const LV2_Feature * const ** host_features, unsigned int * host_feature_count);
+  void (* unregister_port)(GObject * engine_obj_ptr, struct zynjacku_port * port_ptr);
   bool (* set_midi_cc_map)(GObject * engine_obj_ptr, struct zynjacku_port * port_ptr, GObject * midi_cc_map_obj_ptr);
   bool (* midi_cc_map_cc_no_assign)(GObject * engine_obj_ptr, GObject * midi_cc_map_obj_ptr, guint cc_no);
 };
@@ -101,10 +100,6 @@ zynjacku_connect_plugin_ports(
 #endif
   );
 
-void
-zynjacku_free_plugin_ports(
-  struct zynjacku_plugin * plugin_ptr);
-
 void *
 zynjacku_plugin_prerun_rt(
   struct zynjacku_plugin * plugin_ptr);
diff --git a/plugin_repo.c b/plugin_repo.c
deleted file mode 100644
index bb425b7..0000000
--- a/plugin_repo.c
+++ /dev/null
@@ -1,1265 +0,0 @@
-/* -*- Mode: C ; c-basic-offset: 2 -*- */
-/*****************************************************************************
- *
- *   This file is part of zynjacku
- *
- *   Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov <nedko at arnaudov.name>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; version 2 of the License
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *****************************************************************************/
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <slv2/slv2.h>
-#include <glib-object.h>
-#if HAVE_DYNPARAMS
-#include <lv2dynparam/lv2dynparam.h>
-#include <lv2dynparam/lv2_rtmempool.h>
-#include <lv2dynparam/host.h>
-#endif
-#include <jack/jack.h>
-
-#include "lv2-miditype.h"
-#include "lv2_event.h"
-#include "lv2_string_port.h"
-#include "lv2_uri_map.h"
-
-#include "list.h"
-#include "lv2.h"
-#include "gtk2gui.h"
-#include "zynjacku.h"
-#include "plugin.h"
-#include "plugin_internal.h"
-#include "plugin_repo.h"
-//#define LOG_LEVEL LOG_LEVEL_DEBUG
-#include "log.h"
-
-#define LV2_RDF_LICENSE_URI "http://usefulinc.com/ns/doap#license"
-#define LV2_MIDI_PORT_URI "http://ll-plugins.nongnu.org/lv2/ext/MidiPort"
-#define LV2_EVENT_PORT_URI LV2_EVENT_URI "#EventPort"
-#define LV2_CONTEXT_URI "http://lv2plug.in/ns/dev/contexts"
-#define LV2_PORT_CONTEXT_URI LV2_CONTEXT_URI "#context"
-#define LV2_MESSAGE_CONTEXT_URI LV2_CONTEXT_URI "#MessageContext"
-#define LV2_STRING_PORT_ROOT_URI "http://lv2plug.in/ns/dev/string-port#"
-#define LV2_STRING_PORT_TYPE_URI LV2_STRING_PORT_ROOT_URI "StringPort"
-#define LV2_STRING_PORT_DEFAULT_URI LV2_STRING_PORT_ROOT_URI "default"
-
-struct zynjacku_plugin_info
-{
-  struct list_head siblings;
-  SLV2Plugin slv2info;
-  char * name;
-  char * license;
-  char * author;
-  char * uri;
-};
-
-struct zynjacku_iterate_context
-{
-  float progress;
-  float progress_step;
-  const LV2_Feature * const * supported_features;
-  void * context;
-  zynjacku_plugin_repo_check_plugin check_plugin;
-  zynjacku_plugin_repo_tick tick;
-  zynjacku_plugin_repo_tack tack;
-};
-
-/* I would be useful if slv2_world_get_plugins_by_filter() had callback user context... */
-/* this should really be parameter of slv2 filter plugins callback */
-struct zynjacku_iterate_context g_iterate_context;
-
-static struct list_head g_available_plugins; /* "struct zynjacku_plugin_info's linked by siblings */
-static SLV2World g_world;
-static bool g_loaded;
-static bool g_fullscanned;
-static SLV2Value g_slv2uri_port_input;
-static SLV2Value g_slv2uri_port_output;
-static SLV2Value g_slv2uri_port_control;
-static SLV2Value g_slv2uri_port_audio;
-static SLV2Value g_slv2uri_port_midi;
-static SLV2Value g_slv2uri_port_event;
-static SLV2Value g_slv2uri_port_context;
-static SLV2Value g_slv2uri_message_context;
-static SLV2Value g_slv2uri_license;
-static SLV2Value g_slv2uri_event_midi;
-static SLV2Value g_slv2uri_port_string;
-static SLV2Value g_slv2uri_string_port_default;
-
-/* as slv2_value_as_string() but returns NULL if value is NULL or value type is not string
-   such conditions are assumed to be error, thus this function should be
-   used only when caller expects value to be string */
-const char *
-slv2_value_as_string_smart(SLV2Value value)
-{
-  if (value == NULL)
-  {
-    LOG_ERROR("SLV2Value is NULL");
-    return NULL;
-  }
-
-  if (!slv2_value_is_string(value))
-  {
-    LOG_ERROR("SLV2Value is not string");
-    return NULL;
-  }
-
-  return slv2_value_as_string(value);
-}
-
-const char *
-slv2_value_as_uri_smart(SLV2Value value)
-{
-  if (value == NULL)
-  {
-    LOG_ERROR("SLV2Value is NULL");
-    return NULL;
-  }
-
-  if (!slv2_value_is_uri(value))
-  {
-    LOG_ERROR("SLV2Value is not string");
-    return NULL;
-  }
-
-  return slv2_value_as_uri(value);
-}
-
-const char *
-slv2_plugin_get_uri_smart(SLV2Plugin plugin)
-{
-  return slv2_value_as_uri(slv2_plugin_get_uri(plugin));
-}
-
-bool
-slv2_port_is_control(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_control);
-}
-
-bool
-slv2_port_is_string(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_string);
-}
-
-bool
-slv2_port_is_audio(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_audio);
-}
-
-bool
-slv2_port_is_midi(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_midi);
-}
-
-bool
-slv2_port_is_event(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_event);
-}
-
-bool
-slv2_port_is_input(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_input);
-}
-
-bool
-slv2_port_is_output(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_is_a(plugin, port, g_slv2uri_port_output);
-}
-
-bool
-slv2_port_is_midi_event(
-  SLV2Plugin plugin,
-  SLV2Port port)
-{
-  return slv2_port_supports_event(plugin, port, g_slv2uri_event_midi);
-}
-
-struct uri_registration
-{
-  const char *name;
-  SLV2Value *value;
-};
-
-static struct uri_registration uri_regs[] = {
-  {SLV2_PORT_CLASS_INPUT, &g_slv2uri_port_input},
-  {SLV2_PORT_CLASS_OUTPUT, &g_slv2uri_port_output},
-  {SLV2_PORT_CLASS_CONTROL, &g_slv2uri_port_control},
-  {SLV2_PORT_CLASS_AUDIO, &g_slv2uri_port_audio},
-  {LV2_MIDI_PORT_URI, &g_slv2uri_port_midi},
-  {LV2_RDF_LICENSE_URI, &g_slv2uri_license},
-  {LV2_PORT_CONTEXT_URI, &g_slv2uri_port_context},
-  {LV2_MESSAGE_CONTEXT_URI, &g_slv2uri_message_context},
-  {LV2_EVENT_PORT_URI, &g_slv2uri_port_event},
-  {LV2_EVENT_URI_TYPE_MIDI, &g_slv2uri_event_midi},
-  {LV2_STRING_PORT_TYPE_URI, &g_slv2uri_port_string},
-  {LV2_STRING_PORT_DEFAULT_URI, &g_slv2uri_string_port_default},
-};
-
-bool
-zynjacku_plugin_repo_init()
-{
-  int i;
-  g_world = slv2_world_new();
-  if (g_world == NULL)
-  {
-    LOG_ERROR("slv2_world_new() failed.");
-    goto fail;
-  }
-
-  INIT_LIST_HEAD(&g_available_plugins);
-  g_fullscanned = false;
-  g_loaded = false;
-
-  for (i = 0; i < sizeof(uri_regs) / sizeof(struct uri_registration); i++)
-  {
-    *uri_regs[i].value = slv2_value_new_uri(g_world, uri_regs[i].name);
-    if (!*uri_regs[i].value)
-    {
-      LOG_ERROR("slv2_value_new_uri() failed.");
-      for (i--; i >= 0; i--)
-        slv2_value_free(*uri_regs[i].value);
-      goto fail_free_world;
-    }
-  }
-  
-  return true;
-
-fail_free_world:
-  slv2_world_free(g_world);
-
-fail:
-  return false;
-}
-
-void
-zynjacku_plugin_repo_uninit()
-{
-  struct list_head * node_ptr;
-  struct zynjacku_plugin_info * plugin_info_ptr;
-
-  while(!list_empty(&g_available_plugins))
-  {
-    node_ptr = g_available_plugins.next;
-
-    list_del(node_ptr);
-
-    plugin_info_ptr = list_entry(node_ptr, struct zynjacku_plugin_info, siblings);
-
-    //LOG_DEBUG("Removing %s", plugin_info_ptr->name);
-    free(plugin_info_ptr->author);
-    free(plugin_info_ptr->license);
-    free(plugin_info_ptr->name);
-    free(plugin_info_ptr);
-  }
-
-  slv2_value_free(g_slv2uri_event_midi);
-  slv2_value_free(g_slv2uri_port_event);
-  slv2_value_free(g_slv2uri_license);
-  slv2_value_free(g_slv2uri_port_midi);
-  slv2_value_free(g_slv2uri_port_audio);
-  slv2_value_free(g_slv2uri_port_control);
-  slv2_value_free(g_slv2uri_port_output);
-  slv2_value_free(g_slv2uri_port_input);
-  slv2_world_free(g_world);
-}
-
-char *
-zynjacku_plugin_repo_get_plugin_license(
-  SLV2Plugin plugin)
-{
-  SLV2Values slv2_values;
-  SLV2Value slv2_value;
-  char * license;
-  const char * license_uri;
-
-  slv2_values = slv2_plugin_get_value(
-    plugin,
-    g_slv2uri_license);
-
-  if (slv2_values_size(slv2_values) == 0)
-  {
-    LOG_WARNING("Plugin license query returned empty set");
-    return strdup("none");      /* acutally, slv2 should reject those early */
-  }
-
-  slv2_value = slv2_values_get_at(slv2_values, 0);
-  if (!slv2_value_is_uri(slv2_value))
-  {
-    LOG_WARNING("Plugin license is not uri");
-    return strdup("none");      /* acutally, slv2 should reject those early */
-  }
-
-  license_uri = slv2_value_as_string(slv2_value);
-
-  if (strcmp(license_uri, "http://usefulinc.com/doap/licenses/gpl") == 0)
-  {
-    license = strdup("GNU General Public License");
-  }
-  else if (strcmp(license_uri, "http://usefulinc.com/doap/licenses/lgpl") == 0)
-  {
-    license = strdup("GNU Lesser General Public License");
-  }
-  else
-  {
-    license = strdup(license_uri);
-  }
-
-  slv2_values_free(slv2_values);
-
-  return license;
-}
-
-char *
-zynjacku_plugin_repo_get_plugin_author(
-  SLV2Plugin plugin)
-{
-  SLV2Value slv2_value;
-  char * author;
-  const char * author_const;
-
-  slv2_value = slv2_plugin_get_author_name(plugin);
-  if (slv2_value != NULL)
-  {
-    author_const = slv2_value_as_string_smart(slv2_value);
-  }
-  else
-  {
-    author_const = NULL;
-  }
-
-  if (author_const != NULL)
-  {
-    author = strdup(author_const);
-  }
-  else
-  {
-    author = strdup("unknown");
-  }
-
-  return author;
-}
-
-/* check whether plugin is a synth, if it is, save plugin info */
-static
-bool
-zynjacku_plugin_repo_check_and_maybe_init_plugin(
-  SLV2Plugin plugin)
-{
-  gboolean ret;
-  uint32_t audio_in_ports_count;
-  uint32_t audio_out_ports_count;
-  uint32_t midi_in_ports_count;
-  uint32_t control_ports_count;
-  uint32_t string_ports_count;
-  uint32_t event_ports_count;
-  uint32_t midi_event_in_ports_count;
-  uint32_t ports_count;
-  uint32_t port_index;
-  const char *plugin_uri;
-  const char *feature_uri;
-  const char *name;
-  SLV2Value slv2name;
-  SLV2Values slv2features;
-  SLV2Value slv2feature;
-  unsigned int features_count;
-  unsigned int feature_index;
-  struct zynjacku_plugin_info * plugin_info_ptr;
-  SLV2Port port;
-  const LV2_Feature * const * feature_ptr_ptr;
-
-  plugin_uri = slv2_plugin_get_uri_smart(plugin);
-
-  if (g_iterate_context.tick != NULL)
-  {
-    g_iterate_context.tick(g_iterate_context.context, g_iterate_context.progress, plugin_uri);
-  }
-
-  ret = FALSE;
-
-  slv2name = slv2_plugin_get_name(plugin);
-  if (slv2name == NULL)
-  {
-    LOG_ERROR("slv2_plugin_get_name() returned NULL.");
-    goto exit;
-  }
-
-  name = slv2_value_as_string_smart(slv2name);
-  if (name == NULL)
-  {
-    LOG_ERROR("slv2_value_as_string_smart() failed for plugin name value.");
-    goto free_name;
-  }
-
-  /* check required features */
-
-  slv2features = slv2_plugin_get_required_features(plugin);
-  features_count = slv2_values_size(slv2features);
-  //LOG_DEBUG("Plugin \"%s\" has %u required features", name, features_count);
-
-  for (feature_index = 0 ; feature_index < features_count ; feature_index++)
-  {
-    slv2feature = slv2_values_get_at(slv2features, feature_index);
-
-    feature_uri = slv2_value_as_uri_smart(slv2feature);
-    if (feature_uri == NULL)
-    {
-      LOG_ERROR("slv2_value_as_uri_smart() failed for plugin name value.");
-      goto free_features;
-    }
-
-    LOG_DEBUG("%s", feature_uri);
-
-    feature_ptr_ptr = g_iterate_context.supported_features;
-    while (*feature_ptr_ptr != NULL)
-    {
-      if (strcmp((*feature_ptr_ptr)->URI, feature_uri) == 0)
-      {
-        break;
-      }
-
-      feature_ptr_ptr++;
-    }
-
-    if (*feature_ptr_ptr == NULL)
-    {
-      LOG_DEBUG("Plugin \"%s\" requires unsupported feature \"%s\"", name, feature_uri);
-      goto free_features;
-    }
-  }
-
-  /* check port configuration */
-
-  ports_count = slv2_plugin_get_num_ports(plugin);
-  audio_in_ports_count = slv2_plugin_get_num_ports_of_class(plugin, g_slv2uri_port_audio, g_slv2uri_port_input, NULL);
-  audio_out_ports_count = slv2_plugin_get_num_ports_of_class(plugin, g_slv2uri_port_audio, g_slv2uri_port_output, NULL);
-  midi_in_ports_count = slv2_plugin_get_num_ports_of_class(plugin, g_slv2uri_port_midi, g_slv2uri_port_input, NULL);
-  control_ports_count = slv2_plugin_get_num_ports_of_class(plugin, g_slv2uri_port_control, NULL);
-  string_ports_count = slv2_plugin_get_num_ports_of_class(plugin, g_slv2uri_port_string, NULL);
-  event_ports_count = slv2_plugin_get_num_ports_of_class(plugin, g_slv2uri_port_event, NULL);
-
-  midi_event_in_ports_count = 0;
-
-  if (event_ports_count != 0)
-  {
-    for (port_index = 0 ; port_index < ports_count ; port_index++)
-    {
-      port = slv2_plugin_get_port_by_index(plugin, port_index);
-      if (slv2_port_is_midi_event(plugin, port) && slv2_port_is_input(plugin, port))
-      {
-        midi_event_in_ports_count++;
-      }
-    }
-  }
-
-  if (!g_iterate_context.check_plugin(
-        g_iterate_context.context,
-        plugin_uri,
-        name,
-        audio_in_ports_count,
-        audio_out_ports_count,
-        midi_in_ports_count,
-        control_ports_count,
-        string_ports_count,
-        event_ports_count,
-        midi_event_in_ports_count,
-        ports_count))
-  {
-    goto free_features;
-  }
-
-  plugin_info_ptr = malloc(sizeof(struct zynjacku_plugin_info));
-  if (plugin_info_ptr == NULL)
-  {
-    LOG_ERROR("Cannot allocate memory for zynjacku_plugin_info structure");
-    goto free_features;
-  }
-
-  plugin_info_ptr->name = strdup(name);
-  if (plugin_info_ptr->name == NULL)
-  {
-    goto free_info;
-  }
-
-  plugin_info_ptr->uri = strdup(plugin_uri);
-  if (plugin_info_ptr->uri == NULL)
-  {
-    goto free_info_name;
-  }
-
-  plugin_info_ptr->license = zynjacku_plugin_repo_get_plugin_license(plugin);
-  if (plugin_info_ptr->license == NULL)
-  {
-    goto free_info_uri;
-  }
-
-  plugin_info_ptr->author = zynjacku_plugin_repo_get_plugin_author(plugin);
-  if (plugin_info_ptr->author == NULL)
-  {
-    goto free_info_license;
-  }
-
-  plugin_info_ptr->slv2info = plugin;
-
-  list_add_tail(&plugin_info_ptr->siblings, &g_available_plugins);
-
-  if (g_iterate_context.tack != NULL)
-  {
-    g_iterate_context.tack(g_iterate_context.context, plugin_uri);
-  }
-
-  ret = TRUE;
-
-  goto free_features;
-
-free_info_license:
-  free(plugin_info_ptr->license);
-
-free_info_uri:
-  free(plugin_info_ptr->uri);
-
-free_info_name:
-  free(plugin_info_ptr->name);
-
-free_info:
-  free(plugin_info_ptr);
-
-free_features:
-  slv2_values_free(slv2features);
-
-free_name:
-  slv2_value_free(slv2name);
-
-exit:
-  g_iterate_context.progress += g_iterate_context.progress_step;
-  return ret;
-}
-
-void
-zynjacku_plugin_repo_iterate(
-  bool force_scan,
-  const LV2_Feature * const * supported_features,
-  void * context,
-  zynjacku_plugin_repo_check_plugin check_plugin,
-  zynjacku_plugin_repo_tick tick,
-  zynjacku_plugin_repo_tack tack)
-{
-  struct list_head * node_ptr;
-  SLV2Plugins slv2plugins;
-  struct zynjacku_plugin_info * plugin_info_ptr;
-
-  LOG_DEBUG("zynjacku_plugin_repo_iterate() called.");
-
-  if (!force_scan && g_fullscanned)
-  {
-    if (tack != NULL)
-    {
-      LOG_DEBUG("Iterate existing plugins!");
-
-      list_for_each(node_ptr, &g_available_plugins)
-      {
-        plugin_info_ptr = list_entry(node_ptr, struct zynjacku_plugin_info, siblings);
-        tack(context, plugin_info_ptr->uri);
-      }
-    }
-
-    if (tick != NULL)
-    {
-      tick(context, 1.0, "");
-    }
-
-    return;
-  }
-
-  /* scanned in past, clear world to scan again */
-  zynjacku_plugin_repo_uninit();
-  zynjacku_plugin_repo_init();
-
-  LOG_DEBUG("Scanning plugins...");
-
-  if (tick != NULL)
-  {
-    tick(context, 0.0, "Loading plugins (world) ...");
-  }
-
-  assert(!g_loaded);
-
-  slv2_world_load_all(g_world);
-  g_loaded = true;
-
-  /* get plugins count */
-  slv2plugins = slv2_world_get_all_plugins(g_world);
-  g_iterate_context.progress_step = 1.0 / slv2_plugins_size(slv2plugins);
-  slv2_plugins_free(g_world, slv2plugins);
-
-  g_iterate_context.progress = 0.0;
-  g_iterate_context.supported_features = supported_features;
-  g_iterate_context.context = context;
-  g_iterate_context.check_plugin = check_plugin;
-  g_iterate_context.tick = tick;
-  g_iterate_context.tack = tack;
-
-  slv2plugins = slv2_world_get_plugins_by_filter(g_world, zynjacku_plugin_repo_check_and_maybe_init_plugin);
-  slv2_plugins_free(g_world, slv2plugins);
-
-  if (tick != NULL)
-  {
-    tick(context, 1.0, "");
-  }
-
-  g_fullscanned = true;
-}
-
-static
-struct zynjacku_plugin_info *
-zynjacku_plugin_repo_lookup_by_uri(
-  const char * uri)
-{
-  struct list_head * node_ptr;
-  struct zynjacku_plugin_info * plugin_info_ptr;
-
-  list_for_each(node_ptr, &g_available_plugins)
-  {
-    plugin_info_ptr = list_entry(node_ptr, struct zynjacku_plugin_info, siblings);
-    if (strcmp(plugin_info_ptr->uri, uri) == 0)
-    {
-      return plugin_info_ptr;
-    }
-  }
-
-  LOG_ERROR("Unknown plugin '%s'", uri);
-  return NULL;
-}
-
-const char *
-zynjacku_plugin_repo_get_name(
-  const char *uri)
-{
-  struct zynjacku_plugin_info * plugin_info_ptr;
-
-  plugin_info_ptr = zynjacku_plugin_repo_lookup_by_uri(uri);
-  if (plugin_info_ptr == NULL)
-  {
-    return NULL;
-  }
-
-  return plugin_info_ptr->name;
-}
-
-const char *
-zynjacku_plugin_repo_get_license(
-  const char *uri)
-{
-  struct zynjacku_plugin_info * plugin_info_ptr;
-
-  plugin_info_ptr = zynjacku_plugin_repo_lookup_by_uri(uri);
-  if (plugin_info_ptr == NULL)
-  {
-    return NULL;
-  }
-
-  return plugin_info_ptr->license;
-}
-
-const char *
-zynjacku_plugin_repo_get_author(
-  const char * uri)
-{
-  struct zynjacku_plugin_info * plugin_info_ptr;
-
-  plugin_info_ptr = zynjacku_plugin_repo_lookup_by_uri(uri);
-  if (plugin_info_ptr == NULL)
-  {
-    return NULL;
-  }
-
-  return plugin_info_ptr->author;
-}
-
-const char *
-zynjacku_plugin_repo_get_dlpath(
-  const char *uri)
-{
-  struct zynjacku_plugin_info * info_ptr;
-
-  info_ptr = zynjacku_plugin_repo_lookup_by_uri(uri);
-  if (info_ptr == NULL)
-  {
-    return NULL;
-  }
-
-  return slv2_uri_to_path(slv2_value_as_uri(slv2_plugin_get_library_uri(info_ptr->slv2info)));
-}
-
-const char *
-zynjacku_plugin_repo_get_bundle_path(
-  const char *uri)
-{
-  struct zynjacku_plugin_info * info_ptr;
-
-  info_ptr = zynjacku_plugin_repo_lookup_by_uri(uri);
-  if (info_ptr == NULL)
-  {
-    return NULL;
-  }
-
-  return slv2_uri_to_path(slv2_value_as_uri(slv2_plugin_get_bundle_uri(info_ptr->slv2info)));
-}
-
-static
-struct zynjacku_port *
-new_lv2parameter_port(
-  struct zynjacku_plugin_info * info_ptr,
-  SLV2Port port,
-  uint32_t index,
-  const char * symbol_str,
-  struct zynjacku_plugin * plugin_ptr)
-{
-  struct zynjacku_port * port_ptr;
-  SLV2Value name;
-  const char * name_str;
-  SLV2Values contexts;
-  int i;
-
-  port_ptr = malloc(sizeof(struct zynjacku_port));
-  if (port_ptr == NULL)
-  {
-    LOG_ERROR("malloc() failed to allocate memory for struct zynjacku_port.");
-    goto fail;
-  }
-
-  port_ptr->index = index;
-  port_ptr->flags = 0;
-  port_ptr->ui_context = NULL;
-  port_ptr->plugin_ptr = plugin_ptr;
-  port_ptr->midi_cc_map_obj_ptr = NULL;
-
-  port_ptr->symbol = strdup(symbol_str);
-  if (port_ptr->symbol == NULL)
-  {
-    LOG_ERROR("strdup() failed.");
-    goto fail_free_port;
-  }
-
-  /* port name */
-  name = slv2_port_get_name(info_ptr->slv2info, port);
-  if (name == NULL)
-  {
-    LOG_ERROR("slv2_port_get_name() failed.");
-    goto fail_free_symbol;
-  }
-
-  name_str = slv2_value_as_string_smart(name);
-  if (name_str == NULL)
-  {
-    LOG_ERROR("port symbol is not string.");
-    goto fail_free_symbol;
-  }
-
-  port_ptr->name = strdup(name_str);
-
-  slv2_value_free(name);
-
-  if (port_ptr->name == NULL)
-  {
-    LOG_ERROR("strdup() failed.");
-    goto fail_free_symbol;
-  }
-
-  contexts = slv2_port_get_value(info_ptr->slv2info, port, g_slv2uri_port_context);
-  for (i = 0; i < slv2_values_size(contexts); i++)
-  {
-    if (slv2_value_equals(slv2_values_get_at(contexts, i), g_slv2uri_message_context))
-    {
-      port_ptr->flags |= PORT_FLAGS_MSGCONTEXT;
-      LOG_DEBUG("Port %d has message context", index);
-      break;
-    }
-  }
-
-  return port_ptr;
-      
-fail_free_symbol:
-  free(port_ptr->symbol);
-
-fail_free_port:
-  free(port_ptr);
-
-fail:
-  return NULL;
-}
-
-static
-bool
-zynjacku_plugin_repo_create_port_internal(
-  struct zynjacku_plugin_info * info_ptr,
-  uint32_t port_index,
-  struct zynjacku_plugin * plugin_ptr,
-  void * context,
-  zynjacku_plugin_repo_create_port create_port)
-{
-  struct zynjacku_port * port_ptr;
-  SLV2Port port;
-  SLV2Value symbol;
-  const char * symbol_str;
-  SLV2Value default_value;
-  SLV2Value min_value;
-  SLV2Value max_value;
-  unsigned int port_type;
-  bool output_port;
-  SLV2Values defs;
-  SLV2Value defval;
-  const char * defval_str;
-  size_t defval_len;
-
-  port = slv2_plugin_get_port_by_index(info_ptr->slv2info, port_index);
-
-  output_port = slv2_port_is_output(info_ptr->slv2info, port);
-
-  /* port symbol */
-  symbol = slv2_port_get_symbol(info_ptr->slv2info, port);
-  if (symbol == NULL)
-  {
-    LOG_ERROR("slv2_port_get_symbol() failed.");
-    return false;
-  }
-
-  symbol_str = slv2_value_as_string_smart(symbol);
-  if (symbol_str == NULL)
-  {
-    LOG_ERROR("port symbol is not string.");
-    return false;
-  }
-
-  if (slv2_port_is_control(info_ptr->slv2info, port))
-  {
-    port_ptr = new_lv2parameter_port(info_ptr, port, port_index, symbol_str, plugin_ptr);
-
-    port_ptr->type = PORT_TYPE_LV2_FLOAT;
-
-    if (output_port)
-    {
-      port_ptr->flags |= PORT_FLAGS_OUTPUT;
-      list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->measure_ports);
-      return true;
-    }
-
-    /* port range */
-    slv2_port_get_range(
-      info_ptr->slv2info,
-      port,
-      &default_value,
-      &min_value,
-      &max_value);
-
-    if (default_value != NULL)
-    {
-      port_ptr->data.lv2float.value = slv2_value_as_float(default_value);
-      slv2_value_free(default_value);
-    }
-
-    if (min_value != NULL)
-    {
-      port_ptr->data.lv2float.min = slv2_value_as_float(min_value);
-      slv2_value_free(min_value);
-    }
-
-    if (max_value != NULL)
-    {
-      port_ptr->data.lv2float.max = slv2_value_as_float(max_value);
-      slv2_value_free(max_value);
-    }
-
-    list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->parameter_ports);
-
-    return true;
-  }
-
-  if (slv2_port_is_string(info_ptr->slv2info, port))
-  {
-    if (output_port)
-    {
-      /* TODO measure string ports are ignored for now */
-      return true;
-    }
-
-    port_ptr = new_lv2parameter_port(info_ptr, port, port_index, symbol_str, plugin_ptr);
-
-    port_ptr->type = PORT_TYPE_LV2_STRING;
-
-    /* TODO: get from slv2 (requiredSpace) */
-    port_ptr->data.lv2string.storage = 256;
-
-    defval_str = "\0";
-
-    /* get default */
-    defs = slv2_port_get_value(info_ptr->slv2info, port, g_slv2uri_string_port_default);
-    if (defs && slv2_values_size(defs) == 1)
-    {
-      defval = slv2_values_get_at(defs, 0);
-      if (slv2_value_is_string(defval))
-      {
-        defval_str = slv2_value_as_string(defval);
-      }
-    }
-
-    defval_len = strlen(defval_str) + 1;
-
-    if (defval_len > port_ptr->data.lv2string.storage)
-    {
-      port_ptr->data.lv2string.storage = defval_len;
-    }
-
-    port_ptr->data.lv2string.data = malloc(port_ptr->data.lv2string.storage);
-    memcpy(port_ptr->data.lv2string.data, defval_str, defval_len);
-
-    port_ptr->data.lv2string.len = defval_len - 1;
-    port_ptr->data.lv2string.flags = LV2_STRING_DATA_CHANGED_FLAG;
-    port_ptr->data.lv2string.pad = 0;
-
-    list_add_tail(&port_ptr->plugin_siblings, &plugin_ptr->parameter_ports);
-
-    return true;
-  }
-
-  if (slv2_port_is_audio(info_ptr->slv2info, port))
-  {
-    port_type = PORT_TYPE_AUDIO;
-  }
-  else if (slv2_port_is_midi(info_ptr->slv2info, port))
-  {
-    port_type = PORT_TYPE_MIDI;
-  }
-  else if (slv2_port_is_event(info_ptr->slv2info, port) &&
-           slv2_port_is_midi_event(info_ptr->slv2info, port))
-  {
-    port_type = PORT_TYPE_EVENT_MIDI;
-  }
-  else
-  {
-    LOG_ERROR("Unrecognized port '%s' type (index is %u)", slv2_value_as_string_smart(symbol), (unsigned int)port_index);
-    return false;
-  }
-
-  if (create_port(
-        context,
-        port_type,
-        output_port,
-        port_index))
-  {
-    return true;
-  }
-
-  LOG_ERROR("Unmatched port '%s'. type is %u, index is %u", slv2_value_as_string_smart(symbol), (unsigned int)port_type, (unsigned int)port_index);
-  return false;
-}
-
-bool
-zynjacku_plugin_repo_load_plugin(
-  struct zynjacku_plugin * synth_ptr,
-  void * context,
-  zynjacku_plugin_repo_create_port create_port,
-  zynjacku_plugin_repo_check_plugin check_plugin,
-  const LV2_Feature * const * supported_features)
-{
-  struct zynjacku_plugin_info * info_ptr;
-  SLV2Values slv2features;
-  SLV2Value slv2feature;
-  unsigned int features_count;
-  unsigned int feature_index;
-  bool ret;
-  const char *uri;
-  uint32_t ports_count;
-  uint32_t i;
-  SLV2Plugins slv2plugins;
-  SLV2Plugin slv2plugin;
-  SLV2Value uri_value;
-
-  ret = false;
-
-  LOG_DEBUG("zynjacku_plugin_repo_load_plugin() called.");
-
-#if HAVE_DYNPARAMS
-  synth_ptr->dynparams_supported = FALSE;
-#endif
-
-  if (!g_fullscanned)
-  {
-    if (!g_loaded)
-    {
-      slv2_world_load_all(g_world);
-      g_loaded = true;
-    }
-
-    g_iterate_context.supported_features = supported_features;
-    g_iterate_context.context = context;
-    g_iterate_context.check_plugin = check_plugin;
-    g_iterate_context.progress_step = 0.0;
-    g_iterate_context.progress = 0.0;
-    g_iterate_context.tick = NULL;
-    g_iterate_context.tack = NULL;
-
-    slv2plugins = slv2_world_get_all_plugins(g_world);
-
-    uri_value = slv2_value_new_uri(g_world, synth_ptr->uri);
-
-    slv2plugin = slv2_plugins_get_by_uri(slv2plugins, uri_value);
-    if (slv2plugin == NULL)
-    {
-      slv2_value_free(uri_value);
-      slv2_plugins_free(g_world, slv2plugins);
-      LOG_ERROR("Plugin '%s' not found", synth_ptr->uri);
-      goto exit;
-    }
-
-    ret = zynjacku_plugin_repo_check_and_maybe_init_plugin(slv2plugin);
-
-    slv2_value_free(uri_value);
-
-    slv2_plugins_free(g_world, slv2plugins);
-
-    if (!ret)
-    {
-      LOG_ERROR("plugin '%s' failed to match synth constraints", synth_ptr->uri);
-      goto exit;
-    }
-
-    /* MAYBE: return info_ptr from zynjacku_plugin_repo_check_and_maybe_init_plugin()
-       so we dont lookup it in next line, by calling zynjacku_plugin_repo_lookup_by_uri() */
-  }
-
-  info_ptr = zynjacku_plugin_repo_lookup_by_uri(synth_ptr->uri);
-  if (info_ptr == NULL)
-  {
-    LOG_ERROR("Failed to find plugin %s", synth_ptr->uri);
-    goto exit;
-  }
-
-  synth_ptr->name = strdup(info_ptr->name);
-  if (synth_ptr->name == NULL)
-  {
-    LOG_ERROR("Failed to strdup('%s')", info_ptr->name);
-    goto exit;
-  }
-
-  slv2features = slv2_plugin_get_optional_features(info_ptr->slv2info);
-
-  features_count = slv2_values_size(slv2features);
-  LOG_DEBUG("Plugin has %u optional features", features_count);
-  for (feature_index = 0 ; feature_index < features_count ; feature_index++)
-  {
-    slv2feature = slv2_values_get_at(slv2features, feature_index);
-
-    uri = slv2_value_as_uri_smart(slv2feature);
-    if (uri == NULL)
-    {
-      LOG_ERROR("slv2_value_as_uri_smart() failed for plugin name value.");
-      goto free_features;
-    }
-
-    LOG_DEBUG("%s", uri);
-
-#if HAVE_DYNPARAMS
-    if (strcmp(LV2DYNPARAM_URI, uri) == 0)
-    {
-      synth_ptr->dynparams_supported = TRUE;
-    }
-#endif
-  }
-
-  ports_count  = slv2_plugin_get_num_ports(info_ptr->slv2info);
-
-  for (i = 0 ; i < ports_count ; i++)
-  {
-    if (!zynjacku_plugin_repo_create_port_internal(info_ptr, i, synth_ptr, context, create_port))
-    {
-      LOG_ERROR("Failed to create plugin port");
-      goto free_features;
-    }
-  }
-
-  ret = true;
-
-free_features:
-  slv2_values_free(slv2features);
-
-exit:
-  return ret;
-}
-
-static
-const char *
-uri_to_fs_path(
-  const char * uri)
-{
-  if (uri == NULL)
-  {
-    return NULL;
-  }
-
-  if (strlen(uri) <= 8 || memcmp(uri, "file:///", 8) != 0)
-  {
-    return NULL;
-  }
-
-  return uri + 7;
-}
-
-bool
-zynjacku_plugin_repo_get_ui_info(
-  const char * plugin_uri,
-  const char * ui_type_uri,
-  char ** ui_uri_ptr,
-  char ** ui_binary_path_ptr,
-  char ** ui_bundle_path_ptr)
-{
-  struct zynjacku_plugin_info * plugin_info_ptr;
-  SLV2UIs slv2uis;
-  SLV2UI slv2ui;
-  SLV2Value ui_type;
-  SLV2Value ui_uri;
-  SLV2Value ui_binary_uri;
-  SLV2Value ui_bundle_uri;
-  const char * ui_uri_str;
-  const char * ui_binary_path;
-  const char * ui_bundle_path;
-  bool ret;
-
-  LOG_DEBUG("zynjacku_plugin_repo_get_ui_info() called.");
-
-  ret = false;
-
-  ui_type = slv2_value_new_uri(g_world, ui_type_uri);
-  if (ui_type == NULL)
-  {
-    LOG_ERROR("slv2_value_new_uri() failed.");
-    goto fail;
-  }
-
-  plugin_info_ptr = zynjacku_plugin_repo_lookup_by_uri(plugin_uri);
-  if (plugin_info_ptr == NULL)
-  {
-    LOG_ERROR("Unknown plugin '%s'", plugin_uri);
-    goto fail_free_ui_type;
-  }
-
-  slv2uis = slv2_plugin_get_uis(plugin_info_ptr->slv2info);
-
-  if (slv2_uis_size(slv2uis) == 0)
-  {
-    LOG_DEBUG("Plugin '%s' has no UIs", plugin_uri);
-    goto fail_free_uis;
-  }
-
-  slv2ui = slv2_uis_get_at(slv2uis, 0);
-  if (slv2ui == NULL)
-  {
-    LOG_ERROR("slv2_uis_get_at() failed with plugin '%s'", plugin_uri);
-    goto fail_free_uis;
-  }
-
-  if (!slv2_ui_is_a(slv2ui, ui_type))
-  {
-    LOG_DEBUG("First UI of '%s' is not '%s'", plugin_uri, ui_type_uri);
-    goto fail_free_uis;
-  }
-
-  ui_uri = slv2_ui_get_uri(slv2ui);
-  ui_binary_uri = slv2_ui_get_binary_uri(slv2ui);
-  ui_bundle_uri = slv2_ui_get_bundle_uri(slv2ui);
-
-  ui_uri_str = slv2_value_as_uri_smart(ui_uri);
-  ui_binary_path = uri_to_fs_path(slv2_value_as_uri_smart(ui_binary_uri));
-  ui_bundle_path = uri_to_fs_path(slv2_value_as_uri_smart(ui_bundle_uri));
-
-  if (ui_uri_str == NULL)
-  {
-    LOG_ERROR("Failed to retrieve UI URI of '%s'", plugin_uri);
-    goto fail_free_uis;
-  }
-
-  if (ui_binary_uri == NULL)
-  {
-    LOG_ERROR("Failed to retrieve UI binary path of '%s'", plugin_uri);
-    goto fail_free_uis;
-  }
-
-  if (ui_bundle_uri == NULL)
-  {
-    LOG_ERROR("Failed to retrieve UI bundle path of '%s'", plugin_uri);
-    goto fail_free_uis;
-  }
-
-  LOG_DEBUG("UI URI is '%s'", ui_uri_str);
-  LOG_DEBUG("UI binary URI is '%s'", ui_binary_path);
-  LOG_DEBUG("UI bundle URI is '%s'", ui_bundle_path);
-
-  *ui_uri_ptr = strdup(ui_uri_str);
-  if (*ui_uri_ptr == NULL)
-  {
-    LOG_ERROR("strdup() failed");
-    goto fail_free_uis;
-  }
-
-  *ui_binary_path_ptr = strdup(ui_binary_path);
-  if (*ui_binary_path_ptr == NULL)
-  {
-    LOG_ERROR("strdup() failed");
-    free(*ui_uri_ptr);
-    goto fail_free_uis;
-  }
-
-  *ui_bundle_path_ptr = strdup(ui_bundle_path);
-  if (*ui_bundle_path_ptr == NULL)
-  {
-    LOG_ERROR("strdup() failed");
-    free(*ui_binary_path_ptr);
-    goto fail_free_uis;
-  }
-
-  ret = true;
-
-fail_free_uis:
-  slv2_uis_free(slv2uis);
-
-fail_free_ui_type:
-  slv2_value_free(ui_type);
-
-fail:
-  return ret;
-}
diff --git a/plugin_repo.h b/plugin_repo.h
deleted file mode 100644
index eb72acc..0000000
--- a/plugin_repo.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* -*- Mode: C ; c-basic-offset: 2 -*- */
-/*****************************************************************************
- *
- *   This file is part of zynjacku
- *
- *   Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov <nedko at arnaudov.name>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; version 2 of the License
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *****************************************************************************/
-
-#ifndef PLUGIN_REPO_H__27C1E0DC_DD5E_4A79_838B_DC6B90402229__INCLUDED
-#define PLUGIN_REPO_H__27C1E0DC_DD5E_4A79_838B_DC6B90402229__INCLUDED
-
-typedef
-void
-(* zynjacku_plugin_repo_tick)(
-  void * context,
-  float progress,               /* 0..1 */
-  const char * message);
-
-typedef
-void
-(* zynjacku_plugin_repo_tack)(
-  void * context,
-  const char * uri);
-
-typedef
-bool
-(* zynjacku_plugin_repo_check_plugin)(
-  void * context,
-  const char * plugin_uri,
-  const char * plugin_name,
-  uint32_t audio_in_ports_count,
-  uint32_t audio_out_ports_count,
-  uint32_t midi_in_ports_count,
-  uint32_t control_ports_count,
-  uint32_t string_ports_count,
-  uint32_t event_ports_count,
-  uint32_t midi_event_in_ports_count,
-  uint32_t ports_count);
-
-typedef
-bool
-(* zynjacku_plugin_repo_create_port)(
-  void * context,
-  unsigned int port_type,
-  bool output,
-  uint32_t port_index);
-
-bool
-zynjacku_plugin_repo_init();
-
-void
-zynjacku_plugin_repo_iterate(
-  bool force_scan,
-  const LV2_Feature * const * supported_features,
-  void * context,
-  zynjacku_plugin_repo_check_plugin check_plugin,
-  zynjacku_plugin_repo_tick tick,
-  zynjacku_plugin_repo_tack tack);
-
-const char *
-zynjacku_plugin_repo_get_name(
-  const char * uri);
-
-const char *
-zynjacku_plugin_repo_get_license(
-  const char * uri);
-
-const char *
-zynjacku_plugin_repo_get_author(
-  const char * uri);
-
-const char *
-zynjacku_plugin_repo_get_dlpath(
-  const char * uri);
-
-const char *
-zynjacku_plugin_repo_get_bundle_path(
-  const char * uri);
-
-bool
-zynjacku_plugin_repo_get_ui_info(
-  const char * plugin_uri,
-  const char * ui_type_uri,
-  char ** ui_uri_ptr,
-  char ** ui_binary_path_ptr,
-  char ** ui_bundle_path_ptr);
-
-bool
-zynjacku_plugin_repo_load_plugin(
-  struct zynjacku_plugin * synth_ptr,
-  void * context,
-  zynjacku_plugin_repo_create_port create_port,
-  zynjacku_plugin_repo_check_plugin check_plugin,
-  const LV2_Feature * const * supported_features);
-
-void
-zynjacku_plugin_repo_uninit();
-
-#endif /* #ifndef PLUGIN_REPO_H__27C1E0DC_DD5E_4A79_838B_DC6B90402229__INCLUDED */
diff --git a/rack.c b/rack.c
index bdbeea6..2e77171 100644
--- a/rack.c
+++ b/rack.c
@@ -61,7 +61,6 @@
 #if HAVE_DYNPARAMS
 #include "rtmempool.h"
 #endif
-#include "plugin_repo.h"
 #include "lv2_event_helpers.h"
 
 #if HAVE_DYNPARAMS
@@ -107,10 +106,8 @@ struct lv2rack_engine
 
 #define ZYNJACKU_RACK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), ZYNJACKU_RACK_TYPE, struct lv2rack_engine))
 
-#define ZYNJACKU_RACK_SIGNAL_TICK      0 /* plugin iterated */
-#define ZYNJACKU_RACK_SIGNAL_TACK      1 /* "good" plugin found */
-#define ZYNJACKU_RACK_SIGNAL_PROGRESS  2 /* plugin instantiation progress */
-#define ZYNJACKU_RACK_SIGNALS_COUNT    3
+#define ZYNJACKU_RACK_SIGNAL_PROGRESS  0 /* plugin instantiation progress */
+#define ZYNJACKU_RACK_SIGNALS_COUNT    1
 
 /* URI map value for event MIDI type */
 #define ZYNJACKU_MIDI_EVENT_ID 1
@@ -151,7 +148,6 @@ zynjacku_rack_dispose(GObject * obj)
   if (rack_ptr->jack_client)
   {
     zynjacku_rack_stop_jack(ZYNJACKU_RACK(obj));
-    zynjacku_plugin_repo_uninit();
   }
 
   /* Chain up to the parent class */
@@ -187,38 +183,6 @@ zynjacku_rack_class_init(
 
   g_type_class_add_private(G_OBJECT_CLASS(class_ptr), sizeof(struct lv2rack_engine));
 
-  g_zynjacku_rack_signals[ZYNJACKU_RACK_SIGNAL_TICK] =
-    g_signal_new(
-      "tick",                   /* signal_name */
-      ZYNJACKU_RACK_TYPE,       /* itype */
-      G_SIGNAL_RUN_LAST |
-      G_SIGNAL_ACTION,          /* signal_flags */
-      0,                        /* class_offset */
-      NULL,                     /* accumulator */
-      NULL,                     /* accu_data */
-      NULL,                     /* c_marshaller */
-      G_TYPE_NONE,              /* return type */
-      2,                        /* n_params */
-      G_TYPE_FLOAT,             /* progress 0 .. 1 */
-      G_TYPE_STRING);           /* uri of plugin being scanned */
-
-  g_zynjacku_rack_signals[ZYNJACKU_RACK_SIGNAL_TACK] =
-    g_signal_new(
-      "tack",                   /* signal_name */
-      ZYNJACKU_RACK_TYPE,       /* itype */
-      G_SIGNAL_RUN_LAST |
-      G_SIGNAL_ACTION,          /* signal_flags */
-      0,                        /* class_offset */
-      NULL,                     /* accumulator */
-      NULL,                     /* accu_data */
-      NULL,                     /* c_marshaller */
-      G_TYPE_NONE,              /* return type */
-      4,                        /* n_params */
-      G_TYPE_STRING,            /* plugin name */
-      G_TYPE_STRING,            /* plugin uri */
-      G_TYPE_STRING,            /* plugin license */
-      G_TYPE_STRING);           /* plugin author */
-
   g_zynjacku_rack_signals[ZYNJACKU_RACK_SIGNAL_PROGRESS] =
     g_signal_new(
       "progress",               /* signal_name */
@@ -353,8 +317,6 @@ zynjacku_rack_init(
   rack_ptr->host_features[count++] = &rack_ptr->host_feature_progress;
   assert(ZYNJACKU_RACK_ENGINE_FEATURES == count);
   rack_ptr->host_features[count] = NULL;
-
-  zynjacku_plugin_repo_init();
 }
 
 GType zynjacku_rack_get_type()
@@ -531,31 +493,31 @@ jack_process_cb(
     /* Connect plugin LV2 input audio ports */
     zynjacku_lv2_connect_port(
       effect_ptr->lv2plugin,
-      &effect_ptr->subtype.effect.audio_in_left_port,
+      effect_ptr->subtype.effect.audio_in_left_port_ptr,
       left);
 
-    if (effect_ptr->subtype.effect.audio_in_right_port.type == PORT_TYPE_AUDIO)
+    if (effect_ptr->subtype.effect.audio_in_right_port_ptr != NULL)
     {
       zynjacku_lv2_connect_port(
         effect_ptr->lv2plugin,
-        &effect_ptr->subtype.effect.audio_in_right_port,
+        effect_ptr->subtype.effect.audio_in_right_port_ptr,
         mono ? left : right);
     }
 
     /* Connect plugin LV2 output audio ports directly to JACK buffers */
-    left = jack_port_get_buffer(effect_ptr->subtype.effect.audio_out_left_port.data.audio, nframes);
+    left = jack_port_get_buffer(effect_ptr->subtype.effect.audio_out_left_port_ptr->data.audio, nframes);
     zynjacku_lv2_connect_port(
       effect_ptr->lv2plugin,
-      &effect_ptr->subtype.effect.audio_out_left_port,
+      effect_ptr->subtype.effect.audio_out_left_port_ptr,
       left);
 
-    mono = effect_ptr->subtype.effect.audio_out_right_port.type != PORT_TYPE_AUDIO;
+    mono = effect_ptr->subtype.effect.audio_out_right_port_ptr == NULL;
     if (!mono)
     {
-      right = jack_port_get_buffer(effect_ptr->subtype.effect.audio_out_right_port.data.audio, nframes);
+      right = jack_port_get_buffer(effect_ptr->subtype.effect.audio_out_right_port_ptr->data.audio, nframes);
       zynjacku_lv2_connect_port(
         effect_ptr->lv2plugin,
-        &effect_ptr->subtype.effect.audio_out_right_port,
+        effect_ptr->subtype.effect.audio_out_right_port_ptr,
         right);
     }
 
@@ -570,6 +532,7 @@ jack_process_cb(
 
 #undef rack_ptr
 
+static
 void
 zynjacku_rack_deactivate_effect(
   GObject * effect_obj_ptr)
@@ -592,6 +555,36 @@ zynjacku_rack_deactivate_effect(
 }
 
 void
+zynjacku_rack_get_required_features(
+  GObject * rack_obj_ptr,
+  const LV2_Feature * const ** host_features,
+  unsigned int * host_feature_count)
+{
+  struct lv2rack_engine * rack_ptr;
+
+  rack_ptr = ZYNJACKU_RACK_GET_PRIVATE(rack_obj_ptr);
+
+  *host_features = rack_ptr->host_features;
+  *host_feature_count = ZYNJACKU_RACK_ENGINE_FEATURES;
+}
+
+static
+void
+zynjacku_rack_unregister_port(
+  GObject * rack_obj_ptr,
+  struct zynjacku_port * port_ptr)
+{
+  struct lv2rack_engine * rack_ptr;
+
+  rack_ptr = ZYNJACKU_RACK_GET_PRIVATE(rack_obj_ptr);
+
+  if (port_ptr->data.audio != NULL)
+  {
+    jack_port_unregister(rack_ptr->jack_client, port_ptr->data.audio);
+  }
+}
+
+void
 zynjacku_rack_ui_run(
   ZynjackuRack * rack_obj_ptr)
 {
@@ -634,218 +627,121 @@ zynjacku_rack_get_version()
   return VERSION;
 }
 
-#define rack_obj_ptr ((ZynjackuRack *)context)
-
-bool
-zynjacku_rack_check_plugin(
-  void * context,
-  const char * plugin_uri,
-  const char * plugin_name,
-  uint32_t audio_in_ports_count,
-  uint32_t audio_out_ports_count,
-  uint32_t midi_in_ports_count,
-  uint32_t control_ports_count,
-  uint32_t string_ports_count,
-  uint32_t event_ports_count,
-  uint32_t midi_event_in_ports_count,
-  uint32_t ports_count)
-{
-  if (audio_in_ports_count == 0 || audio_out_ports_count == 0)
-  {
-    LOG_DEBUG("Skipping 's' %s, [effect] plugin with unsupported port configuration", name, plugin_uri);
-    LOG_DEBUG("  midi input ports: %d", (unsigned int)midi_in_ports_count);
-    LOG_DEBUG("  control ports: %d", (unsigned int)control_ports_count);
-    LOG_DEBUG("  string ports: %d", (unsigned int)string_ports_count);
-    LOG_DEBUG("  event ports: %d", (unsigned int)event_ports_count);
-    LOG_DEBUG("  event midi input ports: %d", (unsigned int)midi_event_in_ports_count);
-    LOG_DEBUG("  audio input ports: %d", (unsigned int)audio_in_ports_count);
-    LOG_DEBUG("  audio output ports: %d", (unsigned int)audio_out_ports_count);
-    LOG_DEBUG("  total ports %d", (unsigned int)ports_count);
-    return false;
-  }
-
-  LOG_DEBUG("Found effect plugin '%s' %s", name, plugin_uri);
-  LOG_DEBUG("  midi input ports: %d", (unsigned int)midi_in_ports_count);
-  LOG_DEBUG("  control ports: %d", (unsigned int)control_ports_count);
-  LOG_DEBUG("  string ports: %d", (unsigned int)string_ports_count);
-  LOG_DEBUG("  event ports: %d", (unsigned int)event_ports_count);
-  LOG_DEBUG("  event midi input ports: %d", (unsigned int)midi_event_in_ports_count);
-  LOG_DEBUG("  audio input ports: %d", (unsigned int)audio_in_ports_count);
-  LOG_DEBUG("  audio output ports: %d", (unsigned int)audio_out_ports_count);
-  LOG_DEBUG("  total ports %d", (unsigned int)ports_count);
-  return true;
-}
-
-void
-zynjacku_rack_tick(
-  void *context,
-  float progress,               /* 0..1 */
-  const char *message)
-{
-  g_signal_emit(
-    rack_obj_ptr,
-    g_zynjacku_rack_signals[ZYNJACKU_RACK_SIGNAL_TICK],
-    0,
-    progress,
-    message);
-}
-
-void
-zynjacku_rack_tack(
-  void *context,
-  const char *uri)
-{
-  const char * name;
-  const char * license;
-  const char * author;
-
-  name = zynjacku_plugin_repo_get_name(uri);
-  license = zynjacku_plugin_repo_get_license(uri);
-  author = zynjacku_plugin_repo_get_author(uri);
-
-  g_signal_emit(
-    rack_obj_ptr,
-    g_zynjacku_rack_signals[ZYNJACKU_RACK_SIGNAL_TACK],
-    0,
-    name,
-    uri,
-    license,
-    author);
-}
-
-#undef rack_obj_ptr
-
-void
-zynjacku_rack_iterate_plugins(
+const gchar *
+zynjacku_rack_get_supported_feature(
   ZynjackuRack * rack_obj_ptr,
-  gboolean force)
+  guint index)
 {
   struct lv2rack_engine * rack_ptr;
 
+  if (index >= ZYNJACKU_RACK_ENGINE_FEATURES)
+  {
+    return NULL;
+  }
+
   rack_ptr = ZYNJACKU_RACK_GET_PRIVATE(rack_obj_ptr);
 
-  zynjacku_plugin_repo_iterate(
-    force,
-    rack_ptr->host_features,
-    rack_obj_ptr,
-    zynjacku_rack_check_plugin,
-    zynjacku_rack_tick,
-    zynjacku_rack_tack);
+  return rack_ptr->host_features[index]->URI;
 }
 
-void
-zynjacku_free_effect_ports(
-  GObject * plugin_object_ptr)
+#define effect_ptr (&plugin_ptr->subtype.effect)
+
+gboolean
+zynjacku_rack_construct_plugin(
+  ZynjackuRack * rack_object_ptr,
+  ZynjackuPlugin * plugin_object_ptr)
 {
+  static unsigned int id;
+  char * port_name;
+  size_t size_name;
+  size_t size_id;
   struct lv2rack_engine * rack_ptr;
+  struct list_head * node_ptr;
+  struct zynjacku_port * port_ptr;
+  struct zynjacku_port * audio_in_left_port_ptr;
+  struct zynjacku_port * audio_in_right_port_ptr;
+  struct zynjacku_port * audio_out_left_port_ptr;
+  struct zynjacku_port * audio_out_right_port_ptr;
   struct zynjacku_plugin * plugin_ptr;
 
+  rack_ptr = ZYNJACKU_RACK_GET_PRIVATE(rack_object_ptr);
   plugin_ptr = ZYNJACKU_PLUGIN_GET_PRIVATE(plugin_object_ptr);
-  rack_ptr = ZYNJACKU_RACK_GET_PRIVATE(plugin_ptr->engine_object_ptr);
-
-  LOG_DEBUG("zynjacku_free_effect_ports() called");
-
-  zynjacku_free_plugin_ports(plugin_ptr);
 
-  if (plugin_ptr->type == PLUGIN_TYPE_EFFECT)
+  if (plugin_ptr->uri == NULL)
   {
-    if (plugin_ptr->subtype.effect.audio_out_left_port.type == PORT_TYPE_AUDIO)
-    {
-      jack_port_unregister(rack_ptr->jack_client, plugin_ptr->subtype.effect.audio_out_left_port.data.audio);
-    }
-
-    if (plugin_ptr->subtype.effect.audio_out_right_port.type == PORT_TYPE_AUDIO) /* stereo? */
-    {
-      assert(plugin_ptr->subtype.effect.audio_out_left_port.type == PORT_TYPE_AUDIO);
-      jack_port_unregister(rack_ptr->jack_client, plugin_ptr->subtype.effect.audio_out_right_port.data.audio);
-    }
+    LOG_ERROR("\"uri\" property needs to be set before constructing plugin");
+    goto fail;
   }
-}
-
-#define effect_ptr (&((struct zynjacku_plugin *)context)->subtype.effect)
-
-bool
-zynjacku_effect_create_port(
-  void * context,
-  unsigned int port_type,
-  bool output,
-  uint32_t port_index)
-{
-  struct zynjacku_port * port_ptr;
 
-  LOG_NOTICE("creating effect %s port of type %u, index %u", output ? "output" : "input", (unsigned int)port_type, (unsigned int)port_index);
+  if (plugin_ptr->name == NULL)
+  {
+    LOG_ERROR("\"name\" property needs to be set before constructing plugin");
+    goto fail;
+  }
 
-  if (port_type != PORT_TYPE_AUDIO)
+  if (plugin_ptr->dlpath == NULL)
   {
-    /* ignore unknown ports */
-    return true;
+    LOG_ERROR("Plugin %s has no dlpath set", plugin_ptr->uri);
+    goto fail;
   }
 
-  if (!output)
+  if (plugin_ptr->bundle_path == NULL)
   {
-    if (effect_ptr->audio_in_left_port.type == PORT_TYPE_INVALID)
-    {
-      port_ptr = &effect_ptr->audio_in_left_port;
-    }
-    else if (effect_ptr->audio_in_right_port.type == PORT_TYPE_INVALID)
-    {
-      port_ptr = &effect_ptr->audio_in_right_port;
-    }
-    else
-    {
-      /* ignore, we dont support more than two audio ports yet */
-      return true;
-    }
+    LOG_ERROR("Plugin %s has no bundle path set", plugin_ptr->uri);
+    goto fail;
   }
-  else
+
+  audio_in_left_port_ptr = NULL;
+  audio_in_right_port_ptr = NULL;
+
+  list_for_each(node_ptr, &plugin_ptr->audio_ports)
   {
-    if (effect_ptr->audio_out_left_port.type == PORT_TYPE_INVALID)
-    {
-      port_ptr = &effect_ptr->audio_out_left_port;
-    }
-    else if (effect_ptr->audio_out_right_port.type == PORT_TYPE_INVALID)
-    {
-      port_ptr = &effect_ptr->audio_out_right_port;
-    }
-    else
+    port_ptr = list_entry(node_ptr, struct zynjacku_port, plugin_siblings);
+    assert(port_ptr->type == PORT_TYPE_AUDIO);
+    if (PORT_IS_INPUT(port_ptr))
     {
-      /* ignore, we dont support more than two audio ports yet */
-      return true;
+      if (audio_in_left_port_ptr == NULL)
+      {
+        audio_in_left_port_ptr = port_ptr;
+        continue;
+      }
+
+      assert(audio_in_right_port_ptr == NULL);
+      audio_in_right_port_ptr = port_ptr;
+      break;
     }
   }
 
-  port_ptr->type = PORT_TYPE_AUDIO;
-  port_ptr->index = port_index;
-
-  return true;
-}
-
-#undef effect_ptr
-#define effect_ptr (&plugin_ptr->subtype.effect)
-
-bool
-zynjacku_plugin_construct_effect(
-  struct zynjacku_plugin * plugin_ptr,
-  ZynjackuPlugin * plugin_obj_ptr,
-  GObject * rack_object_ptr)
-{
-  static unsigned int id;
-  char * port_name;
-  size_t size_name;
-  size_t size_id;
-  struct lv2rack_engine * rack_ptr;
+  if (audio_in_left_port_ptr == NULL)
+  {
+    LOG_ERROR("Cannot construct effect plugin without audio input port(s). %s", plugin_ptr->uri);
+    goto fail;
+  }
 
-  rack_ptr = ZYNJACKU_RACK_GET_PRIVATE(rack_object_ptr);
+  audio_out_left_port_ptr = NULL;
+  audio_out_right_port_ptr = NULL;
 
-  plugin_ptr->type = PLUGIN_TYPE_EFFECT;
-  effect_ptr->audio_in_left_port.type = PORT_TYPE_INVALID;
-  effect_ptr->audio_in_right_port.type = PORT_TYPE_INVALID;
-  effect_ptr->audio_out_left_port.type = PORT_TYPE_INVALID;
-  effect_ptr->audio_out_right_port.type = PORT_TYPE_INVALID;
+  list_for_each(node_ptr, &plugin_ptr->audio_ports)
+  {
+    port_ptr = list_entry(node_ptr, struct zynjacku_port, plugin_siblings);
+    assert(port_ptr->type == PORT_TYPE_AUDIO);
+    if (PORT_IS_OUTPUT(port_ptr))
+    {
+      if (audio_out_left_port_ptr == NULL)
+      {
+        audio_out_left_port_ptr = port_ptr;
+        continue;
+      }
+
+      assert(audio_out_right_port_ptr == NULL);
+      audio_out_right_port_ptr = port_ptr;
+      break;
+    }
+  }
 
-  if (!zynjacku_plugin_repo_load_plugin(plugin_ptr, plugin_ptr, zynjacku_effect_create_port, zynjacku_rack_check_plugin, rack_ptr->host_features))
+  if (audio_out_left_port_ptr == NULL)
   {
-    LOG_ERROR("Failed to load LV2 info for plugin %s", plugin_ptr->uri);
+    LOG_ERROR("Cannot construct effect plugin without audio output port(s). %s", plugin_ptr->uri);
     goto fail;
   }
 
@@ -855,6 +751,8 @@ zynjacku_plugin_construct_effect(
 
   plugin_ptr->lv2plugin = zynjacku_lv2_load(
     plugin_ptr->uri,
+    plugin_ptr->dlpath,
+    plugin_ptr->bundle_path,
     zynjacku_rack_get_sample_rate(ZYNJACKU_RACK(rack_object_ptr)),
     rack_ptr->host_features);
 
@@ -876,8 +774,8 @@ zynjacku_plugin_construct_effect(
 
   if (!zynjacku_connect_plugin_ports(
         plugin_ptr,
-        plugin_obj_ptr,
-        rack_object_ptr
+        plugin_object_ptr,
+        G_OBJECT(rack_object_ptr)
 #if HAVE_DYNPARAMS
         , &rack_ptr->mempool_allocator
 #endif
@@ -888,31 +786,45 @@ zynjacku_plugin_construct_effect(
 
   /* setup audio ports (they are connected in jack process callback */
 
+  effect_ptr->audio_in_left_port_ptr = audio_in_left_port_ptr;
+  effect_ptr->audio_in_right_port_ptr = audio_in_right_port_ptr;
+  effect_ptr->audio_out_left_port_ptr = audio_out_left_port_ptr;
+  effect_ptr->audio_out_right_port_ptr = audio_out_right_port_ptr;
+
   size_name = strlen(plugin_ptr->name);
   port_name = malloc(size_name + 1024);
   if (port_name == NULL)
   {
     LOG_ERROR("Failed to allocate memory for port name");
-    goto fail_free_ports;
+    goto fail_unload;
   }
 
   size_id = sprintf(port_name, "%u:", id);
   memcpy(port_name + size_id, plugin_ptr->name, size_name);
 
-  if (effect_ptr->audio_out_left_port.type == PORT_TYPE_AUDIO &&
-      effect_ptr->audio_out_right_port.type == PORT_TYPE_AUDIO)
+  if (audio_out_left_port_ptr != NULL &&
+      audio_out_right_port_ptr != NULL)
   {
+    assert(audio_out_left_port_ptr->type == PORT_TYPE_AUDIO);
+    assert(PORT_IS_OUTPUT(audio_out_left_port_ptr));
+
     strcpy(port_name + size_id + size_name, " L");
-    effect_ptr->audio_out_left_port.data.audio = jack_port_register(rack_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+    audio_out_left_port_ptr->data.audio = jack_port_register(rack_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+    assert(audio_out_right_port_ptr->type == PORT_TYPE_AUDIO);
+    assert(PORT_IS_OUTPUT(audio_out_right_port_ptr));
 
     strcpy(port_name + size_id + size_name, " R");
-    effect_ptr->audio_out_right_port.data.audio = jack_port_register(rack_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+    audio_out_right_port_ptr->data.audio = jack_port_register(rack_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
   }
-  else if (effect_ptr->audio_out_left_port.type == PORT_TYPE_AUDIO &&
-           effect_ptr->audio_out_right_port.type == PORT_TYPE_INVALID)
+  else if (audio_out_left_port_ptr != NULL &&
+           audio_out_right_port_ptr == NULL)
   {
+    assert(audio_out_left_port_ptr->type == PORT_TYPE_AUDIO);
+    assert(PORT_IS_OUTPUT(audio_out_left_port_ptr));
+
     port_name[size_id + size_name] = 0;
-    effect_ptr->audio_out_left_port.data.audio = jack_port_register(rack_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+    audio_out_left_port_ptr->data.audio = jack_port_register(rack_ptr->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
   }
 
   port_name[size_id + size_name] = 0;
@@ -933,12 +845,9 @@ zynjacku_plugin_construct_effect(
 
   g_object_ref(plugin_ptr->engine_object_ptr);
 
-  /* no plugins to test gtk2gui */
-  plugin_ptr->gtk2gui = zynjacku_gtk2gui_create(rack_ptr->host_features, ZYNJACKU_RACK_ENGINE_FEATURES, plugin_ptr->lv2plugin, 
-    plugin_ptr, plugin_obj_ptr, plugin_ptr->uri, plugin_ptr->id, &plugin_ptr->parameter_ports);
-
   plugin_ptr->deactivate = zynjacku_rack_deactivate_effect;
-  plugin_ptr->free_ports = zynjacku_free_effect_ports;
+  plugin_ptr->unregister_port = zynjacku_rack_unregister_port;
+  plugin_ptr->get_required_features = zynjacku_rack_get_required_features;
 
   /* we dont support midi cc maps for lv2rack yet */
   plugin_ptr->set_midi_cc_map = NULL;
@@ -948,18 +857,6 @@ zynjacku_plugin_construct_effect(
 
   return true;
 
-fail_free_ports:
-  zynjacku_free_effect_ports(G_OBJECT(plugin_obj_ptr));
-  plugin_ptr->engine_object_ptr = NULL;
-
-#if HAVE_DYNPARAMS
-  if (plugin_ptr->dynparams != NULL)
-  {
-    lv2dynparam_host_detach(plugin_ptr->dynparams);
-    plugin_ptr->dynparams = NULL;
-  }
-#endif
-
 fail_unload:
   zynjacku_lv2_unload(plugin_ptr->lv2plugin);
 
diff --git a/rack.h b/rack.h
index 13b02a9..fe96a6f 100644
--- a/rack.h
+++ b/rack.h
@@ -70,10 +70,15 @@ zynjacku_rack_ui_run(
 const gchar *
 zynjacku_rack_get_version();
 
-void
-zynjacku_rack_iterate_plugins(
+const gchar *
+zynjacku_rack_get_supported_feature(
+  ZynjackuRack * rack_obj_ptr,
+  guint index);
+
+gboolean
+zynjacku_rack_construct_plugin(
   ZynjackuRack * rack_obj_ptr,
-  gboolean force);
+  ZynjackuPlugin * plugin_obj_ptr);
 
 G_END_DECLS
 
diff --git a/synth.h b/synth.h
deleted file mode 100644
index 3031433..0000000
--- a/synth.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C ; c-basic-offset: 2 -*- */
-/*****************************************************************************
- *
- *   This file is part of zynjacku
- *
- *   Copyright (C) 2008,2009 Nedko Arnaudov <nedko at arnaudov.name>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; version 2 of the License
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *****************************************************************************/
-
-#ifndef SYNTH_H__6B544DDB_CFAC_4182_B10B_6183A3AB0933__INCLUDED
-#define SYNTH_H__6B544DDB_CFAC_4182_B10B_6183A3AB0933__INCLUDED
-
-bool
-zynjacku_plugin_construct_synth(
-  struct zynjacku_plugin * plugin_ptr,
-  ZynjackuPlugin * plugin_obj_ptr,
-  GObject * engine_object_ptr);
-
-#endif /* #ifndef SYNTH_H__6B544DDB_CFAC_4182_B10B_6183A3AB0933__INCLUDED */
diff --git a/ttl_lexer.h b/ttl_lexer.h
new file mode 100644
index 0000000..cad2ca5
--- /dev/null
+++ b/ttl_lexer.h
@@ -0,0 +1,10 @@
+#ifndef _TTLDATA_H
+#define _TTLDATA_H
+
+#include <Python.h>
+
+extern PyObject *ttl_list;
+
+#define YY_NO_INPUT
+
+#endif
diff --git a/zynjacku.defs b/zynjacku.defs
index b90ec24..cd115e8 100644
--- a/zynjacku.defs
+++ b/zynjacku.defs
@@ -94,12 +94,21 @@
   )
 )
 
-(define-method iterate_plugins
+(define-method get_supported_feature
   (of-object "ZynjackuEngine")
-  (c-name "zynjacku_engine_iterate_plugins")
-  (return-type "none")
+  (c-name "zynjacku_engine_get_supported_feature")
+  (return-type "const-gchar*")
+  (parameters
+    '("guint" "index")
+  )
+)
+
+(define-method construct_plugin
+  (of-object "ZynjackuEngine")
+  (c-name "zynjacku_engine_construct_plugin")
+  (return-type "gboolean")
   (parameters
-    '("gboolean" "force")
+    '("ZynjackuPlugin*" "plugin_obj_ptr")
   )
 )
 
@@ -148,12 +157,21 @@
   )
 )
 
-(define-method iterate_plugins
+(define-method get_supported_feature
   (of-object "ZynjackuRack")
-  (c-name "zynjacku_rack_iterate_plugins")
-  (return-type "none")
+  (c-name "zynjacku_rack_get_supported_feature")
+  (return-type "const-gchar*")
+  (parameters
+    '("guint" "index")
+  )
+)
+
+(define-method construct_plugin
+  (of-object "ZynjackuRack")
+  (c-name "zynjacku_rack_construct_plugin")
+  (return-type "gboolean")
   (parameters
-    '("gboolean" "force")
+    '("ZynjackuPlugin*" "plugin_obj_ptr")
   )
 )
 
@@ -168,33 +186,12 @@
   )
 )
 
-(define-method construct
-  (of-object "ZynjackuPlugin")
-  (c-name "zynjacku_plugin_construct")
-  (return-type "gboolean")
-  (parameters
-    '("GObject*" "engine_obj_ptr")
-  )
-)
-
 (define-method destruct
   (of-object "ZynjackuPlugin")
   (c-name "zynjacku_plugin_destruct")
   (return-type "none")
 )
 
-(define-method supports_generic_ui
-  (of-object "ZynjackuPlugin")
-  (c-name "zynjacku_plugin_supports_generic_ui")
-  (return-type "gboolean")
-)
-
-(define-method supports_custom_ui
-  (of-object "ZynjackuPlugin")
-  (c-name "zynjacku_plugin_supports_custom_ui")
-  (return-type "gboolean")
-)
-
 (define-method get_instance_name
   (of-object "ZynjackuPlugin")
   (c-name "zynjacku_plugin_get_instance_name")
@@ -217,6 +214,12 @@
   (of-object "ZynjackuPlugin")
   (c-name "zynjacku_plugin_ui_on")
   (return-type "gboolean")
+  (parameters
+    '("const-char*" "ui_uri" (null-ok) (default "NULL"))
+    '("const-char*" "ui_type_uri" (null-ok) (default "NULL"))
+    '("const-char*" "ui_binary_path" (null-ok) (default "NULL"))
+    '("const-char*" "ui_bundle_path" (null-ok) (default "NULL"))
+  )
 )
 
 (define-method ui_off
@@ -301,6 +304,89 @@
   )
 )
 
+(define-method add_supported_feature
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_add_supported_feature")
+  (return-type "none")
+  (parameters
+    '("const-gchar*" "feature_uri")
+  )
+)
+
+(define-method create_oldmidi_input_port
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_create_oldmidi_input_port")
+  (return-type "gboolean")
+  (parameters
+    '("guint" "port_index")
+    '("const-gchar*" "symbol")
+  )
+)
+
+(define-method create_eventmidi_input_port
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_create_eventmidi_input_port")
+  (return-type "gboolean")
+  (parameters
+    '("guint" "port_index")
+    '("const-gchar*" "symbol")
+  )
+)
+
+(define-method create_audio_port
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_create_audio_port")
+  (return-type "gboolean")
+  (parameters
+    '("guint" "port_index")
+    '("const-gchar*" "symbol")
+    '("gboolean" "input")
+  )
+)
+
+(define-method create_float_parameter_port
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_create_float_parameter_port")
+  (return-type "gboolean")
+  (parameters
+    '("guint" "port_index")
+    '("const-gchar*" "symbol")
+    '("const-gchar*" "name")
+    '("gboolean" "msgcontext")
+    '("gboolean" "default_provided")
+    '("gfloat" "default_value")
+    '("gboolean" "min_provided")
+    '("gfloat" "min_value")
+    '("gboolean" "max_provided")
+    '("gfloat" "max_value")
+  )
+)
+
+(define-method create_float_measure_port
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_create_float_measure_port")
+  (return-type "gboolean")
+  (parameters
+    '("guint" "port_index")
+    '("const-gchar*" "symbol")
+    '("gboolean" "msgcontext")
+  )
+)
+
+(define-method create_string_parameter_port
+  (of-object "ZynjackuPlugin")
+  (c-name "zynjacku_plugin_create_string_parameter_port")
+  (return-type "gboolean")
+  (parameters
+    '("guint" "port_index")
+    '("const-gchar*" "symbol")
+    '("const-gchar*" "name")
+    '("gboolean" "msgcontext")
+    '("const-gchar*" "default_value")
+    '("gsize" "maxlen")
+  )
+)
+
 
 
 ;; From enum.h
@@ -455,3 +541,96 @@
 )
 
 
+
+;; From lv2.h
+
+(define-function zynjacku_lv2_dman_get
+  (c-name "zynjacku_lv2_dman_get")
+  (return-type "char*")
+  (parameters
+    '("const-char*" "dlpath")
+  )
+)
+
+(define-function zynjacku_lv2_load
+  (c-name "zynjacku_lv2_load")
+  (return-type "zynjacku_lv2_handle")
+  (parameters
+    '("const-char*" "uri")
+    '("const-char*" "dlpath")
+    '("const-char*" "bundle_path")
+    '("double" "sample_rate")
+    '("const-LV2_Feature*-const*" "host_features")
+  )
+)
+
+(define-function zynjacku_lv2_unload
+  (c-name "zynjacku_lv2_unload")
+  (return-type "none")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+  )
+)
+
+(define-function zynjacku_lv2_connect_port
+  (c-name "zynjacku_lv2_connect_port")
+  (return-type "none")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+    '("struct-zynjacku_port*" "port")
+    '("void*" "data_location")
+  )
+)
+
+(define-function zynjacku_lv2_run
+  (c-name "zynjacku_lv2_run")
+  (return-type "none")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+    '("uint32_t" "sample_count")
+  )
+)
+
+(define-function zynjacku_lv2_activate
+  (c-name "zynjacku_lv2_activate")
+  (return-type "none")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+  )
+)
+
+(define-function zynjacku_lv2_deactivate
+  (c-name "zynjacku_lv2_deactivate")
+  (return-type "none")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+  )
+)
+
+(define-function zynjacku_lv2_get_descriptor
+  (c-name "zynjacku_lv2_get_descriptor")
+  (return-type "const-LV2_Descriptor*")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+  )
+)
+
+(define-function zynjacku_lv2_get_handle
+  (c-name "zynjacku_lv2_get_handle")
+  (return-type "LV2_Handle")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+  )
+)
+
+(define-function zynjacku_lv2_message
+  (c-name "zynjacku_lv2_message")
+  (return-type "none")
+  (parameters
+    '("zynjacku_lv2_handle" "lv2handle")
+    '("const-void*" "input_data")
+    '("void*" "output_data")
+  )
+)
+
+
diff --git a/zynjacku.h b/zynjacku.h
index ae2a525..bd65936 100644
--- a/zynjacku.h
+++ b/zynjacku.h
@@ -49,7 +49,6 @@
 struct zynjacku_port
 {
   struct list_head plugin_siblings;
-  struct list_head port_type_siblings;
   unsigned int type;            /* one of PORT_TYPE_XXX */
   unsigned int flags;           /* bitmask of PORT_FLAGS_XXX */
   uint32_t index;               /* LV2 port index within owning plugin, unused for dynparam ports */
@@ -60,7 +59,9 @@ struct zynjacku_port
     struct
     {
       float value;
+      bool min_provided;
       float min;
+      bool max_provided;
       float max;
     } lv2float;                /* for PORT_TYPE_LV2_FLOAT */
     struct _LV2_String_Data lv2string; /* for PORT_TYPE_LV2_STRING */
diff --git a/zynjacku.override b/zynjacku.override
index 001903c..b2f4ce2 100644
--- a/zynjacku.override
+++ b/zynjacku.override
@@ -11,6 +11,10 @@ headers
 #include "enum.h"
 #include "hints.h"
 #include "midi_cc_map.h"
+
+char *
+zynjacku_lv2_dman_get(
+  const char * dlpath);
 %%
 modulename zynjacku_c
 %%
diff --git a/zynjacku.py b/zynjacku.py
index 290b959..9472b71 100755
--- a/zynjacku.py
+++ b/zynjacku.py
@@ -29,10 +29,30 @@ import xml.dom.minidom
 import cairo
 from math import pi, sin, cos, atan2
 from colorsys import hls_to_rgb, rgb_to_hls
+from distutils import sysconfig
 
 old_path = sys.path
-sys.path.insert(0, "%s/.libs" % os.path.dirname(sys.argv[0]))
-import zynjacku_c as zynjacku
+
+inplace_libs = os.path.join(os.path.dirname(sys.argv[0]), ".libs")
+if os.access(inplace_libs, os.R_OK):
+    sys.path.append(inplace_libs)
+else:
+    inplace_libs = None
+
+try:
+    if inplace_libs:
+        import zynjacku_c as zynjacku
+    else:
+        from zynworld import zynjacku_c as zynjacku
+    from zynworld import lv2
+except Exception, e:
+    print "Failed to import zynjacku internal python modules"
+    print repr(e)
+    print "These directories were searched"
+    for path in sys.path:
+        print "    " + path
+    sys.exit(1)
+
 sys.path = old_path
 
 try:
@@ -1396,13 +1416,18 @@ class PluginUI(gobject.GObject):
 
     def show(self):
         '''Show synth window'''
+        return False
 
     def hide(self):
         '''Hide synth window'''
 
 class PluginUICustom(PluginUI):
-    def __init__(self, plugin):
+    def __init__(self, plugin, ui_uri, ui_type_uri, ui_binary_path, ui_bundle_path):
         PluginUI.__init__(self, plugin)
+        self.ui_uri = ui_uri
+        self.ui_type_uri = ui_type_uri
+        self.ui_binary_path = ui_binary_path
+        self.ui_bundle_path = ui_bundle_path
 
         self.plugin.connect("custom-gui-off", self.on_window_destroy)
 
@@ -1412,7 +1437,7 @@ class PluginUICustom(PluginUI):
 
     def show(self):
         '''Show synth window'''
-        return self.plugin.ui_on()
+        return self.plugin.ui_on(self.ui_uri, self.ui_type_uri, self.ui_binary_path, self.ui_bundle_path)
 
     def hide(self):
         '''Hide synth window'''
@@ -1536,6 +1561,7 @@ class PluginUIUniversal(PluginUI):
                 child.parent_group.child_param_add(child)
 
         self.window.show_all()
+        return True
 
     def hide(self):
         '''Hide synth window'''
@@ -2168,6 +2194,37 @@ class host:
 
         self.lash_client = lash_client
 
+        self.lv2features_supported = []
+        index = 0
+        while True:
+            feature = engine.get_supported_feature(index)
+            if not feature:
+                break
+            self.lv2features_supported.append(feature)
+            index += 1
+
+        self.available_plugins = []
+
+        lv2sources = []
+        cachedir = os.path.join(os.environ["HOME"], ".cache", preset_extension)
+        if not os.path.isdir(cachedir):
+            print 'Creating cache directory "%s"' % cachedir
+            os.makedirs(cachedir, 0755)
+
+        self.lv2_plugins_cache = os.path.join(cachedir, "plugins")
+        if os.path.isfile(self.lv2_plugins_cache):
+            print 'Loading LV2 plugin cache from "%s"' % self.lv2_plugins_cache
+            for line in file(self.lv2_plugins_cache):
+                source = line.strip()
+                if os.path.isfile(source) and os.access(source, os.R_OK):
+                    lv2sources.append(source)
+                else:
+                    print "Warning \"%s\" is not readble" % source
+        else:
+            file(self.lv2_plugins_cache, 'w')
+
+        self.lv2db = lv2.LV2DB(lv2sources)
+
     def lash_check_events(self):
         while lash.lash_get_pending_event_count(self.lash_client):
             event = lash.lash_get_event(self.lash_client)
@@ -2199,11 +2256,30 @@ class host:
         return True
 
     def create_plugin_ui(self, plugin, data=None):
-        if not self.plugin_ui_available(plugin):
-            return False
+        info = self.lv2db.getPluginInfo(plugin.uri)
+
+        ui_binary_path = None
+
+        for ui_uri in info.ui:
+            ui = self.lv2db.get_ui_info(plugin.uri, ui_uri)
+            features_match = True
+            for required_feature in ui.requiredFeatures:
+                if required_feature == "http://lv2plug.in/ns/extensions/ui#makeResident":
+                    continue
+                if not required_feature in self.lv2features_supported:
+                    features_match = False
+
+            if not features_match:
+                print "Skipping UI %s because of missing required feature %s" % (ui_uri, required_feature)
+                break
 
-        if plugin.supports_custom_ui():
-            plugin.ui_win = PluginUICustom(plugin)
+            ui_type_uri = ui.type
+            ui_binary_path = ui.binary
+            break
+
+        if ui_binary_path:
+            ui_bundle_path = os.path.dirname(ui.binary) + '/'
+            plugin.ui_win = PluginUICustom(plugin, ui_uri, ui_type_uri, ui_binary_path, ui_bundle_path)
         else:
             plugin.ui_win = PluginUIUniversal(plugin, self.group_shadow_type, self.layout_type)
 
@@ -2213,12 +2289,98 @@ class host:
     def on_plugin_ui_window_destroyed(self, window, plugin, data):
         return
 
-    def plugin_ui_available(self, plugin):
-        return plugin.supports_custom_ui() or plugin.supports_generic_ui()
-
     def new_plugin(self, uri, parameters=[], maps={}):
-        plugin = zynjacku.Plugin(uri=uri)
-        if not plugin.construct(self.engine):
+        info = self.lv2db.getPluginInfo(uri)
+        if not info:
+            print 'Cannot get info for plugin "%s"' % uri
+            return False
+
+        plugin = zynjacku.Plugin(
+            uri = uri,
+            name = info.name,
+            dlpath = info.binary,
+            bundle_path = os.path.dirname(info.binary) + '/')
+
+        for port in info.ports:
+            msgcontext = "http://lv2plug.in/ns/dev/contexts#MessageContext" in port.contexts
+
+            if port.isAudio and not msgcontext:
+                if not plugin.create_audio_port(port.index, port.symbol, not port.isOutput):
+                    return False
+            elif port.isLarslMidi and port.isInput and not msgcontext:
+                if not plugin.create_oldmidi_input_port(port.index, port.symbol):
+                    return False
+            elif port.isEvent and port.isInput and "http://lv2plug.in/ns/ext/midi#MidiEvent" in port.events and not msgcontext:
+                if not plugin.create_eventmidi_input_port(port.index, port.symbol):
+                    return False
+            elif port.isControl:
+                if port.isInput:
+                    default_provided = not port.defaultValue is None
+                    if default_provided:
+                        default_value = float(port.defaultValue)
+                    else:
+                        default_value = 0.0
+
+                    min_provided = not port.minimum is None
+                    if min_provided:
+                        min_value = float(port.minimum);
+                    else:
+                        min_value = 0.0
+
+                    max_provided = not port.maximum is None
+                    if max_provided:
+                        max_value = float(port.maximum)
+                    else:
+                        max_value = 1.0
+
+                    if not plugin.create_float_parameter_port(
+                        port.index,
+                        port.symbol,
+                        port.name,
+                        msgcontext,
+                        default_provided,
+                        default_value,
+                        min_provided,
+                        min_value,
+                        max_provided,
+                        max_value):
+                        return False
+                else:
+                    if not plugin.create_float_measure_port(port.index, port.symbol, msgcontext):
+                        return False
+            elif port.isString and port.isInput:
+                maxlen = 256 # TODO: get from lv2 (requiredSpace)
+
+                default_provided = port.__dict__.has_key('defaultValue')
+                if default_provided:
+                    default_value = port.defaultValue
+                else:
+                    default_value = ""
+
+                if not plugin.create_string_parameter_port(
+                    port.index,
+                    port.symbol,
+                    port.name,
+                    msgcontext,
+                    default_value,
+                    maxlen):
+                    return False
+            else:
+                print "Port %s with not matched type" % port.symbol
+                if not "http://lv2plug.in/ns/lv2core#connectionOptional" in port.properties:
+                    return False
+
+            # TODO: tell handle scale points somehow
+            #splist = port.scalePoints
+            #splist.sort(lambda x, y: cmp(x[1], y[1]))
+            #if len(splist):
+            #    for sp in splist:
+            #        print "       Scale point %s: %s" % (sp[1], sp[0])
+
+        for feature in info.requiredFeatures + info.optionalFeatures:
+            plugin.add_supported_feature(feature)
+
+        if not self.engine.construct_plugin(plugin):
             return False
 
         for parameter in parameters:
@@ -2241,28 +2403,88 @@ class host:
     def on_plugin_progress(self, engine, name, progress, message):
         print "Loading plugin '%s', %5.1f%% complete. %s" % (name, progress, message)
 
-    def on_plugin_repo_tick(self, repo, progress, uri, progressbar):
-        if progress == 1.0:
-            progressbar.hide()
-            return
+    def check_plugin(self, plugin):
+        return False
+
+    def rescan_plugins(self, view, progressbar, force):
+        # detach from store to optimize speed
+        store = view.get_model()
+        view.set_model(None)
+        view.set_sensitive(False)
+
+        store.clear()
 
         progressbar.show()
-        progressbar.set_fraction(progress)
-        progressbar.set_text("Checking %s" % uri);
-        while gtk.events_pending():
-            gtk.main_iteration()
 
-    def on_plugin_repo_tack(self, repo, name, uri, plugin_license, author, store):
-        #print "tack: %s %s %s" % (name, uri, plugin_license)
-        store.append([name, uri, plugin_license, author])
+        if force:
+            self.lv2db = lv2.LV2DB()
+            self.available_plugins = []
 
-    def rescan_plugins(self, store, progressbar, force):
-        store.clear()
-        tick = self.engine.connect("tick", self.on_plugin_repo_tick, progressbar)
-        tack = self.engine.connect("tack", self.on_plugin_repo_tack, store)
-        self.engine.iterate_plugins(force)
-        self.engine.disconnect(tack)
-        self.engine.disconnect(tick)
+        if self.available_plugins and not force:
+            for plugin in self.available_plugins:
+                store.append([plugin.name, plugin.uri, plugin.license_decoded, plugin.maintainers_string])
+        else:
+            lv2sources = set()
+            self.available_plugins = []
+
+            progressbar.set_text("Searching for LV2 plugins...");
+            progressbar.set_fraction(0.0)
+
+            plugins = self.lv2db.getPluginList()
+
+            step = 1.0 / len(plugins)
+            progress = 0.0
+
+            for uri in plugins:
+                progressbar.set_fraction(progress)
+                progressbar.set_text("Checking %s" % uri);
+                plugin = self.lv2db.getPluginInfo(uri)
+                if plugin == None:
+                    continue
+
+                while gtk.events_pending():
+                    gtk.main_iteration()
+
+                features_met = True
+                for feature in plugin.requiredFeatures:
+                    if not feature in self.lv2features_supported:
+                        features_met = False
+                        break
+
+                if features_met and self.check_plugin(plugin):
+                    plugin.maintainers_string = ""
+                    for maintainer in plugin.maintainers:
+                        if plugin.maintainers_string:
+                            plugin.maintainers_string += "; "
+                        plugin.maintainers_string += maintainer['name']
+                    if not plugin.maintainers_string:
+                        plugin.maintainers_string = "unknown"
+
+                    license_map = {
+                        "http://usefulinc.com/doap/licenses/gpl": "GNU General Public License",
+                        "http://usefulinc.com/doap/licenses/lgpl":"GNU Lesser General Public License",
+                        }
+                    if license_map.has_key(plugin.license):
+                        plugin.license_decoded = license_map[plugin.license]
+                    else:
+                        plugin.license_decoded = plugin.license
+
+                    self.available_plugins.append(plugin)
+                    store.append([plugin.name, plugin.uri, plugin.license_decoded, plugin.maintainers_string])
+                    for source in plugin.sources:
+                        lv2sources.add(source)
+
+                progress += step
+
+            if lv2sources and os.path.isfile(self.lv2_plugins_cache):
+                cachefile = file(self.lv2_plugins_cache, 'w')
+                for source in lv2sources:
+                    cachefile.write(source + "\n")
+
+        progressbar.hide()
+
+        view.set_sensitive(True)
+        view.set_model(store)
 
     def plugins_load(self, title="LV2 plugins"):
         dialog = self.glade_xml.get_widget("zynjacku_plugin_repo")
@@ -2271,33 +2493,37 @@ class host:
 
         dialog.set_title(title)
 
-        plugin_repo_widget.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+        store = plugin_repo_widget.get_model()
+        if not store:
+            plugin_repo_widget.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
 
-        store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
-        text_renderer = gtk.CellRendererText()
+            store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+            text_renderer = gtk.CellRendererText()
+
+            column_name = gtk.TreeViewColumn("Name", text_renderer, text=0)
+            column_uri = gtk.TreeViewColumn("URI", text_renderer, text=1)
+            column_license = gtk.TreeViewColumn("License", text_renderer, text=2)
+            column_author = gtk.TreeViewColumn("Author", text_renderer, text=3)
 
-        column_name = gtk.TreeViewColumn("Name", text_renderer, text=0)
-        column_uri = gtk.TreeViewColumn("URI", text_renderer, text=1)
-        column_license = gtk.TreeViewColumn("License", text_renderer, text=2)
-        column_author = gtk.TreeViewColumn("Author", text_renderer, text=3)
+            column_name.set_sort_column_id(0)
+            column_uri.set_sort_column_id(1)
+            column_license.set_sort_column_id(2)
+            column_author.set_sort_column_id(3)
 
-        column_name.set_sort_column_id(0)
-        column_uri.set_sort_column_id(1)
-        column_license.set_sort_column_id(2)
-        column_author.set_sort_column_id(3)
+            plugin_repo_widget.append_column(column_name)
+            plugin_repo_widget.append_column(column_uri)
+            plugin_repo_widget.append_column(column_license)
+            plugin_repo_widget.append_column(column_author)
 
-        plugin_repo_widget.append_column(column_name)
-        plugin_repo_widget.append_column(column_uri)
-        plugin_repo_widget.append_column(column_license)
-        plugin_repo_widget.append_column(column_author)
+            store.set_sort_column_id(0, gtk.SORT_ASCENDING)
 
-        plugin_repo_widget.set_model(store)
-        def on_row_activated(widget, path, column):
-            dialog.response(0)
-        plugin_repo_widget.connect("row-activated", on_row_activated)
+            plugin_repo_widget.set_model(store)
+            def on_row_activated(widget, path, column):
+                dialog.response(0)
+            plugin_repo_widget.connect("row-activated", on_row_activated)
 
         dialog.show()
-        self.rescan_plugins(store, progressbar, False)
+        self.rescan_plugins(plugin_repo_widget, progressbar, False)
         while True:
             ret = dialog.run()
             if ret == 0:
@@ -2307,7 +2533,7 @@ class host:
                 self.progress_window.hide()
                 return
             elif ret == 1:
-                self.rescan_plugins(store, progressbar, True)
+                self.rescan_plugins(plugin_repo_widget, progressbar, True)
             else:
                 dialog.hide()
                 return
@@ -2497,11 +2723,7 @@ class host:
         self.engine.ui_run()
         return True
 
-    def run(self):
-        ui_run_callback_id = gobject.timeout_add(40, self.ui_run)
-
-        gtk.main()
-        gobject.source_remove(ui_run_callback_id)
+    def run_done(self):
         if self.lash_client:
             #print "removing lash handler, host object refcount is %u" % sys.getrefcount(self)
             gobject.source_remove(self.lash_check_events_callback_id)
@@ -2513,6 +2735,12 @@ class host:
 
         self.engine.disconnect(self.progress_connect_id)
 
+    def run(self):
+        ui_run_callback_id = gobject.timeout_add(40, self.ui_run)
+        gtk.main()
+        gobject.source_remove(ui_run_callback_id)
+        self.run_done()
+
     def on_test(self, obj1, obj2):
         print "on_test() called !!!!!!!!!!!!!!!!!!"
         print repr(obj1)
@@ -2524,20 +2752,39 @@ class ZynjackuHost(host):
 
         host.__init__(self, zynjacku.Engine(), client_name, preset_extension, preset_name, lash_client)
 
+def run_about_dialog(transient_for, program_data):
+    about = gtk.AboutDialog()
+    about.set_transient_for(transient_for)
+    about.set_name(program_data['name'])
+    if program_data.has_key('comments'):
+        about.set_name(program_data['comments'])
+    if program_data.has_key('version'):
+        about.set_version(program_data['version'])
+    about.set_license(program_data['license'])
+    about.set_website(program_data['website'])
+    about.set_authors(program_data['authors'])
+    if program_data.has_key('artists'):
+        about.set_artists(program_data['artists'])
+    if program_data.has_key('logo'):
+        about.set_logo(gtk.gdk.pixbuf_new_from_file(program_data['logo']))
+    about.show()
+    about.run()
+    about.hide()
+
 class ZynjackuHostMulti(ZynjackuHost):
-    def __init__(self, data_dir, glade_xml, client_name, the_license, uris, lash_client):
+    def __init__(self, program_data, client_name, uris, lash_client):
         #print "ZynjackuHostMulti constructor called."
         ZynjackuHost.__init__(self, client_name, "zynjacku", "synth stack", lash_client)
         
-        self.data_dir = data_dir
-        self.glade_xml = glade_xml
+        self.program_data = program_data
+        self.glade_xml = program_data['glade_xml']
 
-        self.main_window = glade_xml.get_widget("zynjacku_main")
+        self.main_window = self.glade_xml.get_widget("zynjacku_main")
         self.main_window.set_title(client_name)
 
         self.statusbar = self.glade_xml.get_widget("statusbar")
 
-        self.hbox_menubar = glade_xml.get_widget("hbox_menubar")
+        self.hbox_menubar = self.glade_xml.get_widget("hbox_menubar")
         self.midi_led = midi_led()
         self.midi_led_frame = gtk.Frame()
         self.midi_led_frame.set_shadow_type(gtk.SHADOW_OUT)
@@ -2555,17 +2802,21 @@ class ZynjackuHostMulti(ZynjackuHost):
 
         self.signal_ids = []
         for k, v in dic.items():
-            w = glade_xml.get_widget(k)
+            w = self.glade_xml.get_widget(k)
             if not w:
                 print "failed to get glade widget '%s'" % k
                 continue
             self.signal_ids.append([w, w.connect("activate", v)])
 
-        self.the_license = the_license
+        self.synths_widget = self.glade_xml.get_widget("treeview_synths")
 
-        self.synths_widget = glade_xml.get_widget("treeview_synths")
+        self.store = gtk.ListStore(
+            gobject.TYPE_BOOLEAN,       # UI visible
+            gobject.TYPE_STRING,        # Instance name
+            gobject.TYPE_STRING,        # Plugin name
+            gobject.TYPE_STRING,        # Plugin URI
+            gobject.TYPE_PYOBJECT)      # Plugin object
 
-        self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
         text_renderer = gtk.CellRendererText()
         self.toggle_renderer = gtk.CellRendererToggle()
         self.toggle_renderer.set_property('activatable', True)
@@ -2620,6 +2871,68 @@ class ZynjackuHostMulti(ZynjackuHost):
         self.midi_led.set(self.engine.get_midi_activity())
         return True
 
+    def check_plugin(self, plugin):
+        ports_count = len(plugin.ports)
+        audio_in_ports_count = 0
+        audio_out_ports_count = 0
+        midi_in_ports_count = 0
+        control_ports_count = 0
+        string_ports_count = 0
+        event_ports_count = 0
+        midi_event_in_ports_count = 0
+
+        for port in plugin.ports:
+            if port.isAudio:
+                if port.isInput:
+                    audio_in_ports_count += 1
+                    continue
+                if port.isOutput:
+                    audio_out_ports_count += 1
+                    continue
+                continue
+            if port.isLarslMidi:
+                if port.isInput:
+                    midi_in_ports_count += 1
+                    continue
+                continue
+            if port.isEvent:
+                event_ports_count += 1
+                if port.isInput and "http://lv2plug.in/ns/ext/midi#MidiEvent" in port.events:
+                    midi_event_in_ports_count += 1
+                    continue
+                continue
+            if port.isControl:
+                control_ports_count += 1
+                continue
+            if port.isString:
+                string_ports_count += 1
+                continue
+
+        # TODO: we must be smarter and check for "optional connect" property
+        if (midi_in_ports_count + control_ports_count + string_ports_count + event_ports_count + audio_out_ports_count != ports_count) or \
+               (midi_in_ports_count + midi_event_in_ports_count != 1) or \
+               (audio_out_ports_count == 0):
+            #print "Skipping %s (%s), [synth] plugin with unsupported port configuration" % (plugin.name, plugin.uri)
+            #print "  midi input ports: %d" % midi_in_ports_count
+            #print "  control ports: %d" % control_ports_count
+            #print "  string ports: %d" % string_ports_count
+            #print "  event ports: %d" % event_ports_count
+            #print "  event midi input ports: %d" % midi_event_in_ports_count
+            #print "  audio input ports: %d" % audio_in_ports_count
+            #print "  audio output ports: %d" % audio_out_ports_count
+            #print "  total ports %d" % ports_count
+            return False
+
+#         print "Found \"simple\" synth plugin '%s' %s" % (plugin.name, plugin.uri)
+#         print "  midi input ports: %d" % midi_in_ports_count
+#         print "  control ports: %d" % control_ports_count
+#         print "  event ports: %d" % event_ports_count
+#         print "  event midi input ports: %d" % midi_event_in_ports_count
+#         print "  audio input ports: %d" % audio_in_ports_count
+#         print "  audio output ports: %d" % audio_out_ports_count
+#         print "  total ports %d" % ports_count
+        return True
+
     def load_plugin(self, uri, parameters=[], maps={}):
         statusbar_context_id = self.statusbar.get_context_id("loading plugin")
         statusbar_id = self.statusbar.push(statusbar_context_id, "Loading %s" % uri)
@@ -2630,7 +2943,7 @@ class ZynjackuHostMulti(ZynjackuHost):
         if not synth:
             self.statusbar.push(statusbar_context_id, "Failed to construct %s" % uri)
         else:
-            row = False, synth.get_instance_name(), synth.get_name(), synth.get_uri(), synth
+            row = False, synth.get_instance_name(), synth.get_name(), synth.uri, synth
             self.store.append(row)
             self.statusbar.remove(statusbar_context_id, statusbar_id)
 
@@ -2664,24 +2977,10 @@ class ZynjackuHostMulti(ZynjackuHost):
             if model[path][4].ui_win.show():
                 model[path][0] = True
             else:
-                self.statusbar.push(statusbar_context_id, "Failed to construct show synth UI")
+                self.statusbar.push(statusbar_context_id, "Failed to show synth UI")
 
     def on_about(self, widget):
-        about = gtk.AboutDialog()
-        about.set_transient_for(self.main_window)
-        about.set_name("zynjacku")
-        if zynjacku.zynjacku_get_version() == "dev":
-            about.set_comments("(development snapshot)")
-        else:
-            about.set_version(zynjacku.zynjacku_get_version())
-        about.set_license(self.the_license)
-        about.set_website("http://home.gna.org/zynjacku/")
-        about.set_authors(["Nedko Arnaudov"])
-        about.set_artists(["Thorsten Wilms"])
-        about.set_logo(gtk.gdk.pixbuf_new_from_file("%s/logo.png" % self.data_dir))
-        about.show()
-        about.run()
-        about.hide()
+        run_about_dialog(self.main_window, self.program_data)
 
     def on_preset_load(self, widget):
         self.preset_load_ask()
@@ -2703,23 +3002,19 @@ class ZynjackuHostMulti(ZynjackuHost):
         self.plugins_load("LV2 synth plugins")
 
     def on_synth_clear(self, widget):
-        self.store.clear();
+        self.store.clear()
         self.clear_plugins()
 
 class ZynjackuHostOne(ZynjackuHost):
-    def __init__(self, glade_xml, client_name, uri):
+    def __init__(self, program_data, client_name, uri):
         #print "ZynjackuHostOne constructor called."
-        ZynjackuHost.__init__(self, client_name)
+        ZynjackuHost.__init__(self, client_name, "zynjacku")
 
         self.plugin = self.new_plugin(uri)
         if not self.plugin:
             print"Failed to construct %s" % uri
             return
 
-        if not ZynjackuHost.plugin_ui_available(self, self.plugin):
-            print"Synth window not available"
-            return
-
         if not ZynjackuHost.create_plugin_ui(self, self.plugin):
             print"Failed to create synth window"
             return
@@ -2738,6 +3033,7 @@ class ZynjackuHostOne(ZynjackuHost):
 
     def run(self):
         if not self.plugin:
+            self.run_done()
             return
 
         self.plugin.ui_win.show()
@@ -2748,7 +3044,15 @@ class ZynjackuHostOne(ZynjackuHost):
 
         ZynjackuHost.__del__(self)
 
-def file_setup():
+def get_program_data(program_name):
+    program_data = {}
+
+    program_data['name'] = program_name
+    if zynjacku.zynjacku_get_version() == "dev":
+        program_data['comments'] = "(development snapshot)"
+    else:
+        program_data['version'] = zynjacku.zynjacku_get_version()
+
     data_dir = os.path.dirname(sys.argv[0])
 
     # since ppl tend to run "python zynjacku.py", lets assume that it is in current directory
@@ -2761,15 +3065,37 @@ def file_setup():
     if not os.path.isfile(glade_file):
         data_dir = data_dir + os.sep + ".." + os.sep + "share"+ os.sep + "zynjacku"
         glade_file = data_dir + os.sep + "zynjacku.glade"
+        logo_dir = data_dir
+    else:
+        logo_dir = data_dir + os.sep + 'art' + os.sep + 'logo'
 
     #print 'data dir is "%s"' % data_dir
     #print 'Loading glade from "%s"' % glade_file
 
-    the_license = file(data_dir + os.sep + "gpl.txt").read()
+    program_data['license'] = file(data_dir + os.sep + "gpl.txt").read()
+
+    program_data['glade_xml'] = gtk.glade.XML(glade_file)
+
+
+    program_data['website'] = "http://home.gna.org/zynjacku/"
+
+    program_data['authors'] = [
+        "Nedko Arnaudov",
+        "Krzysztof Foltman",
+        "Stefano D'Angelo",
+        ]
+
+    program_data['logo'] = "%s/logo.png" % logo_dir
+    program_data['artists'] = [
+        "Thorsten Wilms",
+        "Lapo Calamandrei",
+        ]
 
-    glade_xml = gtk.glade.XML(glade_file)
+    if program_name != 'zynjacku':
+        del program_data['artists']
+        del program_data['logo']
 
-    return data_dir, glade_xml, the_license
+    return program_data
 
 def register_types():
     gobject.signal_new("zynjacku-parameter-changed", PluginUIUniversalParameter, gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION, gobject.TYPE_NONE, [])
@@ -2781,7 +3107,7 @@ def register_types():
     gobject.type_register(PluginUIUniversalParameterBool)
 
 def main():
-    data_dir, glade_xml, the_license = file_setup()
+    program_data = get_program_data('zynjacku')
 
     register_types()
 
@@ -2801,9 +3127,9 @@ def main():
         print "Successfully connected to LASH server at " +  lash.lash_get_server_name(lash_client)
 
     if len(sys.argv) == 2 and sys.argv[1][-9:] != ".zynjacku":
-        host = ZynjackuHostOne(glade_xml, client_name, sys.argv[1])
+        host = ZynjackuHostOne(program_data, client_name, sys.argv[1])
     else:
-        host = ZynjackuHostMulti(data_dir, glade_xml, client_name, the_license, sys.argv[1:], lash_client)
+        host = ZynjackuHostMulti(program_data, client_name, sys.argv[1:], lash_client)
 
     host.run()
 
diff --git a/zynjacku_ttl.c b/zynjacku_ttl.c
new file mode 100644
index 0000000..e9e82e4
--- /dev/null
+++ b/zynjacku_ttl.c
@@ -0,0 +1,50 @@
+#include <Python.h>
+#include "flex_ttl.h"
+#include "ttl_lexer.h"
+
+//////////////////////////////////////////////////// calfpytools
+
+static PyObject * scan_file(PyObject *self, PyObject *args)
+{
+    char *ttl_name = NULL;
+    PyObject *tmp = NULL;
+    yyscan_t scanner;
+    if (!PyArg_ParseTuple(args, "s:scan_file", &ttl_name))
+        return NULL;
+    
+    tmp = ttl_list = PyList_New(0);
+    yylex_init(&scanner);
+    yyset_in(fopen(ttl_name, "r"), scanner);
+    yylex(scanner);
+    yylex_destroy(scanner);
+    ttl_list = NULL;
+    return tmp;
+}
+
+static PyObject * scan_string(PyObject *self, PyObject *args)
+{
+    char *ttl_text = NULL;
+    PyObject *tmp = NULL;
+    yyscan_t scanner;
+    if (!PyArg_ParseTuple(args, "s:scan_string", &ttl_text))
+        return NULL;
+    
+    tmp = ttl_list = PyList_New(0);
+    yylex_init(&scanner);
+    yyset_in(fmemopen(ttl_text, strlen(ttl_text), "r"), scanner);
+    yylex(scanner);
+    yylex_destroy(scanner);
+    ttl_list = NULL;
+    return tmp;
+}
+
+static PyMethodDef module_methods[] = {
+    {"scan_file", scan_file, METH_VARARGS, "Scan a TTL file, return a list of token tuples"},
+    {"scan_string", scan_string, METH_VARARGS, "Scan a string, return a list of token tuples"},
+    {NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC initzynjacku_ttl()
+{
+  Py_InitModule3("zynjacku_ttl", module_methods, "Flex Turtle parser");
+}
diff --git a/zynjacku_wrap.c b/zynjacku_wrap.c
index 0767bb0..aaa5963 100644
--- a/zynjacku_wrap.c
+++ b/zynjacku_wrap.c
@@ -15,7 +15,11 @@
 #include "enum.h"
 #include "hints.h"
 #include "midi_cc_map.h"
-#line 19 "zynjacku.c"
+
+char *
+zynjacku_lv2_dman_get(
+  const char * dlpath);
+#line 23 "zynjacku.c"
 
 
 /* ---------- types from other modules ---------- */
@@ -31,7 +35,7 @@ PyTypeObject G_GNUC_INTERNAL PyZynjackuMidiCcMap_Type;
 PyTypeObject G_GNUC_INTERNAL PyZynjackuPlugin_Type;
 PyTypeObject G_GNUC_INTERNAL PyZynjackuRack_Type;
 
-#line 35 "zynjacku.c"
+#line 39 "zynjacku.c"
 
 
 
@@ -97,20 +101,50 @@ _wrap_zynjacku_engine_get_midi_activity(PyGObject *self)
 }
 
 static PyObject *
-_wrap_zynjacku_engine_iterate_plugins(PyGObject *self, PyObject *args, PyObject *kwargs)
+_wrap_zynjacku_engine_get_supported_feature(PyGObject *self, PyObject *args, PyObject *kwargs)
 {
-    static char *kwlist[] = { "force", NULL };
-    int force;
+    static char *kwlist[] = { "index", NULL };
+    PyObject *py_index = NULL;
+    const gchar *ret;
+    guint index = 0;
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:ZynjackuEngine.iterate_plugins", kwlist, &force))
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:ZynjackuEngine.get_supported_feature", kwlist, &py_index))
         return NULL;
+    if (py_index) {
+        if (PyLong_Check(py_index))
+            index = PyLong_AsUnsignedLong(py_index);
+        else if (PyInt_Check(py_index))
+            index = PyInt_AsLong(py_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
     
-    zynjacku_engine_iterate_plugins(ZYNJACKU_ENGINE(self->obj), force);
+    ret = zynjacku_engine_get_supported_feature(ZYNJACKU_ENGINE(self->obj), index);
     
+    if (ret)
+        return PyString_FromString(ret);
     Py_INCREF(Py_None);
     return Py_None;
 }
 
+static PyObject *
+_wrap_zynjacku_engine_construct_plugin(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "plugin_obj_ptr", NULL };
+    PyGObject *plugin_obj_ptr;
+    int ret;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:ZynjackuEngine.construct_plugin", kwlist, &PyZynjackuPlugin_Type, &plugin_obj_ptr))
+        return NULL;
+    
+    ret = zynjacku_engine_construct_plugin(ZYNJACKU_ENGINE(self->obj), ZYNJACKU_PLUGIN(plugin_obj_ptr->obj));
+    
+    return PyBool_FromLong(ret);
+
+}
+
 static const PyMethodDef _PyZynjackuEngine_methods[] = {
     { "start_jack", (PyCFunction)_wrap_zynjacku_engine_start_jack, METH_VARARGS|METH_KEYWORDS,
       NULL },
@@ -122,7 +156,9 @@ static const PyMethodDef _PyZynjackuEngine_methods[] = {
       NULL },
     { "get_midi_activity", (PyCFunction)_wrap_zynjacku_engine_get_midi_activity, METH_NOARGS,
       NULL },
-    { "iterate_plugins", (PyCFunction)_wrap_zynjacku_engine_iterate_plugins, METH_VARARGS|METH_KEYWORDS,
+    { "get_supported_feature", (PyCFunction)_wrap_zynjacku_engine_get_supported_feature, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "construct_plugin", (PyCFunction)_wrap_zynjacku_engine_construct_plugin, METH_VARARGS|METH_KEYWORDS,
       NULL },
     { NULL, NULL, 0, NULL }
 };
@@ -633,22 +669,6 @@ PyTypeObject G_GNUC_INTERNAL PyZynjackuMidiCcMap_Type = {
 /* ----------- ZynjackuPlugin ----------- */
 
 static PyObject *
-_wrap_zynjacku_plugin_construct(PyGObject *self, PyObject *args, PyObject *kwargs)
-{
-    static char *kwlist[] = { "engine_obj_ptr", NULL };
-    PyGObject *engine_obj_ptr;
-    int ret;
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:ZynjackuPlugin.construct", kwlist, &PyGObject_Type, &engine_obj_ptr))
-        return NULL;
-    
-    ret = zynjacku_plugin_construct(ZYNJACKU_PLUGIN(self->obj), G_OBJECT(engine_obj_ptr->obj));
-    
-    return PyBool_FromLong(ret);
-
-}
-
-static PyObject *
 _wrap_zynjacku_plugin_destruct(PyGObject *self)
 {
     
@@ -659,30 +679,6 @@ _wrap_zynjacku_plugin_destruct(PyGObject *self)
 }
 
 static PyObject *
-_wrap_zynjacku_plugin_supports_generic_ui(PyGObject *self)
-{
-    int ret;
-
-    
-    ret = zynjacku_plugin_supports_generic_ui(ZYNJACKU_PLUGIN(self->obj));
-    
-    return PyBool_FromLong(ret);
-
-}
-
-static PyObject *
-_wrap_zynjacku_plugin_supports_custom_ui(PyGObject *self)
-{
-    int ret;
-
-    
-    ret = zynjacku_plugin_supports_custom_ui(ZYNJACKU_PLUGIN(self->obj));
-    
-    return PyBool_FromLong(ret);
-
-}
-
-static PyObject *
 _wrap_zynjacku_plugin_get_instance_name(PyGObject *self)
 {
     const gchar *ret;
@@ -725,12 +721,16 @@ _wrap_zynjacku_plugin_get_uri(PyGObject *self)
 }
 
 static PyObject *
-_wrap_zynjacku_plugin_ui_on(PyGObject *self)
+_wrap_zynjacku_plugin_ui_on(PyGObject *self, PyObject *args, PyObject *kwargs)
 {
+    static char *kwlist[] = { "ui_uri", "ui_type_uri", "ui_binary_path", "ui_bundle_path", NULL };
+    char *ui_uri = NULL, *ui_type_uri = NULL, *ui_binary_path = NULL, *ui_bundle_path = NULL;
     int ret;
 
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"|zzzz:ZynjackuPlugin.ui_on", kwlist, &ui_uri, &ui_type_uri, &ui_binary_path, &ui_bundle_path))
+        return NULL;
     
-    ret = zynjacku_plugin_ui_on(ZYNJACKU_PLUGIN(self->obj));
+    ret = zynjacku_plugin_ui_on(ZYNJACKU_PLUGIN(self->obj), ui_uri, ui_type_uri, ui_binary_path, ui_bundle_path);
     
     return PyBool_FromLong(ret);
 
@@ -897,22 +897,201 @@ _wrap_zynjacku_plugin_set_midi_cc_map(PyGObject *self, PyObject *args, PyObject
 
 }
 
+static PyObject *
+_wrap_zynjacku_plugin_add_supported_feature(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "feature_uri", NULL };
+    char *feature_uri;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:ZynjackuPlugin.add_supported_feature", kwlist, &feature_uri))
+        return NULL;
+    
+    zynjacku_plugin_add_supported_feature(ZYNJACKU_PLUGIN(self->obj), feature_uri);
+    
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
+_wrap_zynjacku_plugin_create_oldmidi_input_port(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "port_index", "symbol", NULL };
+    PyObject *py_port_index = NULL;
+    char *symbol;
+    int ret;
+    guint port_index = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Os:ZynjackuPlugin.create_oldmidi_input_port", kwlist, &py_port_index, &symbol))
+        return NULL;
+    if (py_port_index) {
+        if (PyLong_Check(py_port_index))
+            port_index = PyLong_AsUnsignedLong(py_port_index);
+        else if (PyInt_Check(py_port_index))
+            port_index = PyInt_AsLong(py_port_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'port_index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
+    
+    ret = zynjacku_plugin_create_oldmidi_input_port(ZYNJACKU_PLUGIN(self->obj), port_index, symbol);
+    
+    return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_zynjacku_plugin_create_eventmidi_input_port(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "port_index", "symbol", NULL };
+    PyObject *py_port_index = NULL;
+    char *symbol;
+    int ret;
+    guint port_index = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Os:ZynjackuPlugin.create_eventmidi_input_port", kwlist, &py_port_index, &symbol))
+        return NULL;
+    if (py_port_index) {
+        if (PyLong_Check(py_port_index))
+            port_index = PyLong_AsUnsignedLong(py_port_index);
+        else if (PyInt_Check(py_port_index))
+            port_index = PyInt_AsLong(py_port_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'port_index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
+    
+    ret = zynjacku_plugin_create_eventmidi_input_port(ZYNJACKU_PLUGIN(self->obj), port_index, symbol);
+    
+    return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_zynjacku_plugin_create_audio_port(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "port_index", "symbol", "input", NULL };
+    PyObject *py_port_index = NULL;
+    char *symbol;
+    int input, ret;
+    guint port_index = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Osi:ZynjackuPlugin.create_audio_port", kwlist, &py_port_index, &symbol, &input))
+        return NULL;
+    if (py_port_index) {
+        if (PyLong_Check(py_port_index))
+            port_index = PyLong_AsUnsignedLong(py_port_index);
+        else if (PyInt_Check(py_port_index))
+            port_index = PyInt_AsLong(py_port_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'port_index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
+    
+    ret = zynjacku_plugin_create_audio_port(ZYNJACKU_PLUGIN(self->obj), port_index, symbol, input);
+    
+    return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_zynjacku_plugin_create_float_parameter_port(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "port_index", "symbol", "name", "msgcontext", "default_provided", "default_value", "min_provided", "min_value", "max_provided", "max_value", NULL };
+    PyObject *py_port_index = NULL;
+    char *symbol, *name;
+    int msgcontext, default_provided, min_provided, max_provided, ret;
+    guint port_index = 0;
+    double default_value, min_value, max_value;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Ossiididid:ZynjackuPlugin.create_float_parameter_port", kwlist, &py_port_index, &symbol, &name, &msgcontext, &default_provided, &default_value, &min_provided, &min_value, &max_provided, &max_value))
+        return NULL;
+    if (py_port_index) {
+        if (PyLong_Check(py_port_index))
+            port_index = PyLong_AsUnsignedLong(py_port_index);
+        else if (PyInt_Check(py_port_index))
+            port_index = PyInt_AsLong(py_port_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'port_index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
+    
+    ret = zynjacku_plugin_create_float_parameter_port(ZYNJACKU_PLUGIN(self->obj), port_index, symbol, name, msgcontext, default_provided, default_value, min_provided, min_value, max_provided, max_value);
+    
+    return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_zynjacku_plugin_create_float_measure_port(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "port_index", "symbol", "msgcontext", NULL };
+    PyObject *py_port_index = NULL;
+    char *symbol;
+    int msgcontext, ret;
+    guint port_index = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Osi:ZynjackuPlugin.create_float_measure_port", kwlist, &py_port_index, &symbol, &msgcontext))
+        return NULL;
+    if (py_port_index) {
+        if (PyLong_Check(py_port_index))
+            port_index = PyLong_AsUnsignedLong(py_port_index);
+        else if (PyInt_Check(py_port_index))
+            port_index = PyInt_AsLong(py_port_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'port_index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
+    
+    ret = zynjacku_plugin_create_float_measure_port(ZYNJACKU_PLUGIN(self->obj), port_index, symbol, msgcontext);
+    
+    return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_zynjacku_plugin_create_string_parameter_port(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "port_index", "symbol", "name", "msgcontext", "default_value", "maxlen", NULL };
+    PyObject *py_port_index = NULL;
+    char *symbol, *name, *default_value;
+    int msgcontext, ret;
+    guint port_index = 0;
+    gsize maxlen;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Ossisk:ZynjackuPlugin.create_string_parameter_port", kwlist, &py_port_index, &symbol, &name, &msgcontext, &default_value, &maxlen))
+        return NULL;
+    if (py_port_index) {
+        if (PyLong_Check(py_port_index))
+            port_index = PyLong_AsUnsignedLong(py_port_index);
+        else if (PyInt_Check(py_port_index))
+            port_index = PyInt_AsLong(py_port_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'port_index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
+    
+    ret = zynjacku_plugin_create_string_parameter_port(ZYNJACKU_PLUGIN(self->obj), port_index, symbol, name, msgcontext, default_value, maxlen);
+    
+    return PyBool_FromLong(ret);
+
+}
+
 static const PyMethodDef _PyZynjackuPlugin_methods[] = {
-    { "construct", (PyCFunction)_wrap_zynjacku_plugin_construct, METH_VARARGS|METH_KEYWORDS,
-      NULL },
     { "destruct", (PyCFunction)_wrap_zynjacku_plugin_destruct, METH_NOARGS,
       NULL },
-    { "supports_generic_ui", (PyCFunction)_wrap_zynjacku_plugin_supports_generic_ui, METH_NOARGS,
-      NULL },
-    { "supports_custom_ui", (PyCFunction)_wrap_zynjacku_plugin_supports_custom_ui, METH_NOARGS,
-      NULL },
     { "get_instance_name", (PyCFunction)_wrap_zynjacku_plugin_get_instance_name, METH_NOARGS,
       NULL },
     { "get_name", (PyCFunction)_wrap_zynjacku_plugin_get_name, METH_NOARGS,
       NULL },
     { "get_uri", (PyCFunction)_wrap_zynjacku_plugin_get_uri, METH_NOARGS,
       NULL },
-    { "ui_on", (PyCFunction)_wrap_zynjacku_plugin_ui_on, METH_NOARGS,
+    { "ui_on", (PyCFunction)_wrap_zynjacku_plugin_ui_on, METH_VARARGS|METH_KEYWORDS,
       NULL },
     { "ui_off", (PyCFunction)_wrap_zynjacku_plugin_ui_off, METH_NOARGS,
       NULL },
@@ -932,6 +1111,20 @@ static const PyMethodDef _PyZynjackuPlugin_methods[] = {
       NULL },
     { "set_midi_cc_map", (PyCFunction)_wrap_zynjacku_plugin_set_midi_cc_map, METH_VARARGS|METH_KEYWORDS,
       NULL },
+    { "add_supported_feature", (PyCFunction)_wrap_zynjacku_plugin_add_supported_feature, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "create_oldmidi_input_port", (PyCFunction)_wrap_zynjacku_plugin_create_oldmidi_input_port, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "create_eventmidi_input_port", (PyCFunction)_wrap_zynjacku_plugin_create_eventmidi_input_port, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "create_audio_port", (PyCFunction)_wrap_zynjacku_plugin_create_audio_port, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "create_float_parameter_port", (PyCFunction)_wrap_zynjacku_plugin_create_float_parameter_port, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "create_float_measure_port", (PyCFunction)_wrap_zynjacku_plugin_create_float_measure_port, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "create_string_parameter_port", (PyCFunction)_wrap_zynjacku_plugin_create_string_parameter_port, METH_VARARGS|METH_KEYWORDS,
+      NULL },
     { NULL, NULL, 0, NULL }
 };
 
@@ -1032,20 +1225,50 @@ _wrap_zynjacku_rack_ui_run(PyGObject *self)
 }
 
 static PyObject *
-_wrap_zynjacku_rack_iterate_plugins(PyGObject *self, PyObject *args, PyObject *kwargs)
+_wrap_zynjacku_rack_get_supported_feature(PyGObject *self, PyObject *args, PyObject *kwargs)
 {
-    static char *kwlist[] = { "force", NULL };
-    int force;
+    static char *kwlist[] = { "index", NULL };
+    PyObject *py_index = NULL;
+    const gchar *ret;
+    guint index = 0;
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:ZynjackuRack.iterate_plugins", kwlist, &force))
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:ZynjackuRack.get_supported_feature", kwlist, &py_index))
         return NULL;
+    if (py_index) {
+        if (PyLong_Check(py_index))
+            index = PyLong_AsUnsignedLong(py_index);
+        else if (PyInt_Check(py_index))
+            index = PyInt_AsLong(py_index);
+        else
+            PyErr_SetString(PyExc_TypeError, "Parameter 'index' must be an int or a long");
+        if (PyErr_Occurred())
+            return NULL;
+    }
     
-    zynjacku_rack_iterate_plugins(ZYNJACKU_RACK(self->obj), force);
+    ret = zynjacku_rack_get_supported_feature(ZYNJACKU_RACK(self->obj), index);
     
+    if (ret)
+        return PyString_FromString(ret);
     Py_INCREF(Py_None);
     return Py_None;
 }
 
+static PyObject *
+_wrap_zynjacku_rack_construct_plugin(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "plugin_obj_ptr", NULL };
+    PyGObject *plugin_obj_ptr;
+    int ret;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:ZynjackuRack.construct_plugin", kwlist, &PyZynjackuPlugin_Type, &plugin_obj_ptr))
+        return NULL;
+    
+    ret = zynjacku_rack_construct_plugin(ZYNJACKU_RACK(self->obj), ZYNJACKU_PLUGIN(plugin_obj_ptr->obj));
+    
+    return PyBool_FromLong(ret);
+
+}
+
 static const PyMethodDef _PyZynjackuRack_methods[] = {
     { "start_jack", (PyCFunction)_wrap_zynjacku_rack_start_jack, METH_VARARGS|METH_KEYWORDS,
       NULL },
@@ -1055,7 +1278,9 @@ static const PyMethodDef _PyZynjackuRack_methods[] = {
       NULL },
     { "ui_run", (PyCFunction)_wrap_zynjacku_rack_ui_run, METH_NOARGS,
       NULL },
-    { "iterate_plugins", (PyCFunction)_wrap_zynjacku_rack_iterate_plugins, METH_VARARGS|METH_KEYWORDS,
+    { "get_supported_feature", (PyCFunction)_wrap_zynjacku_rack_get_supported_feature, METH_VARARGS|METH_KEYWORDS,
+      NULL },
+    { "construct_plugin", (PyCFunction)_wrap_zynjacku_rack_construct_plugin, METH_VARARGS|METH_KEYWORDS,
       NULL },
     { NULL, NULL, 0, NULL }
 };
@@ -1137,11 +1362,34 @@ _wrap_zynjacku_rack_get_version(PyObject *self)
     return Py_None;
 }
 
+static PyObject *
+_wrap_zynjacku_lv2_dman_get(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = { "dlpath", NULL };
+    char *dlpath;
+    gchar *ret;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:zynjacku_lv2_dman_get", kwlist, &dlpath))
+        return NULL;
+    
+    ret = zynjacku_lv2_dman_get(dlpath);
+    
+    if (ret) {
+        PyObject *py_ret = PyString_FromString(ret);
+        g_free(ret);
+        return py_ret;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
 const PyMethodDef zynjacku_c_functions[] = {
     { "zynjacku_get_version", (PyCFunction)_wrap_zynjacku_get_version, METH_NOARGS,
       NULL },
     { "zynjacku_rack_get_version", (PyCFunction)_wrap_zynjacku_rack_get_version, METH_NOARGS,
       NULL },
+    { "zynjacku_lv2_dman_get", (PyCFunction)_wrap_zynjacku_lv2_dman_get, METH_VARARGS|METH_KEYWORDS,
+      NULL },
     { NULL, NULL, 0, NULL }
 };
 
@@ -1165,7 +1413,7 @@ zynjacku_c_register_classes(PyObject *d)
     }
 
 
-#line 1169 "zynjacku.c"
+#line 1417 "zynjacku.c"
     pygobject_register_class(d, "ZynjackuEngine", ZYNJACKU_TYPE_ENGINE, &PyZynjackuEngine_Type, Py_BuildValue("(O)", &PyGObject_Type));
     pyg_set_object_has_new_constructor(ZYNJACKU_TYPE_ENGINE);
     pygobject_register_class(d, "ZynjackuEnum", ZYNJACKU_TYPE_ENUM, &PyZynjackuEnum_Type, Py_BuildValue("(O)", &PyGObject_Type));
diff --git a/zynspect.py b/zynspect.py
new file mode 100755
index 0000000..e8b9972
--- /dev/null
+++ b/zynspect.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+import os
+import sys
+from distutils import sysconfig
+
+old_path = sys.path
+
+inplace_libs = os.path.join(os.path.dirname(sys.argv[0]), ".libs")
+if os.access(inplace_libs, os.R_OK):
+    sys.path.append(inplace_libs)
+
+try:
+    from zynworld import lv2
+except Exception, e:
+    print "Failed to import zynjacku internal python modules"
+    print repr(e)
+    print "These directories were searched"
+    for path in sys.path:
+        print "    " + path
+    sys.exit(1)
+
+sys.path = old_path
+
+def show_plugin_info(plugin):
+    print "Plugin: %s" % plugin.name
+    print "URI: %s" % plugin.uri
+    if plugin.microname != None:
+        print "Tiny name: %s" % plugin.microname
+    if plugin.maintainers:
+        print "Maintainers: %s" % plugin.maintainers
+    print "License: %s" % plugin.license
+    print "Classes: %s" % plugin.classes
+    print "Required features: %s" % list(plugin.requiredFeatures)
+    print "Optional features: %s" % list(plugin.optionalFeatures)
+    print "Binary: " + plugin.binary
+
+    print
+    if plugin.presets:
+        print "Presets:"
+        for preset in plugin.presets:
+            print "    URI:  " + preset
+            info = db.get_preset_info(plugin.uri, preset)
+            #print "    " + info.name
+            print "    Name: " + info.name
+            print "    Port values:"
+            for port in info.ports:
+                print "        %s: %s" % (port.symbol, port.value)
+            print
+    else:
+        print "No presets"
+    print
+
+    print "Ports:"
+    types = ["Audio", "Control", "Event", "Input", "Output", "String", "LarslMidi"]
+    for port in plugin.ports:
+        extra = []
+        for type in types:
+            if port.__dict__["is" + type]:
+                extra.append(type)
+        for sp in ["defaultValue", "minimum", "maximum", "microname"]:
+            if port.__dict__[sp] != None:
+                extra.append("%s=%s" % (sp, repr(port.__dict__[sp])))
+        if len(port.events):
+            s = list()
+            for evt in port.events:
+                if evt in lv2.event_type_names:
+                    s.append(lv2.event_type_names[evt])
+                else:
+                    s.append(evt)
+            extra.append("events=%s" % ",".join(s))
+        if len(port.properties):
+            s = list()
+            for prop in port.properties:
+                if prop in lv2.port_property_names:
+                    s.append(lv2.port_property_names[prop])
+                else:
+                    s.append(prop)
+            extra.append("properties=%s" % ",".join(s))
+        if len(port.contexts):
+            s = list()
+            for context in port.contexts:
+                if context in lv2.context_names:
+                    s.append(lv2.context_names[context])
+                else:
+                    s.append(context)
+            extra.append("contexts=%s" % ",".join(s))
+        print "%4s %-20s %-40s %s" % (port.index, port.symbol, port.name, ", ".join(extra))
+        splist = port.scalePoints
+        splist.sort(lambda x, y: cmp(x[1], y[1]))
+        if len(splist):
+            for sp in splist:
+                print "       Scale point %s: %s" % (sp[1], sp[0])
+        #print port
+    print
+
+    if plugin.ui:
+        print "UI bundles:"
+        for ui_uri in plugin.ui:
+            print "    " + ui_uri
+            ui = db.get_ui_info(plugin.uri, ui_uri)
+            print "        Type: " + ui.type
+            print "        Binary: " + ui.binary
+            print "        Required features: " + repr(ui.requiredFeatures)
+            print "        Optional features: " + repr(ui.optionalFeatures)
+        print
+    print
+
+    print "Sources:"
+    for source in plugin.sources:
+        print "    " + source
+    print
+
+def list_plugins(verbose):
+    plugins = db.getPluginList()
+
+    for uri in plugins:
+        if verbose:
+            plugin = db.getPluginInfo(uri)
+            if plugin == None:
+                continue
+            show_plugin_info(plugin)
+        else:
+            print uri
+
+db = lv2.LV2DB()
+
+if len(sys.argv) >= 2:
+    if sys.argv[1] == "dump":
+        list_plugins(True)
+    else:
+        uri = sys.argv[1]
+
+        plugin = db.getPluginInfo(uri)
+
+        if plugin == None:
+            print 'Plugin URI "%s" is unknown' % uri
+            sys.exit(1)
+
+        show_plugin_info(plugin)
+else:
+    list_plugins(False)
diff --git a/zynworld/__init__.py b/zynworld/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/zynworld/lv2.py b/zynworld/lv2.py
new file mode 100644
index 0000000..1d40866
--- /dev/null
+++ b/zynworld/lv2.py
@@ -0,0 +1,595 @@
+import re
+import os
+import sys
+import glob
+import zynjacku_ttl
+import zynjacku_c
+
+lv2 = "http://lv2plug.in/ns/lv2core#"
+lv2evt = "http://lv2plug.in/ns/ext/event#"
+lv2str = "http://lv2plug.in/ns/dev/string-port#"
+lv2ctx = "http://lv2plug.in/ns/dev/contexts#"
+rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+rdfs = "http://www.w3.org/2000/01/rdf-schema#"
+epi = "http://lv2plug.in/ns/dev/extportinfo#"
+rdf_type = rdf + "type"
+rdfs_see_also = rdfs + "seeAlso"
+rdfs_label = rdfs + "label"
+rdfs_subclass_of = rdfs + "subClassOf"
+tinyname_uri = "http://lv2plug.in/ns/dev/tiny-name"
+foaf = "http://xmlns.com/foaf/0.1/"
+doap = "http://usefulinc.com/ns/doap#"
+lv2ui = "http://lv2plug.in/ns/extensions/ui#"
+lv2ui_ui = lv2ui + "ui"
+lv2ui_binary = lv2ui + "binary"
+lv2preset = "http://lv2plug.in/ns/dev/presets#"
+lv2preset_preset = lv2preset + "Preset"
+lv2preset_appliesTo = lv2preset + "appliesTo"
+lv2preset_hasPreset = lv2preset + "hasPreset"
+lv2preset_value = lv2preset + "value"
+dc = "http://dublincore.org/documents/dcmi-namespace/"
+dc_title = dc + "title"
+dman = "http://naspro.atheme.org/rdf/old-dman#"
+
+event_type_names = {
+    "http://lv2plug.in/ns/ext/midi#MidiEvent" : "MIDI"
+}
+
+port_property_names = {
+    "http://lv2plug.in/ns/lv2core#reportsLatency": "reportsLatency",
+    "http://lv2plug.in/ns/lv2core#toggled": "toggled",
+    "http://lv2plug.in/ns/lv2core#integer": "integer",
+    "http://lv2plug.in/ns/lv2core#connectionOptional": "connectionOptional",
+    "http://lv2plug.in/ns/lv2core#sampleRate": "sampleRate",
+    "http://lv2plug.in/ns/dev/extportinfo#hasStrictBounds": "hasStrictBounds",
+    "http://lv2plug.in/ns/dev/extportinfo#logarithmic": "logarithmic",
+    "http://lv2plug.in/ns/dev/extportinfo#notAutomatic": "notAutomatic",
+    "http://lv2plug.in/ns/dev/extportinfo#trigger": "trigger",
+    "http://lv2plug.in/ns/dev/extportinfo#outputGain": "outputGain",
+    "http://lv2plug.in/ns/dev/extportinfo#reportsBpm": "reportsBpm",
+}
+
+context_names = {
+    "http://lv2plug.in/ns/dev/contexts#MessageContext": "MessageContext",
+}
+
+def uniq_seq(seq):
+    return {}.fromkeys(seq).keys()
+
+class DumpRDFModel:
+    def addTriple(self, s, p, o):
+        print "%s [%s] %s" % (s, p, repr(o))
+
+class SimpleRDFModel:
+    def __init__(self):
+        self.bySubject = {}
+        self.byPredicate = {}
+        #self.byObject = {}
+        self.byClass = {}
+        self.object_sources = {}
+        self.sources = set()
+        self.len = 0
+
+    def size(self):
+        return self.len
+
+    def getByType(self, classname):
+        if classname in self.byClass:
+            return self.byClass[classname]
+        return []
+    def getByPropType(self, propname):
+        if propname in self.byPredicate:
+            return self.byPredicate[propname]
+        return []
+    def getProperty(self, subject, props, optional = False, single = False):
+        #print "getProperty(%s, %s)" % (repr(subject), repr(props))
+        if type(props) is list:
+            prop = props[0]
+        else:
+            prop = props
+        if type(subject) is str or type(subject) is unicode:
+            if not self.bySubject.has_key(subject):
+                return None
+            subject = self.bySubject[subject]
+        elif type(subject) is dict:
+            pass
+        else:
+            #print "subject type is %s" % type(subject)
+            if single:
+                return None
+            else:
+                return []
+        anyprops = set()
+        if prop in subject:
+            for o in subject[prop]:
+                anyprops.add(o)
+        if type(props) is list:
+            if len(props) > 1:
+                result = set()
+                for v in anyprops:
+                    if single:
+                        value = self.getProperty(v, props[1:], optional = optional, single = True)
+                        if value != None:
+                            return value
+                    else:
+                        result |= set(self.getProperty(v, props[1:], optional = optional, single = False))
+                if single:
+                    return None
+                else:
+                    return list(result)
+        if single:
+            if len(anyprops) > 0:
+                if len(anyprops) > 1:
+                    raise Exception, "More than one value of " + prop
+                return list(anyprops)[0]
+            else:
+                return None
+        return list(anyprops)
+
+    def add_object_source(self, uri, source):
+        if o not in self.object_sources:
+            self.object_sources[o] = set((source,))
+        else:
+            self.object_sources[o].add(source)
+
+    def addTriple(self, s, p, o, source=None):
+        self.len += 1
+
+        #if p == lv2 + "binary":
+        #    print 'binary "%s" of %s found' % (o, s)
+        if o not in self.object_sources:
+            self.object_sources[o] = set((source,))
+        else:
+            self.object_sources[o].add(source)
+        if p == rdf_type:
+            p = "a"
+        #if p == 'a' and o == lv2preset_preset:
+        #    print 'preset "%s" found' % s
+
+        if s in self.bySubject:
+            predicates = self.bySubject[s]
+            if p in predicates:
+                predicates[p].append(o)
+            else:
+                predicates[p] = [o]
+        else:
+            self.bySubject[s] = { p : [o] }
+
+        if p in self.byPredicate:
+            subjects = self.byPredicate[p]
+            if s in subjects:
+                subjects[s].append(o)
+            else:
+                subjects[s] = [o]
+        else:
+            self.byPredicate[p] = { s : [o] }
+
+        #if o not in self.byObject:
+        #    self.byObject[o] = {}
+        #if p not in self.byObject[o]:
+        #    self.byObject[o][p] = []
+        #self.byObject[o][p].append(s)
+
+        if p == "a":
+            if s not in self.object_sources:
+                self.object_sources[s] = set()
+            self.object_sources[s].add(source)
+            if o in self.byClass:
+                self.byClass[o].append(s)
+            else:
+                self.byClass[o] = [s]
+    def copyFrom(self, src):
+        #print " *** RDF Model Copy *** ",
+        self.bySubject = {}
+        self.byPredicate = {}
+        self.object_sources = {}
+        self.byClass = {}
+        for s, src_s in src.bySubject.iteritems():
+            dst_s = self.bySubject[s] = {}
+            for p, plist in src_s.iteritems():
+                dst_s[p] = list(plist)
+        for p, src_p in src.byPredicate.iteritems():
+            dst_p = self.byPredicate[p] = {}
+            for s, slist in src_p.iteritems():
+                dst_p[s] = list(slist)
+        for o, val in src.object_sources.iteritems():
+            self.object_sources[o] = set(val)
+        for c, val in src.byClass.iteritems():
+            self.byClass[c] = list(set(val))
+        self.len = src.len
+    def dump(self):
+        for s in self.bySubject.keys():
+            for p in self.bySubject[s].keys():
+                print "%s %s %s" % (s, p, self.bySubject[s][p])
+
+def parseTTL(uri, content, model, debug):
+    #print " *** Parse TTL *** ",
+    # Missing stuff: translated literals, blank nodes
+    if debug:
+        print "Parsing: %s" % uri
+    prefixes = {}
+    spo_stack = []
+    spo = ["", "", ""]
+    item = 0
+    anoncnt = 1
+    for x in zynjacku_ttl.scan_string(content):
+        if x[0] == '':
+            continue
+        if x[0] == "URI_": x = ('URI', x[1][1:-1])
+        if x[0] == "float": x = ('number', float(x[1]))
+        if x[0] == 'prefix':
+            spo[0] = "@prefix"
+            item = 1
+            continue
+        elif (x[0] == '.' and spo_stack == []) or x[0] == ';' or x[0] == ',':
+            if item == 3:
+                if spo[0] == "@prefix":
+                    prefixes[spo[1][:-1]] = spo[2]
+                else:
+                    model.addTriple(spo[0], spo[1], spo[2], uri)
+                if x[0] == '.': item = 0
+                elif x[0] == ';': item = 1
+                elif x[0] == ',': item = 2
+            else:
+                if x[0] == '.':
+                    item = 0
+                elif item != 0:
+                    raise Exception, uri+": Unexpected " + x[0]
+        elif x[0] == "prnot" and item < 3:
+            prnot = x[1].split(":")
+            if item != 0 and spo[0] == "@prefix":
+                spo[item] = x[1]
+            elif prnot[0] == "_":
+                spo[item] = uri + "#" + prnot[1]
+            else:
+                if prnot[0] not in prefixes:
+                    print 'WARNING %s: Prefix %s not defined. Ignoring %s:%s' % (uri, prnot[0], prnot[0], prnot[1])
+                else:
+                    spo[item] = prefixes[prnot[0]] + prnot[1]
+            item += 1
+        elif (x[0] == 'URI' or x[0] == "string" or x[0] == "number" or (x[0] == "symbol" and x[1] == "a" and item == 1)) and (item < 3):
+            if x[0] == "URI" and x[1] == "":
+                x = ("URI", uri)
+            elif x[0] == "URI" and x[1].find(":") == -1 and x[1] != "" and x[1][0] != "/":
+                # This is quite silly
+                x = ("URI", os.path.dirname(uri) + "/" + x[1])
+            spo[item] = x[1]
+            item += 1
+        elif x[0] == '[':
+            if item != 2:
+                raise Exception, "Incorrect use of ["
+            uri2 = uri + "$anon$" + str(anoncnt)
+            spo[2] = uri2
+            spo_stack.append(spo)
+            spo = [uri2, "", ""]
+            item = 1
+            anoncnt += 1
+        elif x[0] == ']' or x[0] == ')':
+            if item == 3:
+                model.addTriple(spo[0], spo[1], spo[2], uri)
+                item = 0
+            spo = spo_stack[-1]
+            spo_stack = spo_stack[:-1]
+            item = 3
+        elif x[0] == '(':
+            if item != 2:
+                raise Exception, "Incorrect use of ("
+            uri2 = uri + "$anon$" + str(anoncnt)
+            spo[2] = uri2
+            spo_stack.append(spo)
+            spo = [uri2, "", ""]
+            item = 2
+            anoncnt += 1
+        else:
+            print uri + ": Unexpected: " + repr(x)
+
+class LV2Port(object):
+    def __init__(self):
+        pass
+    def connectableTo(self, port):
+        if not ((self.isInput and port.isOutput) or (self.isOutput and port.isInput)):
+            return False
+        if self.isAudio != port.isAudio or self.isControl != port.isControl or self.isEvent != port.isEvent:
+            return False
+        if not self.isAudio and not self.isControl and not self.isEvent:
+            return False
+        return True
+
+class LV2Plugin(object):
+    def __init__(self):
+        pass
+        
+class LV2UI(object):
+    def __init__(self):
+        pass
+        
+class LV2Preset(object):
+    def __init__(self):
+        pass
+        
+class LV2DB:
+    def __init__(self, sources=[], debug = False):
+        self.debug = debug
+        self.sources = sources
+        self.initManifests()
+        
+    def initManifests(self):
+        if os.environ.has_key("LV2_PATH"):
+            lv2path = os.environ["LV2_PATH"].split(':')
+        else:
+            lv2path = []
+
+            if os.environ.has_key("HOME"):
+                if sys.platform == "darwin":
+                    lv2path.append(os.environ["HOME"] + "/Library/Audio/Plug-Ins/LV2")
+                else:
+                    lv2path.append(os.environ["HOME"] + "/.lv2")
+
+            if sys.platform == "darwin":
+                lv2path.append("/Library/Audio/Plug-Ins/LV2")
+
+            lv2path += ["/usr/local/lib/lv2", "/usr/lib/lv2"]
+
+            print "LV2_PATH not set, defaulting to %s" % repr(lv2path)
+
+        self.manifests = SimpleRDFModel()
+        self.dynmanifests = []
+        self.paths = {}
+        self.plugin_info = dict()
+
+        if not self.sources:
+            # Scan manifests
+            for dir in lv2path:
+                for bundle in glob.iglob(dir + "/*.lv2"):
+                    fn = bundle+"/manifest.ttl"
+                    if os.path.exists(fn):
+                        parseTTL(fn, file(fn).read(), self.manifests, self.debug)
+            # Read all specifications from all manifests
+            if lv2 + "Specification" in self.manifests.byClass:
+                specs = self.manifests.getByType(lv2 + "Specification")
+                filenames = set()
+                for spec in specs:
+                    subj = self.manifests.bySubject[spec]
+                    if rdfs_see_also in subj:
+                        for fn in subj[rdfs_see_also]:
+                            filenames.add(fn)
+                for fn in filenames:
+                    parseTTL(fn, file(fn).read(), self.manifests, self.debug)
+            #print "%u triples in global world" % self.manifests.size()
+        else:
+            for source in self.sources:
+                parseTTL(source, file(source).read(), self.manifests, self.debug)
+            #fn = "/usr/lib/lv2/lv2core.lv2/lv2.ttl"
+            #parseTTL(fn, file(fn).read(), self.manifests, self.debug)
+
+        self.plugins = set(self.manifests.getByType(lv2 + "Plugin"))
+
+        # Read dynamic manifests
+        wrappers = self.manifests.getByType(dman + "DynManifest")
+        for w in wrappers:
+            subj = self.manifests.bySubject[w]
+            if lv2 + "binary" in subj:
+                #print " *** Parse dynamic TTL *** ",
+                manifest = SimpleRDFModel()
+                filename = subj[lv2 + "binary"][0]
+                data = zynjacku_c.zynjacku_lv2_dman_get(filename)
+                #print data
+                parseTTL(filename, data, manifest, self.debug)
+                #print "%u triples in %s dynmanifest world" % (manifest.size(), filename)
+                # add wrapper filename to list of sources so it gets cached
+                for source in self.manifests.object_sources[w]:
+                    #print "adding wrapper ttl " + source
+                    manifest.sources.add(source)
+                self.dynmanifests.append(manifest)
+                for plugin in manifest.getByType(lv2 + "Plugin"):
+                    self.plugins.add(plugin)
+
+        self.categories = set()
+        self.category_paths = []
+        self.add_category_recursive([], lv2 + "Plugin")
+        
+    def add_category_recursive(self, tree_pos, category):
+        cat_name = self.manifests.getProperty(category, rdfs_label, single = True, optional = True)
+        if not cat_name:
+            return
+        self.category_paths.append(((tree_pos + [cat_name])[1:], category))
+        self.categories.add(category)
+        items = self.manifests.byPredicate[rdfs_subclass_of]
+        for subj in items:
+            if subj in self.categories:
+                continue
+            for o in items[subj]:
+                if o == category and subj not in self.categories:
+                    self.add_category_recursive(list(tree_pos) + [cat_name], subj)
+        
+    def get_categories(self):
+        return self.category_paths
+        
+    def getPluginList(self):
+        return self.plugins
+
+    def get_plugin_full_model(self, uri):
+        sources = []
+        model = None
+
+        if uri in self.plugin_info:
+            model = self.plugin_info[uri]
+            return model, sources
+
+        if self.manifests.bySubject.has_key(uri):
+            if self.sources: # cache/subset preloaded
+                self.plugin_info[uri] = self.manifests
+                model = self.manifests
+                return model, self.sources
+
+            world = SimpleRDFModel()
+            world.copyFrom(self.manifests)
+            #msg = "#%u#" % world.size()
+            #print msg,
+            seeAlso = self.manifests.bySubject[uri][rdfs_see_also]
+            try:
+                for doc in seeAlso:
+                    #print "Loading " + doc + " for plugin " + uri
+                    parseTTL(doc, file(doc).read(), world, self.debug)
+                    #msg = "#%u#" % world.size()
+                    #print msg,
+                    world.sources.add(doc)
+                self.plugin_info[uri] = world
+            except Exception, e:
+                print "ERROR %s: %s" % (uri, str(e))
+                return None
+            for source in self.manifests.object_sources[uri]:
+                world.sources.add(source)
+            sources = world.sources
+            #print "%u triples in %s world" % (world.size(), uri)
+            return world, sources
+
+        for model in self.dynmanifests:
+            if model.bySubject.has_key(uri):
+                self.plugin_info[uri] = model
+                #print model.sources
+                return model, model.sources
+
+        #print 'no subject "%s"' % uri
+        return None
+
+    def getPluginInfo(self, uri):
+        #print "getting info for plugin " + uri
+
+        info, sources = self.get_plugin_full_model(uri)
+        if not info:
+            return None
+
+        dest = LV2Plugin()
+        dest.uri = uri
+
+        dest.binary = info.getProperty(uri, lv2 + "binary", optional = True)
+        if not dest.binary:
+            #print "No binary"
+            return None
+        dest.binary = dest.binary[0]
+
+        dest.name = info.getProperty(uri, doap + 'name', optional = True)
+        if not dest.name:
+            return None
+        dest.name = dest.name[0]
+
+        dest.license = info.bySubject[uri][doap + 'license'][0]
+        dest.classes = info.bySubject[uri]["a"]
+        dest.requiredFeatures = info.getProperty(uri, lv2 + "requiredFeature", optional = True)
+        dest.optionalFeatures = info.getProperty(uri, lv2 + "optionalFeature", optional = True)
+        dest.microname = info.getProperty(uri, tinyname_uri, optional = True)
+        if len(dest.microname):
+            dest.microname = dest.microname[0]
+        else:
+            dest.microname = None
+        dest.maintainers = []
+        if info.bySubject[uri].has_key(doap + "maintainer"):
+            for maintainer in info.bySubject[uri][doap + "maintainer"]:
+                maintainersubj = info.bySubject[maintainer]
+                maintainerdict = {}
+                maintainerdict['name'] = info.getProperty(maintainersubj, foaf + "name")[0]
+                homepages = info.getProperty(maintainersubj, foaf + "homepage")
+                if homepages:
+                    maintainerdict['homepage'] = homepages[0]
+                mboxes = info.getProperty(maintainersubj, foaf + "mbox")
+                if mboxes:
+                    maintainerdict['mbox'] = mboxes[0]
+                dest.maintainers.append(maintainerdict)
+
+        ports = []
+        portDict = {}
+        porttypes = {
+            "isAudio" : lv2 + "AudioPort",
+            "isControl" : lv2 + "ControlPort",
+            "isEvent" : lv2evt + "EventPort",
+            "isString" : lv2str + "StringPort",
+            "isInput" : lv2 + "InputPort",
+            "isOutput" : lv2 + "OutputPort",
+            "isLarslMidi" : "http://ll-plugins.nongnu.org/lv2/ext/MidiPort",
+        }
+        
+        for port in info.bySubject[uri][lv2 + "port"]:
+            psubj = info.bySubject[port]
+            pdata = LV2Port()
+            pdata.uri = port
+            pdata.index = int(info.getProperty(psubj, lv2 + "index")[0])
+            pdata.symbol = info.getProperty(psubj, lv2 + "symbol")[0]
+            pdata.name = info.getProperty(psubj, lv2 + "name")[0]
+            classes = set(info.getProperty(psubj, "a"))
+            pdata.classes = classes
+            for pt in porttypes.keys():
+                pdata.__dict__[pt] = porttypes[pt] in classes
+            sp = info.getProperty(psubj, lv2 + "scalePoint")
+            if sp and len(sp):
+                splist = []
+                for pt in sp:
+                    name = info.getProperty(pt, rdfs_label, optional = True, single = True)
+                    if name != None:
+                        value = info.getProperty(pt, rdf + "value", optional = True, single = True)
+                        if value != None:
+                            splist.append((name, value))
+                pdata.scalePoints = splist
+            else:
+                pdata.scalePoints = []
+            if pdata.isControl:
+                pdata.defaultValue = info.getProperty(psubj, [lv2 + "default"], optional = True, single = True)
+            elif pdata.isString:
+                pdata.defaultValue = info.getProperty(psubj, [lv2str + "default"], optional = True, single = True)
+            else:
+                pdata.defaultValue = None
+            pdata.minimum = info.getProperty(psubj, [lv2 + "minimum"], optional = True, single = True)
+            pdata.maximum = info.getProperty(psubj, [lv2 + "maximum"], optional = True, single = True)
+            pdata.microname = info.getProperty(psubj, [tinyname_uri], optional = True, single = True)
+            pdata.properties = set(info.getProperty(psubj, [lv2 + "portProperty"], optional = True))
+            pdata.events = set(info.getProperty(psubj, [lv2evt + "supportsEvent"], optional = True))
+            pdata.contexts = set(info.getProperty(psubj, [lv2ctx + "context"], optional = True))
+            ports.append(pdata)
+            portDict[pdata.uri] = pdata
+        ports.sort(lambda x, y: cmp(x.index, y.index))
+        dest.ports = ports
+        dest.portDict = portDict
+
+        if info.bySubject[uri].has_key(lv2ui_ui):
+            dest.ui = uniq_seq(info.bySubject[uri][lv2ui_ui])
+        else:
+            dest.ui = []
+
+        if info.bySubject[uri].has_key(lv2preset_hasPreset):
+            dest.presets = info.bySubject[uri][lv2preset_hasPreset]
+        else:
+            dest.presets = []
+
+        dest.sources = sources
+
+        return dest
+
+    def get_ui_info(self, plugin_uri, uri):
+        info = self.plugin_info[plugin_uri]
+
+        dest = LV2Plugin()
+        dest.uri = uri
+        dest.type = set(info.getProperty(uri, "a")).intersection(set([lv2ui + 'GtkUI', lv2ui + 'external'])).pop()
+        dest.binary = info.getProperty(uri, lv2ui_binary)[0]
+        dest.requiredFeatures = info.getProperty(uri, lv2ui + "requiredFeature", optional = True)
+        dest.optionalFeatures = info.getProperty(uri, lv2ui + "optionalFeature", optional = True)
+
+        return dest
+
+    def get_preset_info(self, plugin_uri, uri):
+        info = self.plugin_info[plugin_uri]
+
+        dest = LV2Preset()
+        dest.uri = uri
+        dest.name = info.getProperty(uri, dc_title)[0]
+
+        dest.ports = []
+        for port in info.bySubject[uri][lv2 + "port"]:
+            psubj = info.bySubject[port]
+            pdata = LV2Port()
+            pdata.uri = port
+            pdata.symbol = info.getProperty(psubj, lv2 + "symbol")[0]
+            pdata.value = info.getProperty(psubj, lv2preset_value)[0]
+            dest.ports.append(pdata)
+
+        return dest

-- 
zynjacku packaging



More information about the pkg-multimedia-commits mailing list