aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS7
-rw-r--r--COPYING699
-rw-r--r--INSTALL59
-rw-r--r--NEWS14
-rw-r--r--README13
-rw-r--r--blop.lv2/adsr.ttl82
-rw-r--r--blop.lv2/adsr_gt.ttl91
-rw-r--r--blop.lv2/amp.ttl49
-rw-r--r--blop.lv2/branch.ttl54
-rw-r--r--blop.lv2/dahdsr.ttl128
-rw-r--r--blop.lv2/difference.ttl51
-rw-r--r--blop.lv2/fmod.ttl60
-rw-r--r--blop.lv2/interpolator.ttl32
-rw-r--r--blop.lv2/lp4pole.ttl64
-rw-r--r--blop.lv2/manifest.ttl.in155
-rw-r--r--blop.lv2/product.ttl50
-rw-r--r--blop.lv2/pulse.ttl60
-rw-r--r--blop.lv2/quantiser_100.ttl719
-rw-r--r--blop.lv2/quantiser_20.ttl239
-rw-r--r--blop.lv2/quantiser_50.ttl419
-rw-r--r--blop.lv2/random.ttl64
-rw-r--r--blop.lv2/ratio.ttl52
-rw-r--r--blop.lv2/sawtooth.ttl46
-rw-r--r--blop.lv2/sequencer_16.ttl167
-rw-r--r--blop.lv2/sequencer_32.ttl263
-rw-r--r--blop.lv2/sequencer_64.ttl455
-rw-r--r--blop.lv2/square.ttl46
-rw-r--r--blop.lv2/sum.ttl50
-rw-r--r--blop.lv2/sync_pulse.ttl63
-rw-r--r--blop.lv2/sync_square.ttl55
-rw-r--r--blop.lv2/tracker.ttl122
-rw-r--r--blop.lv2/triangle.ttl61
-rw-r--r--src/adsr.c259
-rw-r--r--src/adsr_gt.c266
-rw-r--r--src/amp.c166
-rw-r--r--src/branch.c223
-rw-r--r--src/dahdsr.c416
-rw-r--r--src/difference.c188
-rw-r--r--src/fmod.c199
-rw-r--r--src/include/common.h54
-rw-r--r--src/include/interpolate.h74
-rw-r--r--src/include/lp4pole_filter.h137
-rw-r--r--src/include/math_func.h48
-rw-r--r--src/include/uris.h59
-rw-r--r--src/include/vector_op.h44
-rw-r--r--src/include/wavedata.h193
-rw-r--r--src/include/wdatutil.h141
-rw-r--r--src/interpolator.c144
-rw-r--r--src/lp4pole.c206
-rw-r--r--src/lp4pole_filter.c55
-rw-r--r--src/product.c187
-rw-r--r--src/pulse.c225
-rw-r--r--src/quantiser.c461
-rw-r--r--src/random.c237
-rw-r--r--src/ratio.c202
-rw-r--r--src/sawtooth.c194
-rw-r--r--src/sequencer.c216
-rw-r--r--src/square.c195
-rw-r--r--src/sum.c185
-rw-r--r--src/sync_pulse.c213
-rw-r--r--src/sync_square.c201
-rw-r--r--src/tracker.c245
-rw-r--r--src/triangle.c232
-rw-r--r--src/wavedata.c79
-rw-r--r--src/wavegen.c310
-rw-r--r--src/wdatutil.c679
-rw-r--r--waflib/.gitignore (renamed from .gitignore)0
-rw-r--r--waflib/Build.py (renamed from Build.py)0
-rw-r--r--waflib/COPYING25
-rw-r--r--waflib/ConfigSet.py (renamed from ConfigSet.py)0
-rw-r--r--waflib/Configure.py (renamed from Configure.py)0
-rw-r--r--waflib/Context.py (renamed from Context.py)0
-rw-r--r--waflib/Errors.py (renamed from Errors.py)0
-rw-r--r--waflib/Logs.py (renamed from Logs.py)0
-rw-r--r--waflib/Node.py (renamed from Node.py)0
-rw-r--r--waflib/Options.py (renamed from Options.py)0
-rw-r--r--waflib/README.md (renamed from README.md)0
-rw-r--r--waflib/Runner.py (renamed from Runner.py)0
-rw-r--r--waflib/Scripting.py (renamed from Scripting.py)0
-rw-r--r--waflib/Task.py (renamed from Task.py)0
-rw-r--r--waflib/TaskGen.py (renamed from TaskGen.py)0
-rw-r--r--waflib/Tools/__init__.py (renamed from Tools/__init__.py)0
-rw-r--r--waflib/Tools/ar.py (renamed from Tools/ar.py)0
-rw-r--r--waflib/Tools/asm.py (renamed from Tools/asm.py)0
-rw-r--r--waflib/Tools/bison.py (renamed from Tools/bison.py)0
-rw-r--r--waflib/Tools/c.py (renamed from Tools/c.py)0
-rw-r--r--waflib/Tools/c_aliases.py (renamed from Tools/c_aliases.py)0
-rw-r--r--waflib/Tools/c_config.py (renamed from Tools/c_config.py)0
-rw-r--r--waflib/Tools/c_osx.py (renamed from Tools/c_osx.py)0
-rw-r--r--waflib/Tools/c_preproc.py (renamed from Tools/c_preproc.py)0
-rw-r--r--waflib/Tools/c_tests.py (renamed from Tools/c_tests.py)0
-rw-r--r--waflib/Tools/ccroot.py (renamed from Tools/ccroot.py)0
-rw-r--r--waflib/Tools/clang.py (renamed from Tools/clang.py)0
-rw-r--r--waflib/Tools/clangxx.py (renamed from Tools/clangxx.py)0
-rw-r--r--waflib/Tools/compiler_c.py (renamed from Tools/compiler_c.py)0
-rw-r--r--waflib/Tools/compiler_cxx.py (renamed from Tools/compiler_cxx.py)0
-rw-r--r--waflib/Tools/compiler_d.py (renamed from Tools/compiler_d.py)0
-rw-r--r--waflib/Tools/compiler_fc.py (renamed from Tools/compiler_fc.py)0
-rw-r--r--waflib/Tools/cs.py (renamed from Tools/cs.py)0
-rw-r--r--waflib/Tools/cxx.py (renamed from Tools/cxx.py)0
-rw-r--r--waflib/Tools/d.py (renamed from Tools/d.py)0
-rw-r--r--waflib/Tools/d_config.py (renamed from Tools/d_config.py)0
-rw-r--r--waflib/Tools/d_scan.py (renamed from Tools/d_scan.py)0
-rw-r--r--waflib/Tools/dbus.py (renamed from Tools/dbus.py)0
-rw-r--r--waflib/Tools/dmd.py (renamed from Tools/dmd.py)0
-rw-r--r--waflib/Tools/errcheck.py (renamed from Tools/errcheck.py)0
-rw-r--r--waflib/Tools/fc.py (renamed from Tools/fc.py)0
-rw-r--r--waflib/Tools/fc_config.py (renamed from Tools/fc_config.py)0
-rw-r--r--waflib/Tools/fc_scan.py (renamed from Tools/fc_scan.py)0
-rw-r--r--waflib/Tools/flex.py (renamed from Tools/flex.py)0
-rw-r--r--waflib/Tools/g95.py (renamed from Tools/g95.py)0
-rw-r--r--waflib/Tools/gas.py (renamed from Tools/gas.py)0
-rw-r--r--waflib/Tools/gcc.py (renamed from Tools/gcc.py)0
-rw-r--r--waflib/Tools/gdc.py (renamed from Tools/gdc.py)0
-rw-r--r--waflib/Tools/gfortran.py (renamed from Tools/gfortran.py)0
-rw-r--r--waflib/Tools/glib2.py (renamed from Tools/glib2.py)0
-rw-r--r--waflib/Tools/gnu_dirs.py (renamed from Tools/gnu_dirs.py)0
-rw-r--r--waflib/Tools/gxx.py (renamed from Tools/gxx.py)0
-rw-r--r--waflib/Tools/icc.py (renamed from Tools/icc.py)0
-rw-r--r--waflib/Tools/icpc.py (renamed from Tools/icpc.py)0
-rw-r--r--waflib/Tools/ifort.py (renamed from Tools/ifort.py)0
-rw-r--r--waflib/Tools/intltool.py (renamed from Tools/intltool.py)0
-rw-r--r--waflib/Tools/irixcc.py (renamed from Tools/irixcc.py)0
-rw-r--r--waflib/Tools/javaw.py (renamed from Tools/javaw.py)0
-rw-r--r--waflib/Tools/ldc2.py (renamed from Tools/ldc2.py)0
-rw-r--r--waflib/Tools/lua.py (renamed from Tools/lua.py)0
-rw-r--r--waflib/Tools/md5_tstamp.py (renamed from Tools/md5_tstamp.py)0
-rw-r--r--waflib/Tools/msvc.py (renamed from Tools/msvc.py)0
-rw-r--r--waflib/Tools/nasm.py (renamed from Tools/nasm.py)0
-rw-r--r--waflib/Tools/nobuild.py (renamed from Tools/nobuild.py)0
-rw-r--r--waflib/Tools/perl.py (renamed from Tools/perl.py)0
-rw-r--r--waflib/Tools/python.py (renamed from Tools/python.py)0
-rw-r--r--waflib/Tools/qt5.py (renamed from Tools/qt5.py)0
-rw-r--r--waflib/Tools/ruby.py (renamed from Tools/ruby.py)0
-rw-r--r--waflib/Tools/suncc.py (renamed from Tools/suncc.py)0
-rw-r--r--waflib/Tools/suncxx.py (renamed from Tools/suncxx.py)0
-rw-r--r--waflib/Tools/tex.py (renamed from Tools/tex.py)0
-rw-r--r--waflib/Tools/vala.py (renamed from Tools/vala.py)0
-rw-r--r--waflib/Tools/waf_unit_test.py (renamed from Tools/waf_unit_test.py)0
-rw-r--r--waflib/Tools/winres.py (renamed from Tools/winres.py)0
-rw-r--r--waflib/Tools/xlc.py (renamed from Tools/xlc.py)0
-rw-r--r--waflib/Tools/xlcxx.py (renamed from Tools/xlcxx.py)0
-rw-r--r--waflib/Utils.py (renamed from Utils.py)0
-rw-r--r--waflib/__init__.py (renamed from __init__.py)0
-rw-r--r--waflib/ansiterm.py (renamed from ansiterm.py)0
-rw-r--r--waflib/extras/__init__.py (renamed from extras/__init__.py)0
-rw-r--r--waflib/extras/autowaf.py (renamed from extras/autowaf.py)0
-rw-r--r--waflib/extras/batched_cc.py (renamed from extras/batched_cc.py)0
-rw-r--r--waflib/extras/biber.py (renamed from extras/biber.py)0
-rw-r--r--waflib/extras/bjam.py (renamed from extras/bjam.py)0
-rw-r--r--waflib/extras/blender.py (renamed from extras/blender.py)0
-rw-r--r--waflib/extras/boo.py (renamed from extras/boo.py)0
-rw-r--r--waflib/extras/boost.py (renamed from extras/boost.py)0
-rw-r--r--waflib/extras/build_file_tracker.py (renamed from extras/build_file_tracker.py)0
-rw-r--r--waflib/extras/build_logs.py (renamed from extras/build_logs.py)0
-rw-r--r--waflib/extras/buildcopy.py (renamed from extras/buildcopy.py)0
-rw-r--r--waflib/extras/c_bgxlc.py (renamed from extras/c_bgxlc.py)0
-rw-r--r--waflib/extras/c_dumbpreproc.py (renamed from extras/c_dumbpreproc.py)0
-rw-r--r--waflib/extras/c_emscripten.py (renamed from extras/c_emscripten.py)0
-rw-r--r--waflib/extras/c_nec.py (renamed from extras/c_nec.py)0
-rw-r--r--waflib/extras/cabal.py (renamed from extras/cabal.py)0
-rw-r--r--waflib/extras/cfg_altoptions.py (renamed from extras/cfg_altoptions.py)0
-rw-r--r--waflib/extras/clang_compilation_database.py (renamed from extras/clang_compilation_database.py)0
-rw-r--r--waflib/extras/codelite.py (renamed from extras/codelite.py)0
-rw-r--r--waflib/extras/color_gcc.py (renamed from extras/color_gcc.py)0
-rw-r--r--waflib/extras/color_rvct.py (renamed from extras/color_rvct.py)0
-rw-r--r--waflib/extras/compat15.py (renamed from extras/compat15.py)0
-rw-r--r--waflib/extras/cppcheck.py (renamed from extras/cppcheck.py)0
-rw-r--r--waflib/extras/cpplint.py (renamed from extras/cpplint.py)0
-rw-r--r--waflib/extras/cross_gnu.py (renamed from extras/cross_gnu.py)0
-rw-r--r--waflib/extras/cython.py (renamed from extras/cython.py)0
-rw-r--r--waflib/extras/dcc.py (renamed from extras/dcc.py)0
-rw-r--r--waflib/extras/distnet.py (renamed from extras/distnet.py)0
-rw-r--r--waflib/extras/doxygen.py (renamed from extras/doxygen.py)0
-rw-r--r--waflib/extras/dpapi.py (renamed from extras/dpapi.py)0
-rw-r--r--waflib/extras/eclipse.py (renamed from extras/eclipse.py)0
-rw-r--r--waflib/extras/erlang.py (renamed from extras/erlang.py)0
-rw-r--r--waflib/extras/fast_partial.py (renamed from extras/fast_partial.py)0
-rw-r--r--waflib/extras/fc_bgxlf.py (renamed from extras/fc_bgxlf.py)0
-rw-r--r--waflib/extras/fc_cray.py (renamed from extras/fc_cray.py)0
-rw-r--r--waflib/extras/fc_nag.py (renamed from extras/fc_nag.py)0
-rw-r--r--waflib/extras/fc_nec.py (renamed from extras/fc_nec.py)0
-rw-r--r--waflib/extras/fc_nfort.py (renamed from extras/fc_nfort.py)0
-rw-r--r--waflib/extras/fc_open64.py (renamed from extras/fc_open64.py)0
-rw-r--r--waflib/extras/fc_pgfortran.py (renamed from extras/fc_pgfortran.py)0
-rw-r--r--waflib/extras/fc_solstudio.py (renamed from extras/fc_solstudio.py)0
-rw-r--r--waflib/extras/fc_xlf.py (renamed from extras/fc_xlf.py)0
-rw-r--r--waflib/extras/file_to_object.py (renamed from extras/file_to_object.py)0
-rw-r--r--waflib/extras/fluid.py (renamed from extras/fluid.py)0
-rw-r--r--waflib/extras/freeimage.py (renamed from extras/freeimage.py)0
-rw-r--r--waflib/extras/fsb.py (renamed from extras/fsb.py)0
-rw-r--r--waflib/extras/fsc.py (renamed from extras/fsc.py)0
-rw-r--r--waflib/extras/gccdeps.py (renamed from extras/gccdeps.py)0
-rw-r--r--waflib/extras/gdbus.py (renamed from extras/gdbus.py)0
-rw-r--r--waflib/extras/gob2.py (renamed from extras/gob2.py)0
-rw-r--r--waflib/extras/halide.py (renamed from extras/halide.py)0
-rwxr-xr-xwaflib/extras/javatest.py (renamed from extras/javatest.py)0
-rw-r--r--waflib/extras/kde4.py (renamed from extras/kde4.py)0
-rw-r--r--waflib/extras/local_rpath.py (renamed from extras/local_rpath.py)0
-rw-r--r--waflib/extras/lv2.py (renamed from extras/lv2.py)0
-rw-r--r--waflib/extras/make.py (renamed from extras/make.py)0
-rw-r--r--waflib/extras/midl.py (renamed from extras/midl.py)0
-rw-r--r--waflib/extras/msvcdeps.py (renamed from extras/msvcdeps.py)0
-rw-r--r--waflib/extras/msvs.py (renamed from extras/msvs.py)0
-rw-r--r--waflib/extras/netcache_client.py (renamed from extras/netcache_client.py)0
-rw-r--r--waflib/extras/objcopy.py (renamed from extras/objcopy.py)0
-rw-r--r--waflib/extras/ocaml.py (renamed from extras/ocaml.py)0
-rw-r--r--waflib/extras/package.py (renamed from extras/package.py)0
-rw-r--r--waflib/extras/parallel_debug.py (renamed from extras/parallel_debug.py)0
-rw-r--r--waflib/extras/pch.py (renamed from extras/pch.py)0
-rw-r--r--waflib/extras/pep8.py (renamed from extras/pep8.py)0
-rw-r--r--waflib/extras/pgicc.py (renamed from extras/pgicc.py)0
-rw-r--r--waflib/extras/pgicxx.py (renamed from extras/pgicxx.py)0
-rw-r--r--waflib/extras/proc.py (renamed from extras/proc.py)0
-rw-r--r--waflib/extras/protoc.py (renamed from extras/protoc.py)0
-rw-r--r--waflib/extras/pyqt5.py (renamed from extras/pyqt5.py)0
-rw-r--r--waflib/extras/pytest.py (renamed from extras/pytest.py)0
-rw-r--r--waflib/extras/qnxnto.py (renamed from extras/qnxnto.py)0
-rw-r--r--waflib/extras/qt4.py (renamed from extras/qt4.py)0
-rw-r--r--waflib/extras/relocation.py (renamed from extras/relocation.py)0
-rw-r--r--waflib/extras/remote.py (renamed from extras/remote.py)0
-rw-r--r--waflib/extras/resx.py (renamed from extras/resx.py)0
-rw-r--r--waflib/extras/review.py (renamed from extras/review.py)0
-rw-r--r--waflib/extras/rst.py (renamed from extras/rst.py)0
-rw-r--r--waflib/extras/run_do_script.py (renamed from extras/run_do_script.py)0
-rw-r--r--waflib/extras/run_m_script.py (renamed from extras/run_m_script.py)0
-rw-r--r--waflib/extras/run_py_script.py (renamed from extras/run_py_script.py)0
-rw-r--r--waflib/extras/run_r_script.py (renamed from extras/run_r_script.py)0
-rw-r--r--waflib/extras/sas.py (renamed from extras/sas.py)0
-rw-r--r--waflib/extras/satellite_assembly.py (renamed from extras/satellite_assembly.py)0
-rw-r--r--waflib/extras/scala.py (renamed from extras/scala.py)0
-rw-r--r--waflib/extras/slow_qt4.py (renamed from extras/slow_qt4.py)0
-rw-r--r--waflib/extras/softlink_libs.py (renamed from extras/softlink_libs.py)0
-rw-r--r--waflib/extras/stale.py (renamed from extras/stale.py)0
-rw-r--r--waflib/extras/stracedeps.py (renamed from extras/stracedeps.py)0
-rw-r--r--waflib/extras/swig.py (renamed from extras/swig.py)0
-rw-r--r--waflib/extras/syms.py (renamed from extras/syms.py)0
-rw-r--r--waflib/extras/ticgt.py (renamed from extras/ticgt.py)0
-rw-r--r--waflib/extras/unity.py (renamed from extras/unity.py)0
-rw-r--r--waflib/extras/use_config.py (renamed from extras/use_config.py)0
-rw-r--r--waflib/extras/valadoc.py (renamed from extras/valadoc.py)0
-rw-r--r--waflib/extras/waf_xattr.py (renamed from extras/waf_xattr.py)0
-rw-r--r--waflib/extras/why.py (renamed from extras/why.py)0
-rw-r--r--waflib/extras/win32_opts.py (renamed from extras/win32_opts.py)0
-rw-r--r--waflib/extras/wix.py (renamed from extras/wix.py)0
-rw-r--r--waflib/extras/xcode6.py (renamed from extras/xcode6.py)0
-rw-r--r--waflib/fixpy2.py (renamed from fixpy2.py)0
-rwxr-xr-xwaflib/processor.py (renamed from processor.py)0
-rwxr-xr-xwaflib/waf16
-rw-r--r--wscript192
250 files changed, 11630 insertions, 25 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..ab05648
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+The original LADSPA plugins on which this port is based are by:
+
+ Mike Rawes <mike_rawes@yahoo.co.uk>
+
+This LV2 port was done by:
+
+ David Robillard <d@drobilla.net>
diff --git a/COPYING b/COPYING
index a4147d2..94a9ed0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,25 +1,674 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..623cddd
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,59 @@
+Installation Instructions
+=========================
+
+Basic Installation
+------------------
+
+Building this software requires only Python. To install with default options:
+
+ ./waf configure
+ ./waf
+ ./waf install
+
+You may need to become root for the install stage, for example:
+
+ sudo ./waf install
+
+Configuration Options
+---------------------
+
+All supported options can be viewed using the command:
+
+ ./waf --help
+
+Most options only need to be passed during the configure stage, for example:
+
+ ./waf configure --prefix=/usr
+ ./waf
+ ./waf install
+
+Compiler Configuration
+----------------------
+
+Several standard environment variables can be used to control how compilers are
+invoked:
+
+ * CC: Path to C compiler
+ * CFLAGS: C compiler options
+ * CXX: Path to C++ compiler
+ * CXXFLAGS: C++ compiler options
+ * CPPFLAGS: C preprocessor options
+ * LINKFLAGS: Linker options
+
+Installation Directories
+------------------------
+
+The --prefix option (or the PREFIX environment variable) can be used to change
+the prefix which all files are installed under. There are also several options
+allowing for more fine-tuned control, see the --help output for details.
+
+Packaging
+---------
+
+Everything can be installed to a specific root directory by passing a --destdir
+option to the install stage (or setting the DESTDIR environment variable),
+which adds a prefix to all install paths. For example:
+
+ ./waf configure --prefix=/usr
+ ./waf
+ ./waf install --destdir=/tmp/package
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..583efcf
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,14 @@
+blop-lv2 (1.0.1) unstable;
+
+ * Add missing properties for options interface
+ * Fix compilation with default hidden visibility
+ * Set Hz unit on frequency ports
+ * Avoid pedantic warnings
+
+ -- David Robillard <d@drobilla.net> Sun, 12 Feb 2017 18:07:57 +0100
+
+blop-lv2 (1.0.0) stable;
+
+ * Initial release
+
+ -- David Robillard <d@drobilla.net> Sun, 05 Jan 2014 12:00:00 -0500
diff --git a/README b/README
new file mode 100644
index 0000000..286c5f6
--- /dev/null
+++ b/README
@@ -0,0 +1,13 @@
+BLOP.LV2
+========
+
+This is a port of the BLOP LADSPA plugins by Mike Rawes to LV2.
+
+This is a mostly faithful port of blop-0.2.8, except plugin variants have been
+eliminated via the use of morphable Control/CV ports. This way, users do not
+have to choose from several versions of the same plugin, but the host can
+configure controls to be control-rate or audio-rate as appropriate. This
+mechanism is backwards compatible, so these ports will simply appear as normal
+LV2 control ports in hosts that do not support port morphing.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/blop.lv2/adsr.ttl b/blop.lv2/adsr.ttl
new file mode 100644
index 0000000..290a745
--- /dev/null
+++ b/blop.lv2/adsr.ttl
@@ -0,0 +1,82 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+
+blop:adsr
+ a lv2:Plugin ,
+ lv2:EnvelopePlugin ;
+ lv2:project blop: ;
+ lv2:symbol "adsr" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Driving Signal" ;
+ lv2:symbol "drive"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Trigger Threshold" ;
+ lv2:symbol "thresh"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Attack Time" ;
+ lv2:symbol "attack" ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:minimum 0 ;
+ lv2:name "Decay Time" ;
+ lv2:symbol "decay" ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 1 ;
+ lv2:index 4 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Sustain Level" ;
+ lv2:symbol "sustain"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 5 ;
+ lv2:minimum 0 ;
+ lv2:name "Release Time" ;
+ lv2:symbol "release" ;
+ units:unit units:s
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 6 ;
+ lv2:name "Envelope Out" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1653> ;
+ doap:name "ADSR Envelope" ;
+ lv2:documentation """
+<p>Generates an ADSR (Attack, Decay, Sustain and Release) envelope.</p>
+
+<p>Driven by a gate signal - if the level of the signal goes higher than the
+Trigger Threshold, the attack stage begins, proceeds to the decay stage and
+then holds at the sustain level. The release stage begins when the gate falls
+below this theshold - even if the previous stages have not completed.</p>
+
+<p>The output is a signal between 0.0 (rest) and 1.0 (peak) and the transitions
+are linear.</p>
+""" .
diff --git a/blop.lv2/adsr_gt.ttl b/blop.lv2/adsr_gt.ttl
new file mode 100644
index 0000000..719330e
--- /dev/null
+++ b/blop.lv2/adsr_gt.ttl
@@ -0,0 +1,91 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+
+blop:adsr_gt
+ a lv2:Plugin ,
+ lv2:EnvelopePlugin ;
+ lv2:project blop: ;
+ lv2:symbol "adsr_gt" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Trigger" ;
+ lv2:portProperty lv2:toggled ,
+ <http://lv2plug.in/ns/ext/port-props#trigger> ;
+ lv2:symbol "trigger"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Attack Time" ;
+ lv2:symbol "attack" ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:minimum 0 ;
+ lv2:name "Decay Time" ;
+ lv2:symbol "decay" ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 1 ;
+ lv2:index 4 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Sustain Level" ;
+ lv2:symbol "sustain"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 5 ;
+ lv2:minimum 0 ;
+ lv2:name "Release Time" ;
+ lv2:symbol "release" ;
+ units:unit units:s
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 6 ;
+ lv2:name "Envelope Out" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1680> ;
+ doap:name "Retriggerable ADSR Envelope" ;
+ lv2:documentation """
+<p>Generates an ADSR (Attack, Decay, Sustain and Release)
+envelope.</p>
+
+<p>Does the same thing as the other ADSR (1658) above, except the
+Trigger Threshold is fixed at zero, and the additional Trigger
+input allows retriggering whilst the gate is still high.</p>
+
+<p>The reasoning behind this design is to remove the need for a keyboard player
+to release a key before pressing another when using with a monosynth. <a
+href="http://www.sospubs.co.uk/sos/nov99/articles/synthsecrets.htm">The
+&lsquo;Synth Secrets&rsquo; article in the November 1999 issue of &lsquo;Sound
+on Sound&rsquo;</a> explains this in detail.</p>
+
+<p>The output is a signal between 0.0 (rest) and 1.0 (peak) and the
+transitions are linear.</p>
+""" .
diff --git a/blop.lv2/amp.ttl b/blop.lv2/amp.ttl
new file mode 100644
index 0000000..8ba454d
--- /dev/null
+++ b/blop.lv2/amp.ttl
@@ -0,0 +1,49 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:amp
+ a lv2:Plugin ,
+ lv2:AmplifierPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "amp" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 96 ;
+ lv2:minimum -96 ;
+ lv2:name "Gain" ;
+ lv2:symbol "gain" ;
+ units:unit units:db ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:AudioPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 2 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1654> ,
+ <urn:ladspa:1655> ;
+ doap:name "Amplifier" ;
+ lv2:documentation """
+<p>A simple monophonic amplifier.</p>
+""" .
diff --git a/blop.lv2/branch.ttl b/blop.lv2/branch.ttl
new file mode 100644
index 0000000..f58b81d
--- /dev/null
+++ b/blop.lv2/branch.ttl
@@ -0,0 +1,54 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:branch
+ a lv2:Plugin ,
+ lv2:UtilityPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "branch" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:name "Input" ;
+ lv2:symbol "in" ;
+ morph:supportsType lv2:CVPort ,
+ lv2:AudioPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 1 ;
+ lv2:name "Output 1" ;
+ lv2:symbol "out1" ;
+ morph:supportsType lv2:CVPort ,
+ lv2:AudioPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 2 ;
+ lv2:name "Output 2" ;
+ lv2:symbol "out2" ;
+ morph:supportsType lv2:CVPort ,
+ lv2:AudioPort
+ ] ;
+ dct:replaces <urn:ladspa:1673> ,
+ <urn:ladspa:1674> ;
+ doap:name "Branch" ;
+ lv2:documentation """
+<p>Splits an input signal into two identical signals. Somewhat redundant, as
+most modular synth hosts allow you to connect an output to more than one input.
+If your host of choice does not allow this, this plugin will do the job...</p>
+""" .
diff --git a/blop.lv2/dahdsr.ttl b/blop.lv2/dahdsr.ttl
new file mode 100644
index 0000000..68652aa
--- /dev/null
+++ b/blop.lv2/dahdsr.ttl
@@ -0,0 +1,128 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+
+blop:dahdsr
+ a lv2:Plugin ,
+ lv2:EnvelopePlugin ;
+ lv2:project blop: ;
+ lv2:symbol "dahdsr" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Trigger" ;
+ lv2:portProperty lv2:toggled ,
+ <http://lv2plug.in/ns/ext/port-props#trigger> ;
+ lv2:symbol "trigger"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0 ;
+ lv2:index 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Delay Time" ;
+ lv2:symbol "delay" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:minimum 0 ;
+ lv2:name "Attack Time" ;
+ lv2:symbol "attack" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0 ;
+ lv2:index 4 ;
+ lv2:minimum 0 ;
+ lv2:name "Hold Time" ;
+ lv2:symbol "hold" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0 ;
+ lv2:index 5 ;
+ lv2:minimum 0 ;
+ lv2:name "Decay Time" ;
+ lv2:symbol "decay" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:s
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 1 ;
+ lv2:index 6 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Sustain Level" ;
+ lv2:symbol "sustain" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0 ;
+ lv2:index 7 ;
+ lv2:minimum 0 ;
+ lv2:name "Release Time" ;
+ lv2:symbol "release" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:s
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 8 ;
+ lv2:name "Envelope Out" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:2021> ,
+ <urn:ladspa:2022> ,
+ <urn:ladspa:2038> ;
+ doap:name "Retriggerable DAHDSR Envelope" ;
+ lv2:documentation """
+<p>Generates a DAHDSR (Delay, Attack, Hold, Decay, Sustain, Release)
+envelope.</p>
+
+<p>Another envelope generator, this time with two additional stages - Delay,
+which delays the onset of the Attack stage, and Hold, which holds the output at
+maximum before the Decay stage begins.</p>
+
+<p>Triggering works in subtly different ways to the <a href="#adsr_gnt">ADSR
+(1680)</a> - the Trigger will restart the envelope even if the Gate is closed -
+the effect of this is to proceed through the stages and begin the release stage
+immediately after the decay stage.</p>
+
+<p>The final variant (ID 2038) uses control-rate gate and trigger, which is a
+little less CPU hungry, but will cause timing errors that are dependent on the
+block size being used by the host.</p>
+""" .
diff --git a/blop.lv2/difference.ttl b/blop.lv2/difference.ttl
new file mode 100644
index 0000000..e55c6fe
--- /dev/null
+++ b/blop.lv2/difference.ttl
@@ -0,0 +1,51 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:difference
+ a lv2:Plugin ,
+ lv2:UtilityPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "difference" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:name "Minuend" ;
+ lv2:symbol "minuend" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 1 ;
+ lv2:name "Subtrahend" ;
+ lv2:symbol "subtrahend" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 2 ;
+ lv2:name "Difference" ;
+ lv2:symbol "difference" ;
+ morph:supportsType lv2:CVPort
+ ] ;
+ dct:replaces <urn:ladspa:2030> ,
+ <urn:ladspa:2031> ,
+ <urn:ladspa:2032> ,
+ <urn:ladspa:2033> ;
+ doap:name "Difference" ;
+ lv2:documentation """
+<p>Subtract two signals.</p>
+""" .
diff --git a/blop.lv2/fmod.ttl b/blop.lv2/fmod.ttl
new file mode 100644
index 0000000..38a552d
--- /dev/null
+++ b/blop.lv2/fmod.ttl
@@ -0,0 +1,60 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:fmod
+ a lv2:Plugin ,
+ lv2:SpectralPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "fmod" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.00001 ;
+ lv2:name "Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 1 ;
+ lv2:name "Modulation" ;
+ lv2:symbol "mod" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:oct
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 2 ;
+ lv2:name "Modulated Frequency" ;
+ lv2:symbol "out" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] ;
+ dct:replaces <urn:ladspa:1656> ,
+ <urn:ladspa:1657> ,
+ <urn:ladspa:1658> ,
+ <urn:ladspa:1659> ;
+ doap:name "Frequency Modulator" ;
+ lv2:documentation """
+<p>Modulates an input frequency by a driving signal, transposing the frequency
+by &#177;1 Octave per unit amplitude of signal.</p>
+""" .
diff --git a/blop.lv2/interpolator.ttl b/blop.lv2/interpolator.ttl
new file mode 100644
index 0000000..38c93db
--- /dev/null
+++ b/blop.lv2/interpolator.ttl
@@ -0,0 +1,32 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+
+blop:interpolator
+ a lv2:Plugin ,
+ lv2:UtilityPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "interpolator" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Control Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 1 ;
+ lv2:name "Interpolated Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1660> ;
+ doap:name "Control to CV Interpolator" ;
+ lv2:documentation """
+<p>Interpolates a control-rate (per-block) signal into a smooth audio-rate
+(per-sample) signal.</p>
+""" .
diff --git a/blop.lv2/lp4pole.ttl b/blop.lv2/lp4pole.ttl
new file mode 100644
index 0000000..8fcf75c
--- /dev/null
+++ b/blop.lv2/lp4pole.ttl
@@ -0,0 +1,64 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:lp4pole
+ a lv2:Plugin ,
+ lv2:LowpassPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "lp4pole" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0.5 ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.00001 ;
+ lv2:name "Cutoff Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "cutoff" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0 ;
+ lv2:index 1 ;
+ lv2:maximum 4 ;
+ lv2:minimum 0 ;
+ lv2:name "Resonance" ;
+ lv2:symbol "resonance" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:AudioPort ,
+ lv2:InputPort ;
+ lv2:index 2 ;
+ lv2:name "Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 3 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1671> ,
+ <urn:ladspa:1672> ;
+ doap:name "4 Pole Resonant Low-Pass" ;
+ lv2:documentation """
+<p>Emulates a low pass filter in popular analogue synthesisers. This particular
+filter is derived from one of <a
+href="http://www.musicdsp.org/archive.php?classid=3#24">many
+implementations</a> of the Moog 4 pole filter.</p>
+""" .
diff --git a/blop.lv2/manifest.ttl.in b/blop.lv2/manifest.ttl.in
new file mode 100644
index 0000000..4942342
--- /dev/null
+++ b/blop.lv2/manifest.ttl.in
@@ -0,0 +1,155 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://drobilla.net/drobilla#me>
+ a foaf:Person ;
+ foaf:name "David Robillard" ;
+ foaf:mbox <mailto:d@drobilla.net> ;
+ rdfs:seeAlso <http://drobilla.net/drobilla> .
+
+blop:
+ a doap:Project ;
+ lv2:symbol "blop" ;
+ doap:name "Blop LV2" ;
+ doap:shortdesc "An LV2 port of the Blop plugins." ;
+ doap:homepage <http://drobilla.net/software/blop-lv2> ;
+ doap:license <http://opensource.org/licenses/gpl-3.0> ;
+ doap:maintainer <http://drobilla.net/drobilla#me> ;
+ doap:developer [
+ a foaf:Person ;
+ foaf:name "Mike Rawes" ;
+ foaf:mbox <mike_rawes@yahoo.co.uk>
+ ] .
+
+blop:adsr
+ a lv2:Plugin ;
+ rdfs:seeAlso <adsr.ttl> ;
+ lv2:binary <adsr@LIB_EXT@> .
+
+blop:adsr_gt
+ a lv2:Plugin ;
+ rdfs:seeAlso <adsr_gt.ttl> ;
+ lv2:binary <adsr_gt@LIB_EXT@> .
+
+blop:amp
+ a lv2:Plugin ;
+ rdfs:seeAlso <amp.ttl> ;
+ lv2:binary <amp@LIB_EXT@> .
+
+blop:branch
+ a lv2:Plugin ;
+ rdfs:seeAlso <branch.ttl> ;
+ lv2:binary <branch@LIB_EXT@> .
+
+blop:dahdsr
+ a lv2:Plugin ;
+ rdfs:seeAlso <dahdsr.ttl> ;
+ lv2:binary <dahdsr@LIB_EXT@> .
+
+blop:difference
+ a lv2:Plugin ;
+ rdfs:seeAlso <difference.ttl> ;
+ lv2:binary <difference@LIB_EXT@> .
+
+blop:fmod
+ a lv2:Plugin ;
+ rdfs:seeAlso <fmod.ttl> ;
+ lv2:binary <fmod@LIB_EXT@> .
+
+blop:interpolator
+ a lv2:Plugin ;
+ rdfs:seeAlso <interpolator.ttl> ;
+ lv2:binary <interpolator@LIB_EXT@> .
+
+blop:lp4pole
+ a lv2:Plugin ;
+ rdfs:seeAlso <lp4pole.ttl> ;
+ lv2:binary <lp4pole@LIB_EXT@> .
+
+blop:product
+ a lv2:Plugin ;
+ rdfs:seeAlso <product.ttl> ;
+ lv2:binary <product@LIB_EXT@> .
+
+blop:pulse
+ a lv2:Plugin ;
+ rdfs:seeAlso <pulse.ttl> ;
+ lv2:binary <pulse@LIB_EXT@> .
+
+blop:quantiser_20
+ a lv2:Plugin ;
+ rdfs:seeAlso <quantiser_20.ttl> ;
+ lv2:binary <quantiser_20@LIB_EXT@> .
+
+blop:quantiser_50
+ a lv2:Plugin ;
+ rdfs:seeAlso <quantiser_50.ttl> ;
+ lv2:binary <quantiser_50@LIB_EXT@> .
+
+blop:quantiser_100
+ a lv2:Plugin ;
+ rdfs:seeAlso <quantiser_100.ttl> ;
+ lv2:binary <quantiser_100@LIB_EXT@> .
+
+blop:random
+ a lv2:Plugin ;
+ rdfs:seeAlso <random.ttl> ;
+ lv2:binary <random@LIB_EXT@> .
+
+blop:ratio
+ a lv2:Plugin ;
+ rdfs:seeAlso <ratio.ttl> ;
+ lv2:binary <ratio@LIB_EXT@> .
+
+blop:sawtooth
+ a lv2:Plugin ;
+ rdfs:seeAlso <sawtooth.ttl> ;
+ lv2:binary <sawtooth@LIB_EXT@> .
+
+blop:sequencer_16
+ a lv2:Plugin ;
+ rdfs:seeAlso <sequencer_16.ttl> ;
+ lv2:binary <sequencer_16@LIB_EXT@> .
+
+blop:sequencer_32
+ a lv2:Plugin ;
+ rdfs:seeAlso <sequencer_32.ttl> ;
+ lv2:binary <sequencer_32@LIB_EXT@> .
+
+blop:sequencer_64
+ a lv2:Plugin ;
+ rdfs:seeAlso <sequencer_64.ttl> ;
+ lv2:binary <sequencer_64@LIB_EXT@> .
+
+blop:square
+ a lv2:Plugin ;
+ rdfs:seeAlso <square.ttl> ;
+ lv2:binary <square@LIB_EXT@> .
+
+blop:sum
+ a lv2:Plugin ;
+ rdfs:seeAlso <sum.ttl> ;
+ lv2:binary <sum@LIB_EXT@> .
+
+blop:sync_pulse
+ a lv2:Plugin ;
+ rdfs:seeAlso <sync_pulse.ttl> ;
+ lv2:binary <sync_pulse@LIB_EXT@> .
+
+blop:sync_square
+ a lv2:Plugin ;
+ rdfs:seeAlso <sync_square.ttl> ;
+ lv2:binary <sync_square@LIB_EXT@> .
+
+blop:tracker
+ a lv2:Plugin ;
+ rdfs:seeAlso <tracker.ttl> ;
+ lv2:binary <tracker@LIB_EXT@> .
+
+blop:triangle
+ a lv2:Plugin ;
+ rdfs:seeAlso <triangle.ttl> ;
+ lv2:binary <triangle@LIB_EXT@> .
diff --git a/blop.lv2/product.ttl b/blop.lv2/product.ttl
new file mode 100644
index 0000000..d273f22
--- /dev/null
+++ b/blop.lv2/product.ttl
@@ -0,0 +1,50 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:product
+ a lv2:Plugin ,
+ lv2:UtilityPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "product" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:name "Multiplicand" ;
+ lv2:symbol "multiplicand" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 1 ;
+ lv2:name "Multiplier" ;
+ lv2:symbol "multiplier" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 2 ;
+ lv2:name "Product" ;
+ lv2:symbol "product" ;
+ morph:supportsType lv2:CVPort
+ ] ;
+ dct:replaces <urn:ladspa:1668> ,
+ <urn:ladspa:1669> ,
+ <urn:ladspa:1670> ;
+ doap:name "Product" ;
+ lv2:documentation """
+<p>Multiply two signals.</p>
+""" .
diff --git a/blop.lv2/pulse.ttl b/blop.lv2/pulse.ttl
new file mode 100644
index 0000000..5a940a9
--- /dev/null
+++ b/blop.lv2/pulse.ttl
@@ -0,0 +1,60 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:pulse
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "pulse" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.000001 ;
+ lv2:default 440.0 ;
+ lv2:name "Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0.5 ;
+ lv2:index 1 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Pulse Width" ;
+ lv2:symbol "pwidth" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 2 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1645> ,
+ <urn:ladspa:1646> ,
+ <urn:ladspa:1647> ,
+ <urn:ladspa:1648> ;
+ doap:name "Pulse" ;
+ lv2:documentation """
+<p>Generates an alias-free pulse wave at given input frequency and pulse width
+(duty).</p>
+""" .
diff --git a/blop.lv2/quantiser_100.ttl b/blop.lv2/quantiser_100.ttl
new file mode 100644
index 0000000..c2873aa
--- /dev/null
+++ b/blop.lv2/quantiser_100.ttl
@@ -0,0 +1,719 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+blop:quantiser_100
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "quantiser_100" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Minimum" ;
+ lv2:symbol "min"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Maximum" ;
+ lv2:symbol "max"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Match Range" ;
+ lv2:symbol "range"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:maximum 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Mode" ;
+ lv2:symbol "mode" ;
+ lv2:portProperty lv2:enumeration ,
+ lv2:integer ;
+ lv2:scalePoint [
+ rdfs:label "Extend" ;
+ rdf:value 0.0
+ ] , [
+ rdfs:label "Wrap" ;
+ rdf:value 1.0
+ ] , [
+ rdfs:label "Clip" ;
+ rdf:value 1.0
+ ]
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 20 ;
+ lv2:index 4 ;
+ lv2:maximum 20 ;
+ lv2:minimum 1 ;
+ lv2:name "Steps" ;
+ lv2:portProperty lv2:integer ;
+ lv2:symbol "steps"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Value 0" ;
+ lv2:symbol "val00"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 6 ;
+ lv2:name "Value 1" ;
+ lv2:symbol "val01"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 7 ;
+ lv2:name "Value 2" ;
+ lv2:symbol "val02"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 8 ;
+ lv2:name "Value 3" ;
+ lv2:symbol "val03"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 9 ;
+ lv2:name "Value 4" ;
+ lv2:symbol "val04"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 10 ;
+ lv2:name "Value 5" ;
+ lv2:symbol "val05"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 11 ;
+ lv2:name "Value 6" ;
+ lv2:symbol "val06"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 12 ;
+ lv2:name "Value 7" ;
+ lv2:symbol "val07"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 13 ;
+ lv2:name "Value 8" ;
+ lv2:symbol "val08"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 14 ;
+ lv2:name "Value 9" ;
+ lv2:symbol "val09"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 15 ;
+ lv2:name "Value 10" ;
+ lv2:symbol "val10"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 16 ;
+ lv2:name "Value 11" ;
+ lv2:symbol "val11"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 17 ;
+ lv2:name "Value 12" ;
+ lv2:symbol "val12"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 18 ;
+ lv2:name "Value 13" ;
+ lv2:symbol "val13"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 19 ;
+ lv2:name "Value 14" ;
+ lv2:symbol "val14"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 20 ;
+ lv2:name "Value 15" ;
+ lv2:symbol "val15"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 21 ;
+ lv2:name "Value 16" ;
+ lv2:symbol "val16"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 22 ;
+ lv2:name "Value 17" ;
+ lv2:symbol "val17"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 23 ;
+ lv2:name "Value 18" ;
+ lv2:symbol "val18"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 24 ;
+ lv2:name "Value 19" ;
+ lv2:symbol "val19"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 25 ;
+ lv2:name "Value 20" ;
+ lv2:symbol "val20"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 26 ;
+ lv2:name "Value 21" ;
+ lv2:symbol "val21"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 27 ;
+ lv2:name "Value 22" ;
+ lv2:symbol "val22"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 28 ;
+ lv2:name "Value 23" ;
+ lv2:symbol "val23"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 29 ;
+ lv2:name "Value 24" ;
+ lv2:symbol "val24"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 30 ;
+ lv2:name "Value 25" ;
+ lv2:symbol "val25"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 31 ;
+ lv2:name "Value 26" ;
+ lv2:symbol "val26"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 32 ;
+ lv2:name "Value 27" ;
+ lv2:symbol "val27"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 33 ;
+ lv2:name "Value 28" ;
+ lv2:symbol "val28"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 34 ;
+ lv2:name "Value 29" ;
+ lv2:symbol "val29"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 35 ;
+ lv2:name "Value 30" ;
+ lv2:symbol "val30"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 36 ;
+ lv2:name "Value 31" ;
+ lv2:symbol "val31"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 37 ;
+ lv2:name "Value 32" ;
+ lv2:symbol "val32"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 38 ;
+ lv2:name "Value 33" ;
+ lv2:symbol "val33"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 39 ;
+ lv2:name "Value 34" ;
+ lv2:symbol "val34"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 40 ;
+ lv2:name "Value 35" ;
+ lv2:symbol "val35"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 41 ;
+ lv2:name "Value 36" ;
+ lv2:symbol "val36"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 42 ;
+ lv2:name "Value 37" ;
+ lv2:symbol "val37"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 43 ;
+ lv2:name "Value 38" ;
+ lv2:symbol "val38"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 44 ;
+ lv2:name "Value 39" ;
+ lv2:symbol "val39"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 45 ;
+ lv2:name "Value 40" ;
+ lv2:symbol "val40"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 46 ;
+ lv2:name "Value 41" ;
+ lv2:symbol "val41"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 47 ;
+ lv2:name "Value 42" ;
+ lv2:symbol "val42"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 48 ;
+ lv2:name "Value 43" ;
+ lv2:symbol "val43"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 49 ;
+ lv2:name "Value 44" ;
+ lv2:symbol "val44"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 50 ;
+ lv2:name "Value 45" ;
+ lv2:symbol "val45"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 51 ;
+ lv2:name "Value 46" ;
+ lv2:symbol "val46"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 52 ;
+ lv2:name "Value 47" ;
+ lv2:symbol "val47"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 53 ;
+ lv2:name "Value 48" ;
+ lv2:symbol "val48"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 54 ;
+ lv2:name "Value 49" ;
+ lv2:symbol "val49"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 55 ;
+ lv2:name "Value 50" ;
+ lv2:symbol "val50"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 56 ;
+ lv2:name "Value 51" ;
+ lv2:symbol "val51"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 57 ;
+ lv2:name "Value 52" ;
+ lv2:symbol "val52"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 58 ;
+ lv2:name "Value 53" ;
+ lv2:symbol "val53"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 59 ;
+ lv2:name "Value 54" ;
+ lv2:symbol "val54"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 60 ;
+ lv2:name "Value 55" ;
+ lv2:symbol "val55"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 61 ;
+ lv2:name "Value 56" ;
+ lv2:symbol "val56"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 62 ;
+ lv2:name "Value 57" ;
+ lv2:symbol "val57"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 63 ;
+ lv2:name "Value 58" ;
+ lv2:symbol "val58"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 64 ;
+ lv2:name "Value 59" ;
+ lv2:symbol "val59"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 65 ;
+ lv2:name "Value 60" ;
+ lv2:symbol "val60"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 66 ;
+ lv2:name "Value 61" ;
+ lv2:symbol "val61"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 67 ;
+ lv2:name "Value 62" ;
+ lv2:symbol "val62"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 68 ;
+ lv2:name "Value 63" ;
+ lv2:symbol "val63"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 69 ;
+ lv2:name "Value 64" ;
+ lv2:symbol "val64"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 70 ;
+ lv2:name "Value 65" ;
+ lv2:symbol "val65"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 71 ;
+ lv2:name "Value 66" ;
+ lv2:symbol "val66"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 72 ;
+ lv2:name "Value 67" ;
+ lv2:symbol "val67"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 73 ;
+ lv2:name "Value 68" ;
+ lv2:symbol "val68"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 74 ;
+ lv2:name "Value 69" ;
+ lv2:symbol "val69"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 75 ;
+ lv2:name "Value 70" ;
+ lv2:symbol "val70"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 76 ;
+ lv2:name "Value 71" ;
+ lv2:symbol "val71"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 77 ;
+ lv2:name "Value 72" ;
+ lv2:symbol "val72"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 78 ;
+ lv2:name "Value 73" ;
+ lv2:symbol "val73"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 79 ;
+ lv2:name "Value 74" ;
+ lv2:symbol "val74"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 80 ;
+ lv2:name "Value 75" ;
+ lv2:symbol "val75"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 81 ;
+ lv2:name "Value 76" ;
+ lv2:symbol "val76"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 82 ;
+ lv2:name "Value 77" ;
+ lv2:symbol "val77"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 83 ;
+ lv2:name "Value 78" ;
+ lv2:symbol "val78"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 84 ;
+ lv2:name "Value 79" ;
+ lv2:symbol "val79"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 85 ;
+ lv2:name "Value 80" ;
+ lv2:symbol "val80"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 86 ;
+ lv2:name "Value 81" ;
+ lv2:symbol "val81"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 87 ;
+ lv2:name "Value 82" ;
+ lv2:symbol "val82"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 88 ;
+ lv2:name "Value 83" ;
+ lv2:symbol "val83"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 89 ;
+ lv2:name "Value 84" ;
+ lv2:symbol "val84"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 90 ;
+ lv2:name "Value 85" ;
+ lv2:symbol "val85"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 91 ;
+ lv2:name "Value 86" ;
+ lv2:symbol "val86"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 92 ;
+ lv2:name "Value 87" ;
+ lv2:symbol "val87"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 93 ;
+ lv2:name "Value 88" ;
+ lv2:symbol "val88"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 94 ;
+ lv2:name "Value 89" ;
+ lv2:symbol "val89"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 95 ;
+ lv2:name "Value 90" ;
+ lv2:symbol "val90"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 96 ;
+ lv2:name "Value 91" ;
+ lv2:symbol "val91"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 97 ;
+ lv2:name "Value 92" ;
+ lv2:symbol "val92"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 98 ;
+ lv2:name "Value 93" ;
+ lv2:symbol "val93"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 99 ;
+ lv2:name "Value 94" ;
+ lv2:symbol "val94"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 100 ;
+ lv2:name "Value 95" ;
+ lv2:symbol "val95"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 101 ;
+ lv2:name "Value 96" ;
+ lv2:symbol "val96"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 102 ;
+ lv2:name "Value 97" ;
+ lv2:symbol "val97"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 103 ;
+ lv2:name "Value 98" ;
+ lv2:symbol "val98"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 104 ;
+ lv2:name "Value 99" ;
+ lv2:symbol "val99"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 105 ;
+ lv2:name "Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 106 ;
+ lv2:name "Quantised Output" ;
+ lv2:symbol "out"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 107 ;
+ lv2:name "Output Changed" ;
+ lv2:symbol "changed"
+ ] ;
+ dct:replaces <urn:ladspa:2029> ;
+ doap:name "Quantiser (100 Steps)" ;
+ lv2:documentation """
+<p>Quantises a signal to a set of arbitrary values within a range.</p>
+
+<p>Match Range determines the distance from the quantised value that the input
+can deviate before being altered. This allows small variations in input to get
+through unmolested. If it is set to 0 the input is quantised to the nearest
+exact match.</p>
+
+<p>Mode is one of Extend (0), Wrap (1) or Clip (2).</p>
+
+<p>Steps is the number of quantisation steps to use (up to a maximum of
+100).</p>
+
+<p>For example, given the following settings:</p>
+<ul>
+ <li>Range Minimum = 0.0</li>
+ <li>Range Maximum = 12.0</li>
+ <li>Match Range = 0.0</li>
+ <li>Steps = 4</li>
+ <li>Quantisation Values 3, 5, 7 and 10</li>
+</ul>
+
+<p>and an input that is a line from -24 to 24, the output will be:</p>
+<ul>
+ <li>Extend: -26, -21, -19, -17, -14, -9, -7, -5, -2, 3, 5, 7, 10, 15, 17, 19,
+ 22</li>
+ <li>Wrap: 10, 3, 5, 7, 10, 3, 5, 7, 10, 3, 5, 7, 10, 3, 5, 7, 10</li>
+ <li>Clip: 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 7, 10, 10, 10, 10, 10</li>
+</ul>
+
+<p>The quantisation values should all be within the range minimum and maximum
+for it to work!</p>
+""" .
diff --git a/blop.lv2/quantiser_20.ttl b/blop.lv2/quantiser_20.ttl
new file mode 100644
index 0000000..095716f
--- /dev/null
+++ b/blop.lv2/quantiser_20.ttl
@@ -0,0 +1,239 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+blop:quantiser_20
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "quantiser_20" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Minimum" ;
+ lv2:symbol "min"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Maximum" ;
+ lv2:symbol "max"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Match Range" ;
+ lv2:symbol "range"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:maximum 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Mode" ;
+ lv2:symbol "mode" ;
+ lv2:portProperty lv2:enumeration ,
+ lv2:integer ;
+ lv2:scalePoint [
+ rdfs:label "Extend" ;
+ rdf:value 0.0
+ ] , [
+ rdfs:label "Wrap" ;
+ rdf:value 1.0
+ ] , [
+ rdfs:label "Clip" ;
+ rdf:value 1.0
+ ]
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 20 ;
+ lv2:index 4 ;
+ lv2:maximum 20 ;
+ lv2:minimum 1 ;
+ lv2:name "Steps" ;
+ lv2:portProperty lv2:integer ;
+ lv2:symbol "steps"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Value 0" ;
+ lv2:symbol "val00"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 6 ;
+ lv2:name "Value 1" ;
+ lv2:symbol "val01"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 7 ;
+ lv2:name "Value 2" ;
+ lv2:symbol "val02"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 8 ;
+ lv2:name "Value 3" ;
+ lv2:symbol "val03"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 9 ;
+ lv2:name "Value 4" ;
+ lv2:symbol "val04"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 10 ;
+ lv2:name "Value 5" ;
+ lv2:symbol "val05"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 11 ;
+ lv2:name "Value 6" ;
+ lv2:symbol "val06"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 12 ;
+ lv2:name "Value 7" ;
+ lv2:symbol "val07"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 13 ;
+ lv2:name "Value 8" ;
+ lv2:symbol "val08"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 14 ;
+ lv2:name "Value 9" ;
+ lv2:symbol "val09"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 15 ;
+ lv2:name "Value 10" ;
+ lv2:symbol "val10"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 16 ;
+ lv2:name "Value 11" ;
+ lv2:symbol "val11"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 17 ;
+ lv2:name "Value 12" ;
+ lv2:symbol "val12"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 18 ;
+ lv2:name "Value 13" ;
+ lv2:symbol "val13"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 19 ;
+ lv2:name "Value 14" ;
+ lv2:symbol "val14"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 20 ;
+ lv2:name "Value 15" ;
+ lv2:symbol "val15"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 21 ;
+ lv2:name "Value 16" ;
+ lv2:symbol "val16"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 22 ;
+ lv2:name "Value 17" ;
+ lv2:symbol "val17"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 23 ;
+ lv2:name "Value 18" ;
+ lv2:symbol "val18"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 24 ;
+ lv2:name "Value 19" ;
+ lv2:symbol "val19"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 25 ;
+ lv2:name "Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 26 ;
+ lv2:name "Quantised Output" ;
+ lv2:symbol "out"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 27 ;
+ lv2:name "Output Changed" ;
+ lv2:symbol "changed"
+ ] ;
+ dct:replaces <urn:ladspa:2027> ;
+ doap:name "Quantiser (20 Steps)" ;
+ lv2:documentation """
+<p>Quantises a signal to a set of arbitrary values within a range.</p>
+
+<p>Match Range determines the distance from the quantised value that the input
+can deviate before being altered. This allows small variations in input to get
+through unmolested. If it is set to 0 the input is quantised to the nearest
+exact match.</p>
+
+<p>Mode is one of Extend (0), Wrap (1) or Clip (2).</p>
+
+<p>Steps is the number of quantisation steps to use (up to a maximum of
+20).</p>
+
+<p>For example, given the following settings:</p>
+<ul>
+ <li>Range Minimum = 0.0</li>
+ <li>Range Maximum = 12.0</li>
+ <li>Match Range = 0.0</li>
+ <li>Steps = 4</li>
+ <li>Quantisation Values 3, 5, 7 and 10</li>
+</ul>
+
+<p>and an input that is a line from -24 to 24, the output will be:</p>
+<ul>
+ <li>Extend: -26, -21, -19, -17, -14, -9, -7, -5, -2, 3, 5, 7, 10, 15, 17, 19,
+ 22</li>
+ <li>Wrap: 10, 3, 5, 7, 10, 3, 5, 7, 10, 3, 5, 7, 10, 3, 5, 7, 10</li>
+ <li>Clip: 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 7, 10, 10, 10, 10, 10</li>
+</ul>
+
+<p>The quantisation values should all be within the range minimum and maximum
+for it to work!</p>
+""" .
diff --git a/blop.lv2/quantiser_50.ttl b/blop.lv2/quantiser_50.ttl
new file mode 100644
index 0000000..901865c
--- /dev/null
+++ b/blop.lv2/quantiser_50.ttl
@@ -0,0 +1,419 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+blop:quantiser_50
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "quantiser_50" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Minimum" ;
+ lv2:symbol "min"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Maximum" ;
+ lv2:symbol "max"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Match Range" ;
+ lv2:symbol "range"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:maximum 2 ;
+ lv2:minimum 0 ;
+ lv2:name "Mode" ;
+ lv2:symbol "mode" ;
+ lv2:portProperty lv2:enumeration ,
+ lv2:integer ;
+ lv2:scalePoint [
+ rdfs:label "Extend" ;
+ rdf:value 0.0
+ ] , [
+ rdfs:label "Wrap" ;
+ rdf:value 1.0
+ ] , [
+ rdfs:label "Clip" ;
+ rdf:value 1.0
+ ]
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 20 ;
+ lv2:index 4 ;
+ lv2:maximum 20 ;
+ lv2:minimum 1 ;
+ lv2:name "Steps" ;
+ lv2:portProperty lv2:integer ;
+ lv2:symbol "steps"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Value 0" ;
+ lv2:symbol "val00"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 6 ;
+ lv2:name "Value 1" ;
+ lv2:symbol "val01"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 7 ;
+ lv2:name "Value 2" ;
+ lv2:symbol "val02"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 8 ;
+ lv2:name "Value 3" ;
+ lv2:symbol "val03"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 9 ;
+ lv2:name "Value 4" ;
+ lv2:symbol "val04"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 10 ;
+ lv2:name "Value 5" ;
+ lv2:symbol "val05"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 11 ;
+ lv2:name "Value 6" ;
+ lv2:symbol "val06"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 12 ;
+ lv2:name "Value 7" ;
+ lv2:symbol "val07"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 13 ;
+ lv2:name "Value 8" ;
+ lv2:symbol "val08"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 14 ;
+ lv2:name "Value 9" ;
+ lv2:symbol "val09"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 15 ;
+ lv2:name "Value 10" ;
+ lv2:symbol "val10"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 16 ;
+ lv2:name "Value 11" ;
+ lv2:symbol "val11"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 17 ;
+ lv2:name "Value 12" ;
+ lv2:symbol "val12"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 18 ;
+ lv2:name "Value 13" ;
+ lv2:symbol "val13"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 19 ;
+ lv2:name "Value 14" ;
+ lv2:symbol "val14"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 20 ;
+ lv2:name "Value 15" ;
+ lv2:symbol "val15"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 21 ;
+ lv2:name "Value 16" ;
+ lv2:symbol "val16"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 22 ;
+ lv2:name "Value 17" ;
+ lv2:symbol "val17"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 23 ;
+ lv2:name "Value 18" ;
+ lv2:symbol "val18"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 24 ;
+ lv2:name "Value 19" ;
+ lv2:symbol "val19"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 25 ;
+ lv2:name "Value 20" ;
+ lv2:symbol "val20"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 26 ;
+ lv2:name "Value 21" ;
+ lv2:symbol "val21"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 27 ;
+ lv2:name "Value 22" ;
+ lv2:symbol "val22"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 28 ;
+ lv2:name "Value 23" ;
+ lv2:symbol "val23"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 29 ;
+ lv2:name "Value 24" ;
+ lv2:symbol "val24"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 30 ;
+ lv2:name "Value 25" ;
+ lv2:symbol "val25"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 31 ;
+ lv2:name "Value 26" ;
+ lv2:symbol "val26"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 32 ;
+ lv2:name "Value 27" ;
+ lv2:symbol "val27"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 33 ;
+ lv2:name "Value 28" ;
+ lv2:symbol "val28"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 34 ;
+ lv2:name "Value 29" ;
+ lv2:symbol "val29"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 35 ;
+ lv2:name "Value 30" ;
+ lv2:symbol "val30"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 36 ;
+ lv2:name "Value 31" ;
+ lv2:symbol "val31"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 37 ;
+ lv2:name "Value 32" ;
+ lv2:symbol "val32"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 38 ;
+ lv2:name "Value 33" ;
+ lv2:symbol "val33"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 39 ;
+ lv2:name "Value 34" ;
+ lv2:symbol "val34"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 40 ;
+ lv2:name "Value 35" ;
+ lv2:symbol "val35"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 41 ;
+ lv2:name "Value 36" ;
+ lv2:symbol "val36"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 42 ;
+ lv2:name "Value 37" ;
+ lv2:symbol "val37"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 43 ;
+ lv2:name "Value 38" ;
+ lv2:symbol "val38"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 44 ;
+ lv2:name "Value 39" ;
+ lv2:symbol "val39"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 45 ;
+ lv2:name "Value 40" ;
+ lv2:symbol "val40"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 46 ;
+ lv2:name "Value 41" ;
+ lv2:symbol "val41"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 47 ;
+ lv2:name "Value 42" ;
+ lv2:symbol "val42"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 48 ;
+ lv2:name "Value 43" ;
+ lv2:symbol "val43"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 49 ;
+ lv2:name "Value 44" ;
+ lv2:symbol "val44"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 50 ;
+ lv2:name "Value 45" ;
+ lv2:symbol "val45"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 51 ;
+ lv2:name "Value 46" ;
+ lv2:symbol "val46"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 52 ;
+ lv2:name "Value 47" ;
+ lv2:symbol "val47"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 53 ;
+ lv2:name "Value 48" ;
+ lv2:symbol "val48"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 54 ;
+ lv2:name "Value 49" ;
+ lv2:symbol "val49"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 55 ;
+ lv2:name "Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 56 ;
+ lv2:name "Quantised Output" ;
+ lv2:symbol "out"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 57 ;
+ lv2:name "Output Changed" ;
+ lv2:symbol "changed"
+ ] ;
+ dct:replaces <urn:ladspa:2028> ;
+ doap:name "Quantiser (50 Steps)" ;
+ lv2:documentation """
+<p>Quantises a signal to a set of arbitrary values within a range.</p>
+
+<p>Match Range determines the distance from the quantised value that the input
+can deviate before being altered. This allows small variations in input to get
+through unmolested. If it is set to 0 the input is quantised to the nearest
+exact match.</p>
+
+<p>Mode is one of Extend (0), Wrap (1) or Clip (2).</p>
+
+<p>Steps is the number of quantisation steps to use (up to a maximum of
+50).</p>
+
+<p>For example, given the following settings:</p>
+<ul>
+ <li>Range Minimum = 0.0</li>
+ <li>Range Maximum = 12.0</li>
+ <li>Match Range = 0.0</li>
+ <li>Steps = 4</li>
+ <li>Quantisation Values 3, 5, 7 and 10</li>
+</ul>
+
+<p>and an input that is a line from -24 to 24, the output will be:</p>
+<ul>
+ <li>Extend: -26, -21, -19, -17, -14, -9, -7, -5, -2, 3, 5, 7, 10, 15, 17, 19,
+ 22</li>
+ <li>Wrap: 10, 3, 5, 7, 10, 3, 5, 7, 10, 3, 5, 7, 10, 3, 5, 7, 10</li>
+ <li>Clip: 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 7, 10, 10, 10, 10, 10</li>
+</ul>
+
+<p>The quantisation values should all be within the range minimum and maximum
+for it to work!</p>
+""" .
diff --git a/blop.lv2/random.ttl b/blop.lv2/random.ttl
new file mode 100644
index 0000000..8b810c9
--- /dev/null
+++ b/blop.lv2/random.ttl
@@ -0,0 +1,64 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:random
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "random" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.000001 ;
+ lv2:default 440.0 ;
+ lv2:name "Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 1 ;
+ lv2:index 1 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Smoothness" ;
+ lv2:symbol "smooth" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ,
+ lv2:Port ;
+ lv2:index 2 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1661> ,
+ <urn:ladspa:1662> ,
+ <urn:ladspa:1663> ,
+ <urn:ladspa:1664> ;
+ doap:name "Random Wave" ;
+ lv2:documentation """
+<p>Generates a random waveform of varying frequency and smoothness. The
+frequency determines how often the output changes. The smoothness, how quickly
+a transition occurs.</p>
+
+<p>The output varies between &#177;1, with an even distribution.</p>
+""" .
diff --git a/blop.lv2/ratio.ttl b/blop.lv2/ratio.ttl
new file mode 100644
index 0000000..04e4d90
--- /dev/null
+++ b/blop.lv2/ratio.ttl
@@ -0,0 +1,52 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:ratio
+ a lv2:Plugin ,
+ lv2:UtilityPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "ratio" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:name "Numerator" ;
+ lv2:symbol "numerator" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 1 ;
+ lv2:name "Denominator" ;
+ lv2:symbol "denominator" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 2 ;
+ lv2:name "Ratio" ;
+ lv2:symbol "ratio" ;
+ morph:supportsType lv2:CVPort
+ ] ;
+ dct:replaces <urn:ladspa:2034> ,
+ <urn:ladspa:2035> ,
+ <urn:ladspa:2036> ,
+ <urn:ladspa:2037> ;
+ doap:name "Ratio" ;
+ lv2:documentation """
+<p>Get the ratio between two signals.</p>
+<p>To avoid divisions by zero, 0 is treated as a really small number.</p>
+""" .
diff --git a/blop.lv2/sawtooth.ttl b/blop.lv2/sawtooth.ttl
new file mode 100644
index 0000000..04dec12
--- /dev/null
+++ b/blop.lv2/sawtooth.ttl
@@ -0,0 +1,46 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:sawtooth
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "sawtooth" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.000001 ;
+ lv2:default 440.0 ;
+ lv2:name "Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 1 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1642> ,
+ <urn:ladspa:1643> ;
+ doap:name "Sawtooth" ;
+ lv2:documentation """
+<p>Generates an alias-free sawtooth wave at given input frequency.</p>
+""" .
diff --git a/blop.lv2/sequencer_16.ttl b/blop.lv2/sequencer_16.ttl
new file mode 100644
index 0000000..97473a6
--- /dev/null
+++ b/blop.lv2/sequencer_16.ttl
@@ -0,0 +1,167 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+
+blop:sequencer_16
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "sequencer_16" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Step Trigger" ;
+ lv2:portProperty lv2:toggled ,
+ <http://lv2plug.in/ns/ext/port-props#trigger> ;
+ lv2:symbol "trigger"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 16 ;
+ lv2:index 2 ;
+ lv2:maximum 16 ;
+ lv2:minimum 1 ;
+ lv2:name "Loop Steps" ;
+ lv2:portProperty lv2:integer ;
+ lv2:symbol "steps"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Reset on Gate Close" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "reset"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 4 ;
+ lv2:name "Closed Gate Value" ;
+ lv2:symbol "closed_val"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Value 0" ;
+ lv2:symbol "val00"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 6 ;
+ lv2:name "Value 1" ;
+ lv2:symbol "val01"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 7 ;
+ lv2:name "Value 2" ;
+ lv2:symbol "val02"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 8 ;
+ lv2:name "Value 3" ;
+ lv2:symbol "val03"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 9 ;
+ lv2:name "Value 4" ;
+ lv2:symbol "val04"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 10 ;
+ lv2:name "Value 5" ;
+ lv2:symbol "val05"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 11 ;
+ lv2:name "Value 6" ;
+ lv2:symbol "val06"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 12 ;
+ lv2:name "Value 7" ;
+ lv2:symbol "val07"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 13 ;
+ lv2:name "Value 8" ;
+ lv2:symbol "val08"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 14 ;
+ lv2:name "Value 9" ;
+ lv2:symbol "val09"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 15 ;
+ lv2:name "Value 10" ;
+ lv2:symbol "val10"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 16 ;
+ lv2:name "Value 11" ;
+ lv2:symbol "val11"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 17 ;
+ lv2:name "Value 12" ;
+ lv2:symbol "val12"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 18 ;
+ lv2:name "Value 13" ;
+ lv2:symbol "val13"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 19 ;
+ lv2:name "Value 14" ;
+ lv2:symbol "val14"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 20 ;
+ lv2:name "Value 15" ;
+ lv2:symbol "val15"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 21 ;
+ lv2:name "Value Out" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1677> ;
+ doap:name "16 Step Sequencer" ;
+ lv2:documentation """
+<p>Simulates an analogue step sequencer. The sequencer stores a number of
+values which are stepped through using a trigger when the gate is open, looping
+after a given number of steps. This variant has 16 steps.</p>
+
+<p>When the gate is closed, the sequencer returns to the start. Output when
+the gate is closed can be set to a default value. If not, it will just output
+the last value reached before the gate was closed.</p>
+""" .
diff --git a/blop.lv2/sequencer_32.ttl b/blop.lv2/sequencer_32.ttl
new file mode 100644
index 0000000..ae289f2
--- /dev/null
+++ b/blop.lv2/sequencer_32.ttl
@@ -0,0 +1,263 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+
+blop:sequencer_32
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "sequencer_32" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Step Trigger" ;
+ lv2:portProperty lv2:toggled ,
+ <http://lv2plug.in/ns/ext/port-props#trigger> ;
+ lv2:symbol "trigger"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 32 ;
+ lv2:index 2 ;
+ lv2:maximum 32 ;
+ lv2:minimum 1 ;
+ lv2:name "Loop Steps" ;
+ lv2:portProperty lv2:integer ;
+ lv2:symbol "steps"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Reset on Gate Close" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "reset"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 4 ;
+ lv2:name "Closed Gate Value" ;
+ lv2:symbol "closed_val"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Value 0" ;
+ lv2:symbol "val00"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 6 ;
+ lv2:name "Value 1" ;
+ lv2:symbol "val01"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 7 ;
+ lv2:name "Value 2" ;
+ lv2:symbol "val02"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 8 ;
+ lv2:name "Value 3" ;
+ lv2:symbol "val03"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 9 ;
+ lv2:name "Value 4" ;
+ lv2:symbol "val04"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 10 ;
+ lv2:name "Value 5" ;
+ lv2:symbol "val05"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 11 ;
+ lv2:name "Value 6" ;
+ lv2:symbol "val06"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 12 ;
+ lv2:name "Value 7" ;
+ lv2:symbol "val07"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 13 ;
+ lv2:name "Value 8" ;
+ lv2:symbol "val08"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 14 ;
+ lv2:name "Value 9" ;
+ lv2:symbol "val09"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 15 ;
+ lv2:name "Value 10" ;
+ lv2:symbol "val10"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 16 ;
+ lv2:name "Value 11" ;
+ lv2:symbol "val11"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 17 ;
+ lv2:name "Value 12" ;
+ lv2:symbol "val12"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 18 ;
+ lv2:name "Value 13" ;
+ lv2:symbol "val13"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 19 ;
+ lv2:name "Value 14" ;
+ lv2:symbol "val14"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 20 ;
+ lv2:name "Value 15" ;
+ lv2:symbol "val15"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 21 ;
+ lv2:name "Value 16" ;
+ lv2:symbol "val16"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 22 ;
+ lv2:name "Value 17" ;
+ lv2:symbol "val17"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 23 ;
+ lv2:name "Value 18" ;
+ lv2:symbol "val18"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 24 ;
+ lv2:name "Value 19" ;
+ lv2:symbol "val19"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 25 ;
+ lv2:name "Value 20" ;
+ lv2:symbol "val20"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 26 ;
+ lv2:name "Value 21" ;
+ lv2:symbol "val21"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 27 ;
+ lv2:name "Value 22" ;
+ lv2:symbol "val22"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 28 ;
+ lv2:name "Value 23" ;
+ lv2:symbol "val23"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 29 ;
+ lv2:name "Value 24" ;
+ lv2:symbol "val24"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 30 ;
+ lv2:name "Value 25" ;
+ lv2:symbol "val25"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 31 ;
+ lv2:name "Value 26" ;
+ lv2:symbol "val26"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 32 ;
+ lv2:name "Value 27" ;
+ lv2:symbol "val27"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 33 ;
+ lv2:name "Value 28" ;
+ lv2:symbol "val28"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 34 ;
+ lv2:name "Value 29" ;
+ lv2:symbol "val29"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 35 ;
+ lv2:name "Value 30" ;
+ lv2:symbol "val30"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 36 ;
+ lv2:name "Value 31" ;
+ lv2:symbol "val31"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 37 ;
+ lv2:name "Value Out" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1676> ;
+ doap:name "32 Step Sequencer" ;
+ lv2:documentation """
+<p>Simulates an analogue step sequencer. The sequencer stores a number of
+values which are stepped through using a trigger when the gate is open, looping
+after a given number of steps. This variant has 32 steps.</p>
+
+<p>When the gate is closed, the sequencer returns to the start. Output when
+the gate is closed can be set to a default value. If not, it will just output
+the last value reached before the gate was closed.</p>
+""" .
diff --git a/blop.lv2/sequencer_64.ttl b/blop.lv2/sequencer_64.ttl
new file mode 100644
index 0000000..3a3ea0f
--- /dev/null
+++ b/blop.lv2/sequencer_64.ttl
@@ -0,0 +1,455 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+
+blop:sequencer_64
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "sequencer_64" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Step Trigger" ;
+ lv2:portProperty lv2:toggled ,
+ <http://lv2plug.in/ns/ext/port-props#trigger> ;
+ lv2:symbol "trigger"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 32 ;
+ lv2:index 2 ;
+ lv2:maximum 32 ;
+ lv2:minimum 1 ;
+ lv2:name "Loop Steps" ;
+ lv2:portProperty lv2:integer ;
+ lv2:symbol "steps"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:default 0 ;
+ lv2:index 3 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Reset on Gate Close" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "reset"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 4 ;
+ lv2:name "Closed Gate Value" ;
+ lv2:symbol "closed_val"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Value 0" ;
+ lv2:symbol "val00"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 6 ;
+ lv2:name "Value 1" ;
+ lv2:symbol "val01"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 7 ;
+ lv2:name "Value 2" ;
+ lv2:symbol "val02"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 8 ;
+ lv2:name "Value 3" ;
+ lv2:symbol "val03"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 9 ;
+ lv2:name "Value 4" ;
+ lv2:symbol "val04"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 10 ;
+ lv2:name "Value 5" ;
+ lv2:symbol "val05"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 11 ;
+ lv2:name "Value 6" ;
+ lv2:symbol "val06"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 12 ;
+ lv2:name "Value 7" ;
+ lv2:symbol "val07"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 13 ;
+ lv2:name "Value 8" ;
+ lv2:symbol "val08"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 14 ;
+ lv2:name "Value 9" ;
+ lv2:symbol "val09"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 15 ;
+ lv2:name "Value 10" ;
+ lv2:symbol "val10"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 16 ;
+ lv2:name "Value 11" ;
+ lv2:symbol "val11"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 17 ;
+ lv2:name "Value 12" ;
+ lv2:symbol "val12"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 18 ;
+ lv2:name "Value 13" ;
+ lv2:symbol "val13"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 19 ;
+ lv2:name "Value 14" ;
+ lv2:symbol "val14"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 20 ;
+ lv2:name "Value 15" ;
+ lv2:symbol "val15"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 21 ;
+ lv2:name "Value 16" ;
+ lv2:symbol "val16"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 22 ;
+ lv2:name "Value 17" ;
+ lv2:symbol "val17"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 23 ;
+ lv2:name "Value 18" ;
+ lv2:symbol "val18"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 24 ;
+ lv2:name "Value 19" ;
+ lv2:symbol "val19"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 25 ;
+ lv2:name "Value 20" ;
+ lv2:symbol "val20"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 26 ;
+ lv2:name "Value 21" ;
+ lv2:symbol "val21"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 27 ;
+ lv2:name "Value 22" ;
+ lv2:symbol "val22"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 28 ;
+ lv2:name "Value 23" ;
+ lv2:symbol "val23"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 29 ;
+ lv2:name "Value 24" ;
+ lv2:symbol "val24"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 30 ;
+ lv2:name "Value 25" ;
+ lv2:symbol "val25"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 31 ;
+ lv2:name "Value 26" ;
+ lv2:symbol "val26"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 32 ;
+ lv2:name "Value 27" ;
+ lv2:symbol "val27"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 33 ;
+ lv2:name "Value 28" ;
+ lv2:symbol "val28"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 34 ;
+ lv2:name "Value 29" ;
+ lv2:symbol "val29"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 35 ;
+ lv2:name "Value 30" ;
+ lv2:symbol "val30"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 36 ;
+ lv2:name "Value 31" ;
+ lv2:symbol "val31"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 37 ;
+ lv2:name "Value 32" ;
+ lv2:symbol "val32"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 38 ;
+ lv2:name "Value 33" ;
+ lv2:symbol "val33"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 39 ;
+ lv2:name "Value 34" ;
+ lv2:symbol "val34"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 40 ;
+ lv2:name "Value 35" ;
+ lv2:symbol "val35"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 41 ;
+ lv2:name "Value 36" ;
+ lv2:symbol "val36"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 42 ;
+ lv2:name "Value 37" ;
+ lv2:symbol "val37"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 43 ;
+ lv2:name "Value 38" ;
+ lv2:symbol "val38"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 44 ;
+ lv2:name "Value 39" ;
+ lv2:symbol "val39"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 45 ;
+ lv2:name "Value 40" ;
+ lv2:symbol "val40"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 46 ;
+ lv2:name "Value 41" ;
+ lv2:symbol "val41"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 47 ;
+ lv2:name "Value 42" ;
+ lv2:symbol "val42"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 48 ;
+ lv2:name "Value 43" ;
+ lv2:symbol "val43"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 49 ;
+ lv2:name "Value 44" ;
+ lv2:symbol "val44"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 50 ;
+ lv2:name "Value 45" ;
+ lv2:symbol "val45"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 51 ;
+ lv2:name "Value 46" ;
+ lv2:symbol "val46"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 52 ;
+ lv2:name "Value 47" ;
+ lv2:symbol "val47"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 53 ;
+ lv2:name "Value 48" ;
+ lv2:symbol "val48"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 54 ;
+ lv2:name "Value 49" ;
+ lv2:symbol "val49"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 55 ;
+ lv2:name "Value 50" ;
+ lv2:symbol "val50"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 56 ;
+ lv2:name "Value 51" ;
+ lv2:symbol "val51"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 57 ;
+ lv2:name "Value 52" ;
+ lv2:symbol "val52"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 58 ;
+ lv2:name "Value 53" ;
+ lv2:symbol "val53"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 59 ;
+ lv2:name "Value 54" ;
+ lv2:symbol "val54"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 60 ;
+ lv2:name "Value 55" ;
+ lv2:symbol "val55"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 61 ;
+ lv2:name "Value 56" ;
+ lv2:symbol "val56"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 62 ;
+ lv2:name "Value 57" ;
+ lv2:symbol "val57"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 63 ;
+ lv2:name "Value 58" ;
+ lv2:symbol "val58"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 64 ;
+ lv2:name "Value 59" ;
+ lv2:symbol "val59"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 65 ;
+ lv2:name "Value 60" ;
+ lv2:symbol "val60"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 66 ;
+ lv2:name "Value 61" ;
+ lv2:symbol "val61"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 67 ;
+ lv2:name "Value 62" ;
+ lv2:symbol "val62"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ;
+ lv2:index 68 ;
+ lv2:name "Value 63" ;
+ lv2:symbol "val63"
+ ] , [
+ a lv2:CVPort ,
+ lv2:OutputPort ;
+ lv2:index 69 ;
+ lv2:name "Value Out" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1675> ;
+ doap:name "64 Step Sequencer" ;
+ lv2:documentation """
+<p>Simulates an analogue step sequencer. The sequencer stores a number of
+values which are stepped through using a trigger when the gate is open, looping
+after a given number of steps. This variant has 64 steps.</p>
+
+<p>When the gate is closed, the sequencer returns to the start. Output when
+the gate is closed can be set to a default value. If not, it will just output
+the last value reached before the gate was closed.</p>
+""" .
diff --git a/blop.lv2/square.ttl b/blop.lv2/square.ttl
new file mode 100644
index 0000000..77f30bc
--- /dev/null
+++ b/blop.lv2/square.ttl
@@ -0,0 +1,46 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:square
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "square" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.000001 ;
+ lv2:default 440.0 ;
+ lv2:name "Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 1 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1643> ,
+ <urn:ladspa:1644> ;
+ doap:name "Square" ;
+ lv2:documentation """
+<p>Generates an alias-free square wave at given input frequency.</p>
+""" .
diff --git a/blop.lv2/sum.ttl b/blop.lv2/sum.ttl
new file mode 100644
index 0000000..66ea88d
--- /dev/null
+++ b/blop.lv2/sum.ttl
@@ -0,0 +1,50 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:sum
+ a lv2:Plugin ,
+ lv2:UtilityPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "sum" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:name "Input 1" ;
+ lv2:symbol "in1" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 1 ;
+ lv2:name "Input 2" ;
+ lv2:symbol "in2" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:ControlPort ,
+ lv2:OutputPort ,
+ morph:AutoMorphPort ;
+ lv2:index 2 ;
+ lv2:name "Sum" ;
+ lv2:symbol "sum" ;
+ morph:supportsType lv2:CVPort
+ ] ;
+ dct:replaces <urn:ladspa:1665> ,
+ <urn:ladspa:1666> ,
+ <urn:ladspa:1667> ;
+ doap:name "Sum" ;
+ lv2:documentation """
+<p>Add two signals.</p>
+""" .
diff --git a/blop.lv2/sync_pulse.ttl b/blop.lv2/sync_pulse.ttl
new file mode 100644
index 0000000..47051e2
--- /dev/null
+++ b/blop.lv2/sync_pulse.ttl
@@ -0,0 +1,63 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:sync_pulse
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "sync_pulse" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 16 ;
+ lv2:index 0 ;
+ lv2:maximum 64 ;
+ lv2:minimum 0 ;
+ lv2:name "Frequency" ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0.5 ;
+ lv2:index 1 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Pulse Width" ;
+ lv2:symbol "pwidth" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 2 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 3 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:2023> ,
+ <urn:ladspa:2024> ;
+ doap:name "Clock Pulse" ;
+ lv2:documentation """
+<p>Same as the <a href="http://drobilla.net/plugins/blop/sync_square">Clock
+Oscillator</a>, but with pulse-width modulation.</p>
+""" .
diff --git a/blop.lv2/sync_square.ttl b/blop.lv2/sync_square.ttl
new file mode 100644
index 0000000..873619f
--- /dev/null
+++ b/blop.lv2/sync_square.ttl
@@ -0,0 +1,55 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:sync_square
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "sync_square" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 16 ;
+ lv2:index 0 ;
+ lv2:maximum 64 ;
+ lv2:minimum 0 ;
+ lv2:name "Frequency" ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 1 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 2 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1678> ,
+ <urn:ladspa:1679> ;
+ doap:name "Clock Square" ;
+ lv2:documentation """
+<p>Generates a non-bandlimited simple square waveform for use as a clock.
+Useful for triggering the sequencers, or anything else that uses a clock
+signal.</p>
+
+<p>When the gate is closed, it outputs silence, and the phase is reset.</p>
+""" .
diff --git a/blop.lv2/tracker.ttl b/blop.lv2/tracker.ttl
new file mode 100644
index 0000000..b62d811
--- /dev/null
+++ b/blop.lv2/tracker.ttl
@@ -0,0 +1,122 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:tracker
+ a lv2:Plugin ;
+ lv2:project blop: ;
+ lv2:symbol "tracker" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:CVPort ,
+ lv2:InputPort ;
+ lv2:index 0 ;
+ lv2:name "Gate" ;
+ lv2:portProperty lv2:toggled ;
+ lv2:symbol "gate"
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 100 ;
+ lv2:index 1 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.00001 ;
+ lv2:name "High Attack Rate" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "hattack" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 100 ;
+ lv2:index 2 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.00001 ;
+ lv2:name "High Decay Rate" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "hdecay" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 100 ;
+ lv2:index 3 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.00001 ;
+ lv2:name "Low Attack Rate" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "lattack" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 100 ;
+ lv2:index 4 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.00001 ;
+ lv2:name "Low Decay Rate" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "ldecay" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:AudioPort ,
+ lv2:InputPort ;
+ lv2:index 5 ;
+ lv2:name "Input" ;
+ lv2:symbol "in"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 6 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:2025> ,
+ <urn:ladspa:2026> ;
+ doap:name "Tracker" ;
+ lv2:documentation """
+<p>This tracks an incoming signal and outputs the result.</p>
+<p>The rate controls tell the plugin how quickly to respond to a change in
+input. Low values will mean a slower response - a setting of 0 will hold the
+last value, and a very high value will track the input exactly.</p>
+<p>Attack rate is how quickly an upward change is tracked, and Decay for
+downward changes. There are two pairs of these - the one used depends on the
+level of the Gate.</p>
+<p>Example uses:</p>
+<ul>
+ <li>
+ <em>An envelope tracker</em>- use the &#8216;Gate Low&#8217; track rates with
+ the Gate held low, and run the output through a full-wave rectifier (an ABS()
+ operator) to get an estimate of the level of a signal.</li>
+ <li>
+ <em>Sample and Hold</em>- run a narrow pulse wave into the gate, set the
+ &#8216;Gate High&#8217; rates to maximum, and the &#8216;Gate Low&#8217;
+ rates set to 0.</li>
+ <li>
+ <em>Track and Hold</em>- run a variable pulse wave into the gate, set the
+ &#8216;Gate High&#8217; rates to 0, and the &#8216;Gate Low&#8217; rates set
+ high. Varying the pulsewidth will vary the time the input is tracked, and
+ when it is held.</li>
+</ul>
+""" .
diff --git a/blop.lv2/triangle.ttl b/blop.lv2/triangle.ttl
new file mode 100644
index 0000000..60b5a2b
--- /dev/null
+++ b/blop.lv2/triangle.ttl
@@ -0,0 +1,61 @@
+@prefix blop: <http://drobilla.net/plugins/blop/> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix morph: <http://lv2plug.in/ns/ext/morph#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
+@prefix units: <http://lv2plug.in/ns/extensions/units#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+blop:triangle
+ a lv2:Plugin ,
+ lv2:OscillatorPlugin ;
+ lv2:project blop: ;
+ lv2:symbol "triangle" ;
+ lv2:microVersion 0 ;
+ lv2:minorVersion 0 ;
+ lv2:extensionData opts:interface ;
+ lv2:optionalFeature lv2:hardRTCapable ,
+ urid:map ;
+ lv2:port [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:index 0 ;
+ lv2:maximum 0.5 ;
+ lv2:minimum 0.000001 ;
+ lv2:default 440.0 ;
+ lv2:name "Frequency" ;
+ lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic> ,
+ lv2:sampleRate ;
+ lv2:symbol "freq" ;
+ morph:supportsType lv2:CVPort ;
+ units:unit units:hz
+ ] , [
+ a lv2:ControlPort ,
+ lv2:InputPort ,
+ morph:MorphPort ;
+ lv2:default 0.5 ;
+ lv2:index 1 ;
+ lv2:maximum 1 ;
+ lv2:minimum 0 ;
+ lv2:name "Slope" ;
+ lv2:symbol "slope" ;
+ morph:supportsType lv2:CVPort
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 2 ;
+ lv2:name "Output" ;
+ lv2:symbol "out"
+ ] ;
+ dct:replaces <urn:ladspa:1649> ,
+ <urn:ladspa:1650> ,
+ <urn:ladspa:1651> ,
+ <urn:ladspa:1652> ;
+ doap:name "Triangle" ;
+ lv2:documentation """
+<p>Generates an alias-free variable slope triangle wave at given input
+frequency and slope. The slope changes the wave shape from sawtooth to
+triangle.</p>
+""" .
diff --git a/src/adsr.c b/src/adsr.c
new file mode 100644
index 0000000..a84469f
--- /dev/null
+++ b/src/adsr.c
@@ -0,0 +1,259 @@
+/*
+ An LV2 plugin to generate ADSR envelopes.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+
+#define ADSR_SIGNAL 0
+#define ADSR_TRIGGER 1
+#define ADSR_ATTACK 2
+#define ADSR_DECAY 3
+#define ADSR_SUSTAIN 4
+#define ADSR_RELEASE 5
+#define ADSR_OUTPUT 6
+
+typedef enum {
+ IDLE,
+ ATTACK,
+ DECAY,
+ SUSTAIN,
+ RELEASE
+} ADSRState;
+
+typedef struct {
+ const float* signal;
+ const float* trigger;
+ const float* attack;
+ const float* decay;
+ const float* sustain;
+ const float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float from_level;
+ float level;
+ ADSRState state;
+ uint32_t samples;
+} Adsr;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ switch (port) {
+ case ADSR_SIGNAL:
+ plugin->signal = (const float*)data;
+ break;
+ case ADSR_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case ADSR_ATTACK:
+ plugin->attack = (const float*)data;
+ break;
+ case ADSR_DECAY:
+ plugin->decay = (const float*)data;
+ break;
+ case ADSR_SUSTAIN:
+ plugin->sustain = (const float*)data;
+ break;
+ case ADSR_RELEASE:
+ plugin->release = (const float*)data;
+ break;
+ case ADSR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Adsr* plugin = (Adsr*)malloc(sizeof(Adsr));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ plugin->from_level = 0.0f;
+ plugin->level = 0.0f;
+ plugin->state = IDLE;
+ plugin->samples = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ /* Driving signal */
+ const float* signal = plugin->signal;
+
+ /* Trigger Threshold */
+ const float trigger = *(plugin->trigger);
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ const float sustain = f_clip(*(plugin->sustain), 0.0f, 1.0f);
+
+ /* Release Time (s) */
+ float release = *(plugin->release);
+
+ /* Envelope Out */
+ float* output = plugin->output;
+
+ float srate = plugin->srate;
+ float inv_srate = plugin->inv_srate;
+ float from_level = plugin->from_level;
+ float level = plugin->level;
+ ADSRState state = plugin->state;
+ uint32_t samples = plugin->samples;
+
+ float elapsed;
+
+ /* Convert times into rates */
+ attack = attack > 0.0f ? inv_srate / attack : srate;
+ decay = decay > 0.0f ? inv_srate / decay : srate;
+ release = release > 0.0f ? inv_srate / release : srate;
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ /* Determine if attack or release happened */
+ if ((state == IDLE) || (state == RELEASE)) {
+ if (signal[s] > trigger) {
+ if (attack < srate) {
+ state = ATTACK;
+ } else {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ }
+ samples = 0;
+ }
+ } else {
+ if (signal[s] <= trigger) {
+ state = release < srate ? RELEASE : IDLE;
+ samples = 0;
+ }
+ }
+
+ if (samples == 0) {
+ from_level = level;
+ }
+
+ /* Calculate level of envelope from current state */
+ switch (state) {
+ case IDLE:
+ level = 0;
+ break;
+ case ATTACK:
+ samples++;
+ elapsed = (float)samples * attack;
+ if (elapsed > 1.0f) {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (1.0f - from_level);
+ }
+ break;
+ case DECAY:
+ samples++;
+ elapsed = (float)samples * decay;
+ if (elapsed > 1.0f) {
+ state = SUSTAIN;
+ level = sustain;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (sustain - from_level);
+ }
+ break;
+ case SUSTAIN:
+ level = sustain;
+ break;
+ case RELEASE:
+ samples++;
+ elapsed = (float)samples * release;
+ if (elapsed > 1.0f) {
+ state = IDLE;
+ level = 0.0f;
+ samples = 0;
+ } else {
+ level = from_level - elapsed * from_level;
+ }
+ break;
+ default:
+ /* Should never happen */
+ level = 0.0f;
+ }
+
+ output[s] = level;
+ }
+
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/adsr",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/adsr_gt.c b/src/adsr_gt.c
new file mode 100644
index 0000000..4f23649
--- /dev/null
+++ b/src/adsr_gt.c
@@ -0,0 +1,266 @@
+/*
+ An LV2 plugin to generate ADSR envelopes Gate and Trigger variant.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+
+#define ADSR_GATE 0
+#define ADSR_TRIGGER 1
+#define ADSR_ATTACK 2
+#define ADSR_DECAY 3
+#define ADSR_SUSTAIN 4
+#define ADSR_RELEASE 5
+#define ADSR_OUTPUT 6
+
+typedef enum {
+ IDLE,
+ ATTACK,
+ DECAY,
+ SUSTAIN,
+ RELEASE
+} ADSRState;
+
+typedef struct {
+ const float* gate;
+ const float* trigger;
+ const float* attack;
+ const float* decay;
+ const float* sustain;
+ const float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_trigger;
+ float from_level;
+ float level;
+ ADSRState state;
+ uint32_t samples;
+} Adsr;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ switch (port) {
+ case ADSR_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case ADSR_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case ADSR_ATTACK:
+ plugin->attack = (const float*)data;
+ break;
+ case ADSR_DECAY:
+ plugin->decay = (const float*)data;
+ break;
+ case ADSR_SUSTAIN:
+ plugin->sustain = (const float*)data;
+ break;
+ case ADSR_RELEASE:
+ plugin->release = (const float*)data;
+ break;
+ case ADSR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Adsr* plugin = (Adsr*)malloc(sizeof(Adsr));
+
+ if (plugin) {
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ plugin->last_trigger = 0.0f;
+ plugin->from_level = 0.0f;
+ plugin->level = 0.0f;
+ plugin->state = IDLE;
+ plugin->samples = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Adsr* plugin = (Adsr*)instance;
+
+ /* Gate */
+ const float* gate = plugin->gate;
+
+ /* Trigger */
+ const float* trigger = plugin->trigger;
+
+ /* Attack Time (s) */
+ float attack = *(plugin->attack);
+
+ /* Decay Time (s) */
+ float decay = *(plugin->decay);
+
+ /* Sustain Level */
+ const float sustain = f_clip(*(plugin->sustain), 0.0f, 1.0f);
+
+ /* Release Time (s) */
+ float release = *(plugin->release);
+
+ /* Envelope Out */
+ float* output = plugin->output;
+
+ float srate = plugin->srate;
+ float inv_srate = plugin->inv_srate;
+ float last_trigger = plugin->last_trigger;
+ float from_level = plugin->from_level;
+ float level = plugin->level;
+ ADSRState state = plugin->state;
+ uint32_t samples = plugin->samples;
+
+ float elapsed;
+
+ /* Convert times into rates */
+ attack = attack > 0.0f ? inv_srate / attack : srate;
+ decay = decay > 0.0f ? inv_srate / decay : srate;
+ release = release > 0.0f ? inv_srate / release : srate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ /* Attack on trigger, if gate is open */
+ if (trigger[s] > 0.0f
+ && !(last_trigger > 0.0f)
+ && gate[s] > 0.0f) {
+ if (attack < srate) {
+ state = ATTACK;
+ } else {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ }
+ samples = 0;
+ }
+
+ /* Release if gate closed */
+ if (state != IDLE
+ && state != RELEASE
+ && !(gate[s] > 0.0f)) {
+ state = release < srate ? RELEASE : IDLE;
+ samples = 0;
+ }
+
+ if (samples == 0) {
+ from_level = level;
+ }
+
+ /* Calculate level of envelope from current state */
+ switch (state) {
+ case IDLE:
+ level = 0;
+ break;
+ case ATTACK:
+ samples++;
+ elapsed = (float)samples * attack;
+ if (elapsed > 1.0f) {
+ state = decay < srate ? DECAY : SUSTAIN;
+ level = 1.0f;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (1.0f - from_level);
+ }
+ break;
+ case DECAY:
+ samples++;
+ elapsed = (float)samples * decay;
+ if (elapsed > 1.0f) {
+ state = SUSTAIN;
+ level = sustain;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (sustain - from_level);
+ }
+ break;
+ case SUSTAIN:
+ level = sustain;
+ break;
+ case RELEASE:
+ samples++;
+ elapsed = (float)samples * release;
+ if (elapsed > 1.0f) {
+ state = IDLE;
+ level = 0.0f;
+ samples = 0;
+ } else {
+ level = from_level - elapsed * from_level;
+ }
+ break;
+ default:
+ /* Should never happen */
+ level = 0.0f;
+ }
+
+ output[s] = level;
+ last_trigger = trigger[s];
+ }
+
+ plugin->last_trigger = last_trigger;
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/adsr_gt",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/amp.c b/src/amp.c
new file mode 100644
index 0000000..4f6de84
--- /dev/null
+++ b/src/amp.c
@@ -0,0 +1,166 @@
+/*
+ An LV2 plugin representing a simple mono amplifier.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "uris.h"
+
+#define AMP_GAIN 0
+#define AMP_INPUT 1
+#define AMP_OUTPUT 2
+
+typedef struct {
+ const float* gain;
+ const float* input;
+ float* output;
+ URIs uris;
+ uint32_t gain_is_cv;
+} Amp;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Amp* plugin = (Amp*)instance;
+
+ switch (port) {
+ case AMP_GAIN:
+ plugin->gain = (const float*)data;
+ break;
+ case AMP_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case AMP_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Amp* plugin = (Amp*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case AMP_GAIN:
+ plugin->gain_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Amp* plugin = (Amp*)malloc(sizeof(Amp));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->gain_is_cv = 0;
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Amp* plugin = (Amp*)instance;
+
+ /* Gain (dB) */
+ const float* gain = plugin->gain;
+
+ /* Input */
+ const float* input = plugin->input;
+
+ /* Output */
+ float* output = plugin->output;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float gn = gain[s * plugin->gain_is_cv];
+ const float scale = (float)EXPF(M_LN10 * gn * 0.05f);
+
+ output[s] = scale * input[s];
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/amp",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/branch.c b/src/branch.c
new file mode 100644
index 0000000..a4de320
--- /dev/null
+++ b/src/branch.c
@@ -0,0 +1,223 @@
+/*
+ An LV2 plugin to split a signal into two.
+ Copyright 2011-2014 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+
+#define BRANCH_INPUT 0
+#define BRANCH_OUTPUT1 1
+#define BRANCH_OUTPUT2 2
+
+typedef struct {
+ const float* input;
+ float* output1;
+ float* output2;
+ LV2_URID input_type;
+ URIs uris;
+} Branch;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Branch* plugin = (Branch*)instance;
+
+ switch (port) {
+ case BRANCH_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case BRANCH_OUTPUT1:
+ plugin->output1 = (float*)data;
+ break;
+ case BRANCH_OUTPUT2:
+ plugin->output2 = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Branch* plugin = (Branch*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_AudioPort &&
+ port_type != plugin->uris.lv2_CVPort &&
+ port_type != plugin->uris.lv2_ControlPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case BRANCH_INPUT:
+ plugin->input_type = port_type;
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Branch* plugin = (const Branch*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT &&
+ o->subject != BRANCH_OUTPUT1 &&
+ o->subject != BRANCH_OUTPUT2) {
+ fprintf(stderr, "Bad subject %d\n", o->subject);
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ fprintf(stderr, "Bad key %d\n", o->subject);
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = &plugin->input_type;
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Branch* plugin = (Branch*)malloc(sizeof(Branch));
+
+ map_uris(&plugin->uris, features);
+ plugin->input_type = plugin->uris.lv2_ControlPort;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+runBranch_ia_oaoa(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Branch* plugin = (Branch*)instance;
+
+ /* Input (array of floats of length sample_count) */
+ const float* input = plugin->input;
+
+ /* First Output (array of floats of length sample_count) */
+ float* output1 = plugin->output1;
+
+ /* Second Output (array of floats of length sample_count) */
+ float* output2 = plugin->output2;
+
+ float in;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ output1[s] = in;
+ output2[s] = in;
+ }
+}
+
+static void
+runBranch_ic_ococ(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Branch* plugin = (Branch*)instance;
+
+ /* Input (float value) */
+ const float input = *(plugin->input);
+
+ /* First Output (pointer to float value) */
+ float* output1 = plugin->output1;
+
+ /* Second Output (pointer to float value) */
+ float* output2 = plugin->output2;
+
+ output1[0] = input;
+ output2[0] = input;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Branch* plugin = (Branch*)instance;
+
+ if (plugin->input_type == plugin->uris.lv2_AudioPort ||
+ plugin->input_type == plugin->uris.lv2_CVPort) {
+ runBranch_ia_oaoa(instance, sample_count);
+ } else {
+ runBranch_ic_ococ(instance, sample_count);
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/branch",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/dahdsr.c b/src/dahdsr.c
new file mode 100644
index 0000000..a4b9c65
--- /dev/null
+++ b/src/dahdsr.c
@@ -0,0 +1,416 @@
+/*
+ An LV2 plugin to generate DAHDSR envelopes Gate and (re)trigger
+ Copyright 2011 David Robillard
+ Copyright 2004 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+#include "uris.h"
+
+#define DAHDSR_GATE 0
+#define DAHDSR_TRIGGER 1
+#define DAHDSR_DELAY 2
+#define DAHDSR_ATTACK 3
+#define DAHDSR_HOLD 4
+#define DAHDSR_DECAY 5
+#define DAHDSR_SUSTAIN 6
+#define DAHDSR_RELEASE 7
+#define DAHDSR_OUTPUT 8
+
+typedef enum {
+ IDLE,
+ DELAY,
+ ATTACK,
+ HOLD,
+ DECAY,
+ SUSTAIN,
+ RELEASE
+} DAHDSRState;
+
+typedef struct {
+ const float* gate;
+ const float* trigger;
+ const float* delay;
+ const float* attack;
+ const float* hold;
+ const float* decay;
+ const float* sustain;
+ const float* release;
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_gate;
+ float last_trigger;
+ float from_level;
+ float level;
+ uint32_t delay_is_cv;
+ uint32_t attack_is_cv;
+ uint32_t hold_is_cv;
+ uint32_t decay_is_cv;
+ uint32_t sustain_is_cv;
+ uint32_t release_is_cv;
+ DAHDSRState state;
+ uint32_t samples;
+ URIs uris;
+} Dahdsr;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ switch (port) {
+ case DAHDSR_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case DAHDSR_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case DAHDSR_DELAY:
+ plugin->delay = (const float*)data;
+ break;
+ case DAHDSR_ATTACK:
+ plugin->attack = (const float*)data;
+ break;
+ case DAHDSR_HOLD:
+ plugin->hold = (const float*)data;
+ break;
+ case DAHDSR_DECAY:
+ plugin->decay = (const float*)data;
+ break;
+ case DAHDSR_SUSTAIN:
+ plugin->sustain = (const float*)data;
+ break;
+ case DAHDSR_RELEASE:
+ plugin->release = (const float*)data;
+ break;
+ case DAHDSR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case DAHDSR_DELAY:
+ plugin->delay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_ATTACK:
+ plugin->attack_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_HOLD:
+ plugin->hold_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_DECAY:
+ plugin->decay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_SUSTAIN:
+ plugin->sustain_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DAHDSR_RELEASE:
+ plugin->release_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Dahdsr* plugin = (Dahdsr*)malloc(sizeof(Dahdsr));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ plugin->delay_is_cv = 0;
+ plugin->attack_is_cv = 0;
+ plugin->hold_is_cv = 0;
+ plugin->decay_is_cv = 0;
+ plugin->sustain_is_cv = 0;
+ plugin->release_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ plugin->last_gate = 0.0f;
+ plugin->last_trigger = 0.0f;
+ plugin->from_level = 0.0f;
+ plugin->level = 0.0f;
+ plugin->state = IDLE;
+ plugin->samples = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Dahdsr* plugin = (Dahdsr*)instance;
+
+ /* Gate */
+ const float* gate = plugin->gate;
+
+ /* Trigger */
+ const float* trigger = plugin->trigger;
+
+ /* Delay Time (s) */
+ const float* delay = plugin->delay;
+
+ /* Attack Time (s) */
+ const float* attack = plugin->attack;
+
+ /* Hold Time (s) */
+ const float* hold = plugin->hold;
+
+ /* Decay Time (s) */
+ const float* decay = plugin->decay;
+
+ /* Sustain Level */
+ const float* sustain = plugin->sustain;
+
+ /* Release Time (s) */
+ const float* release = plugin->release;
+
+ /* Envelope Out */
+ float* output = plugin->output;
+
+ /* Instance Data */
+ float srate = plugin->srate;
+ float inv_srate = plugin->inv_srate;
+ float last_gate = plugin->last_gate;
+ float last_trigger = plugin->last_trigger;
+ float from_level = plugin->from_level;
+ float level = plugin->level;
+ DAHDSRState state = plugin->state;
+ uint32_t samples = plugin->samples;
+
+ float elapsed;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float dl = delay[s * plugin->delay_is_cv];
+ const float at = attack[s * plugin->attack_is_cv];
+ const float hl = hold[s * plugin->hold_is_cv];
+ const float dc = decay[s * plugin->decay_is_cv];
+ const float st = sustain[s * plugin->sustain_is_cv];
+ const float rl = release[s * plugin->release_is_cv];
+
+ /* Convert times into rates */
+ const float del = dl > 0.0f ? inv_srate / dl : srate;
+ const float att = at > 0.0f ? inv_srate / at : srate;
+ const float hld = hl > 0.0f ? inv_srate / hl : srate;
+ const float dec = dc > 0.0f ? inv_srate / dc : srate;
+ const float rel = rl > 0.0f ? inv_srate / rl : srate;
+
+ const float gat = gate[s];
+ const float trg = trigger[s];
+ const float sus = f_clip(st, 0.0f, 1.0f);
+
+ /* Initialise delay phase if gate is opened and was closed, or
+ we received a trigger */
+ if ((trg > 0.0f && !(last_trigger > 0.0f))
+ || (gat > 0.0f && !(last_gate > 0.0f))) {
+ if (del < srate) {
+ state = DELAY;
+ } else if (att < srate) {
+ state = ATTACK;
+ } else {
+ state = hld < srate ? HOLD
+ : (dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE)));
+ level = 1.0f;
+ }
+ samples = 0;
+ }
+
+ /* Release if gate was open and now closed */
+ if (state != IDLE && state != RELEASE
+ && last_gate > 0.0f && !(gat > 0.0f)) {
+ state = rel < srate ? RELEASE : IDLE;
+ samples = 0;
+ }
+
+ if (samples == 0) {
+ from_level = level;
+ }
+
+ /* Calculate level of envelope from current state */
+ switch (state) {
+ case IDLE:
+ level = 0;
+ break;
+ case DELAY:
+ samples++;
+ elapsed = (float)samples * del;
+ if (elapsed > 1.0f) {
+ state = att < srate ? ATTACK
+ : (hld < srate ? HOLD
+ : (dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE))));
+ samples = 0;
+ }
+ break;
+ case ATTACK:
+ samples++;
+ elapsed = (float)samples * att;
+ if (elapsed > 1.0f) {
+ state = hld < srate ? HOLD
+ : (dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE)));
+ level = 1.0f;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (1.0f - from_level);
+ }
+ break;
+ case HOLD:
+ samples++;
+ elapsed = (float)samples * hld;
+ if (elapsed > 1.0f) {
+ state = dec < srate ? DECAY
+ : (gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE));
+ samples = 0;
+ }
+ break;
+ case DECAY:
+ samples++;
+ elapsed = (float)samples * dec;
+ if (elapsed > 1.0f) {
+ state = gat > 0.0f ? SUSTAIN
+ : (rel < srate ? RELEASE
+ : IDLE);
+ level = sus;
+ samples = 0;
+ } else {
+ level = from_level + elapsed * (sus - from_level);
+ }
+ break;
+ case SUSTAIN:
+ level = sus;
+ break;
+ case RELEASE:
+ samples++;
+ elapsed = (float)samples * rel;
+ if (elapsed > 1.0f) {
+ state = IDLE;
+ level = 0.0f;
+ samples = 0;
+ } else {
+ level = from_level - elapsed * from_level;
+ }
+ break;
+ default:
+ /* Should never happen */
+ level = 0.0f;
+ }
+
+ output[s] = level;
+
+ last_gate = gate[s];
+ last_trigger = trigger[s];
+ }
+
+ plugin->last_gate = last_gate;
+ plugin->last_trigger = last_trigger;
+ plugin->from_level = from_level;
+ plugin->level = level;
+ plugin->state = state;
+ plugin->samples = samples;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/dahdsr",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/difference.c b/src/difference.c
new file mode 100644
index 0000000..9e76e0a
--- /dev/null
+++ b/src/difference.c
@@ -0,0 +1,188 @@
+/*
+ An LV2 plugin to calculate the difference of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2004 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "vector_op.h"
+
+#define DIFFERENCE_MINUEND 0
+#define DIFFERENCE_SUBTRAHEND 1
+#define DIFFERENCE_DIFFERENCE 2
+
+typedef struct {
+ const float* minuend;
+ const float* subtrahend;
+ float* difference;
+ uint32_t minuend_is_cv;
+ uint32_t subtrahend_is_cv;
+ uint32_t difference_is_cv;
+ URIs uris;
+} Difference;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Difference* plugin = (Difference*)instance;
+
+ switch (port) {
+ case DIFFERENCE_MINUEND:
+ plugin->minuend = (const float*)data;
+ break;
+ case DIFFERENCE_SUBTRAHEND:
+ plugin->subtrahend = (const float*)data;
+ break;
+ case DIFFERENCE_DIFFERENCE:
+ plugin->difference = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Difference* plugin = (Difference*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case DIFFERENCE_MINUEND:
+ plugin->minuend_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case DIFFERENCE_SUBTRAHEND:
+ plugin->subtrahend_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->difference_is_cv = plugin->minuend_is_cv || plugin->subtrahend_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Difference* plugin = (const Difference*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT ||
+ o->subject != DIFFERENCE_DIFFERENCE) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->difference_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Difference* plugin = (Difference*)malloc(sizeof(Difference));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->minuend_is_cv = 0;
+ plugin->subtrahend_is_cv = 0;
+ plugin->difference_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ const Difference* const plugin = (Difference*)instance;
+ const float* const minuend = plugin->minuend;
+ const float* const subtrahend = plugin->subtrahend;
+ float* const difference = plugin->difference;
+
+ VECTOR_OP(-, difference,
+ minuend, plugin->minuend_is_cv,
+ subtrahend, plugin->subtrahend_is_cv);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/difference",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/fmod.c b/src/fmod.c
new file mode 100644
index 0000000..9284d9d
--- /dev/null
+++ b/src/fmod.c
@@ -0,0 +1,199 @@
+/*
+ An LV2 plugin to modulate a frequency by a signal.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "uris.h"
+
+#define FMOD_FREQUENCY 0
+#define FMOD_MODULATOR 1
+#define FMOD_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* modulator;
+ float* output;
+ uint32_t frequency_is_cv;
+ uint32_t modulator_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Fmod;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ switch (port) {
+ case FMOD_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case FMOD_MODULATOR:
+ plugin->modulator = (const float*)data;
+ break;
+ case FMOD_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Fmod* plugin = (Fmod*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case FMOD_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case FMOD_MODULATOR:
+ plugin->modulator_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Fmod* plugin = (const Fmod*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != FMOD_OUTPUT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Fmod* plugin = (Fmod*)malloc(sizeof(Fmod));
+
+ if (plugin) {
+ plugin->frequency_is_cv = 0;
+ plugin->modulator_is_cv = 0;
+ plugin->output_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Fmod* plugin = (Fmod*)instance;
+
+ /* Frequency to Modulate (array of floats of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* LFO Input (array of floats of length 1 or sample_count) */
+ const float* modulator = plugin->modulator;
+
+ /* Output Frequency (array of floats of length 1 or sample_count) */
+ float* output = plugin->output;
+
+ if (!plugin->output_is_cv) { /* TODO: Avoid this branch */
+ sample_count = 1;
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ const float mod = modulator[s * plugin->modulator_is_cv];
+ const float scale = (float)EXPF(M_LN2 * mod);
+
+ output[s] = scale * freq;
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/fmod",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/include/common.h b/src/include/common.h
new file mode 100644
index 0000000..26a405a
--- /dev/null
+++ b/src/include/common.h
@@ -0,0 +1,54 @@
+/*
+ Common definitions.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_common_h
+#define blop_common_h
+
+#include "math_func.h"
+
+/* Handy constants and macros */
+
+#ifndef SMALLEST_FLOAT
+/** Smallest generated non-zero float, used for pre-empting denormals */
+#define SMALLEST_FLOAT (1.0 / (float)0xFFFFFFFF)
+#endif
+
+/*
+ * Clip without branch (from http://musicdsp.org)
+ */
+
+static inline float
+f_min (float x, float a)
+{
+ return a - (a - x + FABSF (a - x)) * 0.5f;
+}
+
+static inline float
+f_max (float x, float b)
+{
+ return (x - b + FABSF (x - b)) * 0.5f + b;
+}
+
+static inline float
+f_clip (float x, float a, float b)
+{
+ return 0.5f * (FABSF (x - a) + a + b - FABSF (x - b));
+}
+
+#endif /* blop_common_h */
diff --git a/src/include/interpolate.h b/src/include/interpolate.h
new file mode 100644
index 0000000..237008c
--- /dev/null
+++ b/src/include/interpolate.h
@@ -0,0 +1,74 @@
+#ifndef blop_interpolate_h
+#define blop_interpolate_h
+
+#include "blop_config.h"
+#include "math_func.h"
+
+/**
+ Interpolate between p0 and n0 taking the previous (p1) and next (n1) points
+ into account, using a 3rd order polynomial (a.k.a. cubic spline).
+ @param interval Normalised time interval between intepolated sample and p0
+ @param p1 Sample two previous to interpolated one
+ @param p0 Previous sample to interpolated one
+ @param n0 Sample following interpolated one
+ @param n1 Sample following n0
+ @return Interpolated sample.
+
+ Adapted from Steve Harris' plugin code
+ swh-plugins-0.2.7/ladspa-util.h::cube_interp
+ http://plugin.org.uk/releases/0.2.7/
+*/
+static inline float
+interpolate_cubic(float interval,
+ float p1,
+ float p0,
+ float n0,
+ float n1)
+{
+ return p0 + 0.5f * interval * (n0 - p1 +
+ interval * (4.0f * n0 + 2.0f * p1 - 5.0f * p0 - n1 +
+ interval * (3.0f * (p0 - n0) - p1 + n1)));
+}
+
+/**
+ Interpolate between p0 and n0 taking the previous (p1) and next (n1) points
+ into account, using a 5th order polynomial.
+ @param interval Normalised time interval between intepolated sample and p0
+ @param p1 Sample two previous to interpolated one
+ @param p0 Previous sample to interpolated one
+ @param n0 Sample following interpolated one
+ @param n1 Sample following n0
+ @return Interpolated sample.
+
+ Adapted from http://www.musicdsp.org/archive.php?classid=5#62
+*/
+static inline float
+interpolate_quintic(float interval,
+ float p1,
+ float p0,
+ float n0,
+ float n1)
+{
+ return p0 + 0.5f * interval * (n0 - p1 +
+ interval * (n0 - 2.0f * p0 + p1 +
+ interval * ( 9.0f * (n0 - p0) + 3.0f * (p1 - n1) +
+ interval * (15.0f * (p0 - n0) + 5.0f * (n1 - p1) +
+ interval * ( 6.0f * (n0 - p0) + 2.0f * (p1 - n1))))));
+}
+
+/**
+ Linear interpolation
+*/
+static inline float
+f_lerp (float value,
+ float v1,
+ float v2)
+{
+ value -= LRINTF (value - 0.5f);
+ value *= (v2 - v1);
+ value += v1;
+
+ return value;
+}
+
+#endif /* blop_interpolate_h */
diff --git a/src/include/lp4pole_filter.h b/src/include/lp4pole_filter.h
new file mode 100644
index 0000000..310fbbe
--- /dev/null
+++ b/src/include/lp4pole_filter.h
@@ -0,0 +1,137 @@
+/*
+ Header for lp4pole_filter struct, and functions to run instance.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ Originally originally appeared in CSound as Timo Tossavainen's (sp?)
+ implementation from the Stilson/Smith CCRMA paper.
+
+ See http://musicdsp.org/archive.php?classid=3#26
+
+ Originally appeared in the arts softsynth by Stefan Westerfeld:
+ http://www.arts-project.org/
+
+ First ported to LADSPA by Reiner Klenk (pdq808[at]t-online.de)
+
+ Tuning and stability issues (output NaN) and additional audio-rate
+ variant added by Mike Rawes (mike_rawes[at]yahoo.co.uk)
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_lp4pole_filter_h
+#define blop_lp4pole_filter_h
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+
+typedef struct {
+ float f;
+ float coeff;
+ float fb;
+ float in1;
+ float in2;
+ float in3;
+ float in4;
+ float inv_nyquist;
+ float out1;
+ float out2;
+ float out3;
+ float out4;
+ float max_abs_in;
+} LP4PoleFilter;
+
+/**
+ Allocate a new LP4PoleFilter instance.
+ @param sample_rate Intended playback (DAC) rate
+ @return Allocated LP4PoleFilter instance
+*/
+LP4PoleFilter* lp4pole_new(double sample_rate);
+
+/**
+ Cleanup an existing LP4PoleFilter instance.
+ @param lpf Pointer to LP4PoleFilter instance allocated with initFilter
+*/
+void lp4pole_cleanup(LP4PoleFilter* lpf);
+
+/**
+ Initialise filter.
+ @param lpf Pointer to LP4PoleFilter instance allocated with initFilter
+*/
+void lp4pole_init(LP4PoleFilter* lpf);
+
+/**
+ Set up filter coefficients for given LP4Pole instance.
+ @param lpf Pointer to LP4PoleFilter instance
+ @param cutoff Cutoff frequency in Hz
+ @param resonance Resonance [Min=0.0, Max=4.0]
+*/
+static inline void
+lp4pole_set_params(LP4PoleFilter* lpf,
+ float cutoff,
+ float resonance)
+{
+ float fsqd;
+ float tuning;
+
+ /* Normalise cutoff and find tuning - Magic numbers found empirically :) */
+ lpf->f = cutoff * lpf->inv_nyquist;
+ tuning = f_clip(3.13f - (lpf->f * 4.24703592f), 1.56503274f, 3.13f);
+
+ /* Clip to bounds */
+ lpf->f = f_clip(lpf->f * tuning, lpf->inv_nyquist, 1.16f);
+
+ fsqd = lpf->f * lpf->f;
+ lpf->coeff = fsqd * fsqd * 0.35013f;
+
+ lpf->fb = f_clip(resonance, -1.3f, 4.0f) * (1.0f - 0.15f * fsqd);
+
+ lpf->f = 1.0f - lpf->f;
+}
+
+/**
+ Run given LP4PoleFilter instance for a single sample.
+ @param lpf Pointer to LP4PoleFilter instance
+ @param in Input sample
+ @return Filtered sample
+*/
+static inline float
+lp4pole_run(LP4PoleFilter* lpf,
+ float in)
+{
+ const float abs_in = fabsf(16.0f * in); /* ~24dB unclipped headroom */
+
+ lpf->max_abs_in = f_max(lpf->max_abs_in, abs_in);
+
+ in -= lpf->out4 * lpf->fb;
+ in *= lpf->coeff;
+
+ lpf->out1 = in + 0.3f * lpf->in1 + lpf->f * lpf->out1; /* Pole 1 */
+ lpf->in1 = in;
+ lpf->out2 = lpf->out1 + 0.3f * lpf->in2 + lpf->f * lpf->out2; /* Pole 2 */
+ lpf->in2 = lpf->out1;
+ lpf->out3 = lpf->out2 + 0.3f * lpf->in3 + lpf->f * lpf->out3; /* Pole 3 */
+ lpf->in3 = lpf->out2;
+ lpf->out4 = lpf->out3 + 0.3f * lpf->in4 + lpf->f * lpf->out4; /* Pole 4 */
+ lpf->in4 = lpf->out3;
+
+ /* Simple hard clip to prevent NaN */
+ lpf->out4 = f_clip(lpf->out4, -lpf->max_abs_in, lpf->max_abs_in);
+
+ lpf->max_abs_in *= 0.999f;
+
+ return lpf->out4;
+}
+
+#endif /* blop_lp4pole_filter_h */
diff --git a/src/include/math_func.h b/src/include/math_func.h
new file mode 100644
index 0000000..60f7ce0
--- /dev/null
+++ b/src/include/math_func.h
@@ -0,0 +1,48 @@
+/*
+ * Provide double fallbacks for environments lacking sinf and
+ * friends (e.g. Solaris)
+ */
+
+#ifndef math_func_h
+#define math_func_h
+
+#include <math.h>
+#include "blop_config.h"
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+#ifndef M_LN10
+# define M_LN10 2.30258509299404568402 /* log_e(10) */
+#endif
+
+#ifndef M_LN2
+# define M_LN2 0.69314718055994530942 /* log_e(2) */
+#endif
+
+#ifdef HAVE_SINF
+/* Use float functions */
+#define SINF(x) sinf(x)
+#define COSF(x) cosf(x)
+#define FABSF(x) fabsf(x)
+#define FLOORF(x) floorf(x)
+#define EXPF(x) expf(x)
+#define POWF(x,p) powf(x,p)
+#define COPYSIGNF(s,d) copysignf(s,d)
+#define LRINTF(x) lrintf(x)
+
+#else
+/* Use double functions */
+#define SINF(x) sin(x)
+#define COSF(x) cos(x)
+#define FABSF(x) fabs(x)
+#define FLOORF(x) floor(x)
+#define EXPF(x) exp(x)
+#define POWF(x,p) pow(x)
+#define COPYSIGNF(s,d) copysign(s,d)
+#define LRINTF(x) lrint(x)
+
+#endif
+
+#endif
diff --git a/src/include/uris.h b/src/include/uris.h
new file mode 100644
index 0000000..0c35628
--- /dev/null
+++ b/src/include/uris.h
@@ -0,0 +1,59 @@
+/*
+ Common URIs used by plugins.
+ Copyright 2012 David Robillard
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_uris_h
+#define blop_uris_h
+
+#include <string.h>
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+typedef struct {
+ LV2_URID atom_URID;
+ LV2_URID lv2_AudioPort;
+ LV2_URID lv2_CVPort;
+ LV2_URID lv2_ControlPort;
+ LV2_URID morph_currentType;
+} URIs;
+
+static inline void
+map_uris(URIs* uris,
+ const LV2_Feature* const* features)
+{
+ LV2_URID_Map* map = NULL;
+ for (int i = 0; features[i]; ++i) {
+ if (!strcmp(features[i]->URI, LV2_URID__map)) {
+ map = (LV2_URID_Map*)features[i]->data;
+ break;
+ }
+ }
+
+ if (map) {
+ uris->atom_URID = map->map(map->handle, LV2_ATOM__URID);
+ uris->lv2_AudioPort = map->map(map->handle, LV2_CORE__AudioPort);
+ uris->lv2_CVPort = map->map(map->handle, LV2_CORE__CVPort);
+ uris->lv2_ControlPort = map->map(map->handle, LV2_CORE__ControlPort);
+ uris->morph_currentType = map->map(map->handle, LV2_MORPH__currentType);
+ } else {
+ memset(uris, 0, sizeof(*uris));
+ }
+}
+
+#endif /* blop_uris_h */
diff --git a/src/include/vector_op.h b/src/include/vector_op.h
new file mode 100644
index 0000000..f261ff8
--- /dev/null
+++ b/src/include/vector_op.h
@@ -0,0 +1,44 @@
+/*
+ Apply a C arithmetical operator to two sample buffers.
+ Copyright 2012-2014 David Robillard
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef BLOP_VECTOR_OP_H
+#define BLOP_VECTOR_OP_H
+
+#define VECTOR_OP(op, output, input1, input1_is_cv, input2, input2_is_cv) \
+ switch ((input1_is_cv << 1) + input2_is_cv) { \
+ case 0: /* 00 (control * control) */ \
+ output[0] = input1[0] op input2[0]; \
+ break; \
+ case 1: /* 01 (control * cv) */ \
+ for (uint32_t s = 0; s < sample_count; ++s) { \
+ output[s] = input1[0] op input2[s]; \
+ } \
+ break; \
+ case 2: /* 10 (cv * control) */ \
+ for (uint32_t s = 0; s < sample_count; ++s) { \
+ output[s] = input1[s] op input2[0]; \
+ } \
+ break; \
+ case 3: /* 11 (cv * cv) */ \
+ for (uint32_t s = 0; s < sample_count; ++s) { \
+ output[s] = input1[s] op input2[s]; \
+ } \
+ break; \
+ }
+
+#endif /* BLOP_VECTOR_OP_H */
diff --git a/src/include/wavedata.h b/src/include/wavedata.h
new file mode 100644
index 0000000..97b2986
--- /dev/null
+++ b/src/include/wavedata.h
@@ -0,0 +1,193 @@
+/*
+ Structures to represent a set of wavetables.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_wavedata_h
+#define blop_wavedata_h
+
+#include "blop_config.h"
+#include "math_func.h"
+#include "interpolate.h"
+#include "common.h"
+
+/* Functions identifying wavedata dlls */
+#define BLOP_DLSYM_SAWTOOTH "blop_get_sawtooth"
+#define BLOP_DLSYM_SQUARE "blop_get_square"
+#define BLOP_DLSYM_PARABOLA "blop_get_parabola"
+
+/*
+ * Structure holding a single segment of sample data
+ * along with information relating to playback.
+ */
+typedef struct {
+ unsigned long sample_count; /* Sample count */
+ float* samples_lf; /* Sample data played back at amplitude
+ inversely proportional to frequency */
+ float* samples_hf; /* Sample data played back at amplitude
+ proportional to frequency */
+ unsigned long harmonics; /* Max harmonic content of sample data */
+
+ float phase_scale_factor; /* Phase scale factor for playback */
+ float min_frequency; /* Minimum playback frequency */
+ float max_frequency; /* Maximum playback frequency */
+ float range_scale_factor; /* Blend scale factor for cross fading */
+} Wavetable;
+
+/*
+ * Structure holding the wavetable data and playback state
+ */
+typedef struct {
+ void* data_handle; /* Data DLL handle */
+ unsigned long table_count; /* Number of wavetables in wavedata */
+ Wavetable** tables; /* One or more wavetables, plus pair of
+ extra tables for frequency extremes */
+ unsigned long* lookup; /* Wavetable lookup vector */
+ unsigned long lookup_max; /* For clipping lookup indices */
+
+ float sample_rate; /* Sample rate */
+ float nyquist; /* Nyquist rate (sample_rate / 2) */
+
+ /* Playback state */
+ float frequency; /* Current playback frequency */
+ float abs_freq; /* Absolute playback frequency */
+ float xfade; /* Crossfade factor for fading */
+ Wavetable* table; /* Wavetable to playback */
+} Wavedata;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int wavedata_load(Wavedata* w,
+ const char* bundle_path,
+ const char* lib_name,
+ const char* wdat_descriptor_name,
+ double sample_rate);
+
+void wavedata_unload(Wavedata* w);
+
+/*****************************************************************************
+ * Description: Get interpolated sample from current wavetable in wavedata
+ * at given phase offset
+ *
+ * Arguments: w Wavedata containing playback state and data
+ * phase Phase offset [0.0, sample_rate]
+ *
+ * Returns: Interpolated sample
+ *
+ * Notes: Cubic (or quintic) interpolation requires four consecutive
+ * samples for operation:
+ *
+ * phase
+ * :
+ * p1 p0 : n0 n1
+ * | | x | |
+ * : :
+ * <-o->
+ * :
+ * interval
+ *
+ * Phase values less than one make p0 the first sample in
+ * the table - p1 will be the last sample, as a previous
+ * sample does not exist. To avoid checking for this case,
+ * a copy of the last sample is stored before the first
+ * sample in each table.
+ * Likewise, if the phase means p0 is the last sample, n0
+ * and n1 will be the first and second samples respectively.
+ * Copies of these samples are stored after the last sample
+ * in each table.
+ *
+ *****************************************************************************/
+static inline float
+wavedata_get_sample(Wavedata* w,
+ float phase)
+{
+ float* samples_hf = w->table->samples_hf;
+ float* samples_lf = w->table->samples_lf;
+ float p1, p0, n0, n1;
+ float phase_f;
+ long int index;
+
+ /* Scale phase to map to position in wavetable */
+ phase *= w->table->phase_scale_factor;
+
+ /* Get position of first contributing sample (p1) */
+ index = LRINTF((float)phase - 0.5f);
+ phase_f = (float)index;
+
+ index %= w->table->sample_count;
+
+ /* Cross-fade table pairs */
+ /* Previous two samples */
+ p1 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+ index++;
+ p0 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+ index++;
+ /* Next two samples */
+ n0 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+ index++;
+ n1 = w->xfade * (samples_lf[index] - samples_hf[index]) + samples_hf[index];
+
+ /* Return interpolated sample */
+ return interpolate_cubic(phase - phase_f, p1, p0, n0, n1);
+}
+
+/*****************************************************************************
+ * Description: Get wavetable to use for playback frequency.
+ *
+ * Arguments: w Wavedata object (contains all table info)
+ * frequency Playback frequency
+ *
+ * Notes: The lookup vector used to determine the wavetable
+ * is indexed by harmonic number.
+ *
+ * The maximum playback frequency for a wavetable is
+ * determined by its harmonic content and the sample rate,
+ * and equals sample_rate / 2 / max_harmonic_in_table.
+ *
+ *****************************************************************************/
+static inline void
+wavedata_get_table(Wavedata* w,
+ float frequency)
+{
+ unsigned long harmonic;
+
+ w->frequency = frequency;
+ w->abs_freq = (float)FABSF((float)frequency);
+
+ /* Get highest harmonic possible in frequency */
+ harmonic = LRINTF(w->nyquist / w->abs_freq - 0.5f);
+
+ /* Clip so lookup is within bounds */
+ if (harmonic > w->lookup_max) {
+ harmonic = w->lookup_max;
+ }
+
+ /* Set playback table */
+ w->table = w->tables[w->lookup[harmonic]];
+
+ /* Get cross fade factor */
+ w->xfade = f_max(w->table->max_frequency - w->abs_freq, 0.0f) * w->table->range_scale_factor;
+ w->xfade = f_min(w->xfade, 1.0f);
+}
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* blop_wavedata_h */
diff --git a/src/include/wdatutil.h b/src/include/wdatutil.h
new file mode 100644
index 0000000..9a0810a
--- /dev/null
+++ b/src/include/wdatutil.h
@@ -0,0 +1,141 @@
+/*
+ Code to generate wavedata dl containing pre-calculated wavetables.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef blop_wdatutil_h
+#define blop_wdatutil_h
+
+#include <stdio.h>
+#include <stdint.h>
+#include "math_func.h"
+#include "wavedata.h"
+
+#define WAVE_TYPE_COUNT 3
+
+extern const char* wave_names[];
+extern const char* wave_descriptions[];
+extern unsigned long wave_first_harmonics[];
+extern unsigned long wave_harmonic_intervals[];
+
+/** Get actual maximum harmonic from given harmonic, h, and wavetype, w */
+#define ACTUAL_HARM(h, w) h - (h - wave_first_harmonics[w]) % wave_harmonic_intervals[w]
+
+/** Get minimum harmonic content in given wavetype, w */
+#define MIN_HARM(w) wave_first_harmonics[w]
+
+/** Get minimum extra harmonic content possible in given wavetype, w */
+#define MIN_EXTRA_HARM(w) wave_harmonic_intervals[w]
+
+/** Get frequency from MIDI note, n */
+#define FREQ_FROM_NOTE(n) 6.875f * POWF(2.0f, (float)(n + 3) / 12.0f)
+
+/** Get max harmonic from given frequency, f, at sample rate, r */
+#define HARM_FROM_FREQ(f, r) (unsigned long)((float)r / f / 2.0f)
+
+/*
+ * A single wavetable will have a range of pitches at which their samples
+ * may be played back.
+ *
+ * The upper bound is determined by the maximum harmonic present in the
+ * waveform - above this frequency, the higher harmonics will alias.
+ *
+ * The lower bound is chosen to be the higher bound of the previous wavetable
+ * (or a pre-defined limit if there is no such table).
+ */
+
+typedef enum {
+ SAW,
+ SQUARE,
+ PARABOLA
+} Wavetype;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*******************************************************************************
+ * Description: Allocate new wavedata struct
+ *
+ * Arguments: sample_rate Sample rate to use when generating data
+ *
+ * Returns: Pointer to wavedata on success
+ * NULL (0) on failure
+ *
+ * Notes: No wavetables are allocated. Use wavedata_add_table
+ ******************************************************************************/
+Wavedata* wavedata_new(double sample_rate);
+
+/*******************************************************************************
+ * Description: Destroy allocated wavedata and any tables
+ *
+ * Arguments: w Wavedata struct to cleanup
+ ******************************************************************************/
+void wavedata_cleanup(Wavedata* w);
+
+/*******************************************************************************
+ * Description: Add new wavetable information to wavedata file object
+ *
+ * Arguments: w Wavedata to add table to
+ * sample_count Number of samples in wavetable
+ * harmonics Maximum harmonics present in table
+ *
+ * Returns: 0 on success
+ * -1 otherwise
+ ******************************************************************************/
+int wavedata_add_table(Wavedata* w,
+ uint32_t sample_count,
+ unsigned long harmonics);
+
+/*******************************************************************************
+ * Description: Initialise all wavetables in wavedata with a waveform
+ * generated from Fourier series.
+ *
+ * Arguments: w Wavedata to generate data for
+ * wavetype Wavetype to generate
+ * gibbs_comp Compensation for Gibbs' effect:
+ * 0.0: none (wave will overshoot)
+ * 1.0: full (wave will not overshoot)
+ *
+ * Notes: Compensation for Gibbs' Effect will reduce the degree
+ * of overshoot and ripple at the transitions. A value of 1.0
+ * will pretty much eliminate it.
+ ******************************************************************************/
+void wavedata_generate_tables(Wavedata* w,
+ Wavetype wavetype,
+ float gibbs_comp);
+
+/*******************************************************************************
+ * Description: Write wavedata as a c header file
+ *
+ * Arguments: w Wavedata to write
+ * wdat_fp Pointer to output file
+ * prefix Prefix to use in declarations. If this is null
+ * declarations are prefixed with 'wdat'.
+ *
+ * Returns: 0 on success
+ * -1 otherwise
+ ******************************************************************************/
+int wavedata_write(Wavedata* w,
+ FILE* wdat_fp,
+ const char* prefix);
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* blop_wdatutil_h */
diff --git a/src/interpolator.c b/src/interpolator.c
new file mode 100644
index 0000000..e47fbe3
--- /dev/null
+++ b/src/interpolator.c
@@ -0,0 +1,144 @@
+/*
+ An LV2 plugin to generate a smooth audio signal from a control source.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define INTERPOLATOR_INPUT 0
+#define INTERPOLATOR_OUTPUT 1
+
+/**
+ Mutated spline interpolator using only two previous samples and one next.
+ @param interval Normalised time interval between inteprolated sample and p0
+ @param p1 Sample two previous to interpolated one
+ @param p0 Previous sample to interpolated one
+ @param n0 Sample following interpolated one
+ @return Interpolated sample.
+*/
+static inline float
+interpolate(float interval,
+ float p1,
+ float p0,
+ float n0)
+{
+ return p0 + 0.5f * interval * (n0 - p1 +
+ interval * (4.0f * n0 + 2.0f * p1 - 5.0f * p0 - n0 +
+ interval * (3.0f * (p0 - n0) - p1 + n0)));
+}
+
+typedef struct {
+ const float* input;
+ float* output;
+ float p1;
+ float p0;
+} Interpolator;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ switch (port) {
+ case INTERPOLATOR_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case INTERPOLATOR_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Interpolator* plugin = (Interpolator*)malloc(sizeof(Interpolator));
+ if (!plugin) {
+ return NULL;
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ plugin->p1 = 0.0f;
+ plugin->p0 = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Interpolator* plugin = (Interpolator*)instance;
+
+ /* Control Input (float value) */
+ const float input = *(plugin->input);
+
+ /* Interpolated Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* We use two previous values and the input as the 'next' one */
+ float p1 = plugin->p1;
+ float p0 = plugin->p0;
+
+ const float inv_scount = 1.0f / (float)sample_count;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float interval = (float)s * inv_scount;
+
+ output[s] = interpolate(interval, p1, p0, input);
+ }
+
+ plugin->p1 = p0;
+ plugin->p0 = input;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/interpolator",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/lp4pole.c b/src/lp4pole.c
new file mode 100644
index 0000000..7d2eb6b
--- /dev/null
+++ b/src/lp4pole.c
@@ -0,0 +1,206 @@
+/*
+ An LV2 plugin simulating a 4 pole low pass resonant filter.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "lp4pole_filter.h"
+#include "common.h"
+#include "uris.h"
+
+#define LP4POLE_CUTOFF 0
+#define LP4POLE_RESONANCE 1
+#define LP4POLE_INPUT 2
+#define LP4POLE_OUTPUT 3
+
+typedef struct {
+ const float* cutoff;
+ const float* resonance;
+ const float* input;
+ float* output;
+ LP4PoleFilter* lpf;
+ uint32_t cutoff_is_cv;
+ uint32_t resonance_is_cv;
+ URIs uris;
+} Lp4pole;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ lp4pole_cleanup(plugin->lpf);
+
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ switch (port) {
+ case LP4POLE_CUTOFF:
+ plugin->cutoff = (const float*)data;
+ break;
+ case LP4POLE_RESONANCE:
+ plugin->resonance = (const float*)data;
+ break;
+ case LP4POLE_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case LP4POLE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case LP4POLE_CUTOFF:
+ plugin->cutoff_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case LP4POLE_RESONANCE:
+ plugin->resonance_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Lp4pole* plugin = (Lp4pole*)malloc(sizeof(Lp4pole));
+
+ if (plugin) {
+ plugin->lpf = lp4pole_new(sample_rate);
+ if (!plugin->lpf) {
+ free(plugin);
+ return NULL;
+ }
+ plugin->cutoff_is_cv = 0;
+ plugin->resonance_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ lp4pole_init(plugin->lpf);
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Lp4pole* plugin = (Lp4pole*)instance;
+
+ /* Cutoff Frequency (array of floats of length 1 or sample_count) */
+ const float* cutoff = plugin->cutoff;
+
+ /* Resonance (array of floats of length 1 or sample_count) */
+ const float* resonance = plugin->resonance;
+
+ /* Input (array of floats of length sample_count) */
+ const float* input = plugin->input;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ LP4PoleFilter* lpf = plugin->lpf;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float co = cutoff[s * plugin->cutoff_is_cv];
+ const float res = resonance[s * plugin->resonance_is_cv];
+ const float in = input[s];
+
+ /* TODO: There is no branching in this function.
+ Would it actually be faster to check if co or res has changed?
+ */
+ lp4pole_set_params(lpf, co, res);
+
+ output[s] = lp4pole_run(lpf, in);
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/lp4pole",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/lp4pole_filter.c b/src/lp4pole_filter.c
new file mode 100644
index 0000000..7d54691
--- /dev/null
+++ b/src/lp4pole_filter.c
@@ -0,0 +1,55 @@
+/*
+ lp4pole filter admin.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ See lp4pole_filter.h for history
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "lp4pole_filter.h"
+
+LP4PoleFilter*
+lp4pole_new(double sample_rate)
+{
+ LP4PoleFilter* lpf;
+
+ lpf = (LP4PoleFilter*)malloc(sizeof(LP4PoleFilter));
+
+ if (lpf) {
+ lpf->inv_nyquist = 2.0f / sample_rate;
+ lp4pole_init(lpf);
+ }
+
+ return lpf;
+}
+
+void
+lp4pole_cleanup(LP4PoleFilter* lpf)
+{
+ if (lpf) {
+ free(lpf);
+ }
+}
+
+void
+lp4pole_init(LP4PoleFilter* lpf)
+{
+ lpf->in1 = lpf->in2 = lpf->in3 = lpf->in4 = 0.0f;
+ lpf->out1 = lpf->out2 = lpf->out3 = lpf->out4 = 0.0f;
+ lpf->max_abs_in = 0.0f;
+}
diff --git a/src/product.c b/src/product.c
new file mode 100644
index 0000000..007d853
--- /dev/null
+++ b/src/product.c
@@ -0,0 +1,187 @@
+/*
+ An LV2 plugin to calculate the product of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "vector_op.h"
+
+#define PRODUCT_MULTIPLICAND 0
+#define PRODUCT_MULTIPLIER 1
+#define PRODUCT_PRODUCT 2
+
+typedef struct {
+ const float* input1;
+ const float* input2;
+ float* output;
+ uint32_t input1_is_cv;
+ uint32_t input2_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Product;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Product* plugin = (Product*)instance;
+
+ switch (port) {
+ case PRODUCT_MULTIPLICAND:
+ plugin->input1 = (const float*)data;
+ break;
+ case PRODUCT_MULTIPLIER:
+ plugin->input2 = (const float*)data;
+ break;
+ case PRODUCT_PRODUCT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Product* plugin = (Product*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case PRODUCT_MULTIPLICAND:
+ plugin->input1_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case PRODUCT_MULTIPLIER:
+ plugin->input2_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->output_is_cv = plugin->input1_is_cv || plugin->input2_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Product* plugin = (const Product*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != PRODUCT_PRODUCT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Product* plugin = (Product*)malloc(sizeof(Product));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->input1_is_cv = 0;
+ plugin->input2_is_cv = 0;
+ plugin->output_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ const Product* const plugin = (Product*)instance;
+ const float* const input1 = plugin->input1;
+ const float* const input2 = plugin->input2;
+ float* const output = plugin->output;
+
+ VECTOR_OP(*, output,
+ input1, plugin->input1_is_cv,
+ input2, plugin->input2_is_cv);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/product",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/pulse.c b/src/pulse.c
new file mode 100644
index 0000000..29e1001
--- /dev/null
+++ b/src/pulse.c
@@ -0,0 +1,225 @@
+/*
+ An LV2 plugin to generate a bandlimited variable pulse waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define PULSE_FREQUENCY 0
+#define PULSE_PULSEWIDTH 1
+#define PULSE_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* pulsewidth;
+ float* output;
+ float phase;
+ uint32_t frequency_is_cv;
+ uint32_t pulsewidth_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Pulse;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ switch (port) {
+ case PULSE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case PULSE_PULSEWIDTH:
+ plugin->pulsewidth = (const float*)data;
+ break;
+ case PULSE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Pulse* plugin = (Pulse*)malloc(sizeof(Pulse));
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case PULSE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case PULSE_PULSEWIDTH:
+ plugin->pulsewidth_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Pulse* plugin = (Pulse*)malloc(sizeof(Pulse));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "sawtooth_data",
+ BLOP_DLSYM_SAWTOOTH, sample_rate)) {
+ free(plugin);
+ return 0;
+ }
+
+ plugin->frequency_is_cv = 0;
+ plugin->pulsewidth_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Pulse* plugin = (Pulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Pulse Width (array of float of length sample_count) */
+ const float* pulsewidth = plugin->pulsewidth;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ float last_pwidth = pulsewidth[0];
+ float pwidth = f_clip(last_pwidth, 0.0f, 1.0f);
+ float dc_shift = 1.0 - (2.0 * pwidth);
+ float phase_shift = pwidth * wdat->sample_rate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ const float this_pwidth = pulsewidth[s * plugin->pulsewidth_is_cv];
+ if (this_pwidth != last_pwidth) {
+ /* Pulsewidth changed, recalculate */
+ last_pwidth = this_pwidth;
+ pwidth = f_clip(this_pwidth, 0.0f, 1.0f);
+ dc_shift = 1.0f - (2.0f * pwidth);
+ phase_shift = pwidth * wdat->sample_rate;
+ }
+
+ /* Get samples from sawtooth and phase shifted inverted sawtooth,
+ with approriate DC offset */
+ output[s] = wavedata_get_sample(wdat, phase)
+ - wavedata_get_sample(wdat, phase + phase_shift)
+ + dc_shift;
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/pulse",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/quantiser.c b/src/quantiser.c
new file mode 100644
index 0000000..ed3ae1d
--- /dev/null
+++ b/src/quantiser.c
@@ -0,0 +1,461 @@
+/*
+ An LV2 plugin to quantise an input to a set of fixed values.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+
+#define QUANTISER_RANGE_MIN 0
+#define QUANTISER_RANGE_MAX 1
+#define QUANTISER_MATCH_RANGE 2
+#define QUANTISER_MODE 3
+#define QUANTISER_COUNT 4
+#define QUANTISER_VALUE_START 5
+#define QUANTISER_INPUT (QUANTISER_MAX_INPUTS + 5)
+#define QUANTISER_OUTPUT (QUANTISER_MAX_INPUTS + 6)
+#define QUANTISER_OUTPUT_CHANGED (QUANTISER_MAX_INPUTS + 7)
+
+typedef struct {
+ const float* min;
+ const float* max;
+ const float* match_range;
+ const float* mode;
+ const float* count;
+ const float* values[QUANTISER_MAX_INPUTS];
+ const float* input;
+ float* output_changed;
+ float* output;
+ float svalues[QUANTISER_MAX_INPUTS + 2];
+ float temp[QUANTISER_MAX_INPUTS + 2];
+ float last_found;
+} Quantiser;
+
+/*
+ * f <= m <= l
+*/
+static inline void
+merge(float* v,
+ int f,
+ int m,
+ int l,
+ float* temp)
+{
+ int f1 = f;
+ int l1 = m;
+ int f2 = m + 1;
+ int l2 = l;
+ int i = f1;
+
+ while ((f1 <= l1) && (f2 <= l2)) {
+ if (v[f1] < v[f2]) {
+ temp[i] = v[f1];
+ f1++;
+ } else {
+ temp[i] = v[f2];
+ f2++;
+ }
+ i++;
+ }
+ while (f1 <= l1) {
+ temp[i] = v[f1];
+ f1++;
+ i++;
+ }
+ while (f2 <= l2) {
+ temp[i] = v[f2];
+ f2++;
+ i++;
+ }
+ for (i = f; i <= l; i++) {
+ v[i] = temp[i];
+ }
+}
+
+/*
+ * Recursive Merge Sort
+ * Sort elements in unsorted vector v
+*/
+static inline void
+msort(float* v,
+ int f,
+ int l,
+ float* temp)
+{
+ int m;
+
+ if (f < l) {
+ m = (f + l) / 2;
+ msort(v, f, m, temp);
+ msort(v, m + 1, l, temp);
+ merge(v, f, m, l, temp);
+ }
+}
+
+/*
+ * Binary search for nearest match to sought value in
+ * ordered vector v of given size
+*/
+static inline int
+fuzzy_bsearch(float* v,
+ int size,
+ float sought)
+{
+ int f = 0;
+ int l = size - 1;
+ int m = ((l - f) / 2);
+
+ while ((l - f) > 1) {
+ if (v[m] < sought) {
+ f = (l - f) / 2 + f;
+ } else {
+ l = (l - f) / 2 + f;
+ }
+
+ m = ((l - f) / 2 + f);
+ }
+
+ if (sought < v[m]) {
+ if (m > 0) {
+ if (FABSF(v[m] - sought) > FABSF(v[m - 1] - sought)) {
+ m--;
+ }
+ }
+ } else if (m < size - 1) {
+ if (FABSF(v[m] - sought) > FABSF(v[m + 1] - sought)) {
+ m++;
+ }
+ }
+
+ return m;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ plugin->last_found = 0.0f;
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ switch (port) {
+ case QUANTISER_RANGE_MIN:
+ plugin->min = (const float*)data;
+ break;
+ case QUANTISER_RANGE_MAX:
+ plugin->max = (const float*)data;
+ break;
+ case QUANTISER_MATCH_RANGE:
+ plugin->match_range = (const float*)data;
+ break;
+ case QUANTISER_MODE:
+ plugin->mode = (const float*)data;
+ break;
+ case QUANTISER_COUNT:
+ plugin->count = (const float*)data;
+ break;
+ case QUANTISER_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case QUANTISER_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ case QUANTISER_OUTPUT_CHANGED:
+ plugin->output_changed = (float*)data;
+ break;
+ default:
+ if (port >= QUANTISER_VALUE_START && port < QUANTISER_OUTPUT) {
+ plugin->values[port - QUANTISER_VALUE_START] = (float*)data;
+ }
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Quantiser* plugin = (Quantiser*)malloc(sizeof(Quantiser));
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Quantiser* plugin = (Quantiser*)instance;
+
+ /* Range Min (float value) */
+ float min = *(plugin->min);
+
+ /* Range Max (float value) */
+ float max = *(plugin->max);
+
+ /* Match Range (float value) */
+ const float match_range = FABSF(*(plugin->match_range));
+
+ /* Mode (float value) */
+ const float mode = *(plugin->mode);
+
+ /* Count (float value) */
+ const float count = *(plugin->count);
+
+ /* Input (array of float of length sample_count) */
+ const float* input = plugin->input;
+
+ /* Output (array of float of length sample_count) */
+ float* output = plugin->output;
+
+ /* Output Changed (array of float of length sample_count) */
+ float* output_changed = plugin->output_changed;
+
+ /* Instance Data */
+ float* temp = plugin->temp;
+ float* values = plugin->svalues;
+ float last_found = plugin->last_found;
+
+ int md = LRINTF(mode);
+ int n = LRINTF(count);
+ int i;
+ float in;
+ float out_changed;
+ float range;
+ float offset;
+ float found = last_found;
+ int found_index = 0;
+
+ /* Clip count if out of range */
+ n = n < 1 ? 1 : (n > QUANTISER_MAX_INPUTS ? QUANTISER_MAX_INPUTS : n);
+
+ /* Swap min and max if wrong way around */
+ if (min > max) {
+ range = min;
+ min = max;
+ max = range;
+ }
+ range = max - min;
+
+ /* Copy and sort required values */
+ for (i = 0; i < n; i++) {
+ values[i + 1] = *(plugin->values[i]);
+ }
+
+ msort(values, 1, n, temp);
+
+ /* Add wrapped extremes */
+ values[0] = values[n] - range;
+ values[n + 1] = values[1] + range;
+
+ if (md < 1) {
+ /* Extend mode */
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (range > 0.0f) {
+ if ((in < min) || (in > max)) {
+ offset = FLOORF((in - max) / range) + 1.0f;
+ offset *= range;
+ in -= offset;
+
+ /* Quantise */
+ found_index = fuzzy_bsearch(values, n + 2, in);
+
+ /* Wrapped */
+ if (found_index == 0) {
+ found_index = n;
+ offset -= range;
+ } else if (found_index == n + 1) {
+ found_index = 1;
+ offset += range;
+ }
+
+ found = values[found_index];
+
+ /* Allow near misses */
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ found += offset;
+ } else {
+ /* Quantise */
+ found_index = fuzzy_bsearch(values, n + 2, in);
+ if (found_index == 0) {
+ found_index = n;
+ found = values[n] - range;
+ } else if (found_index == n + 1) {
+ found_index = 1;
+ found = values[1] + range;
+ } else {
+ found = values[found_index];
+ }
+
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ }
+ } else {
+ /* Min and max the same, so only one value to quantise to */
+ found = min;
+ }
+
+ /* Has quantised output changed? */
+ if (FABSF(found - last_found) > 2.0001f * match_range) {
+ out_changed = 1.0f;
+ last_found = found;
+ } else {
+ out_changed = 0.0f;
+ }
+
+ output[s] = found;
+ output_changed[s] = out_changed;
+ }
+ } else if (md == 1) {
+ /* Wrap mode */
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (range > 0.0f) {
+ if ((in < min) || (in > max)) {
+ in -= (FLOORF((in - max) / range) + 1.0f) * range;
+ }
+
+ /* Quantise */
+ found_index = fuzzy_bsearch(values, n + 2, in);
+ if (found_index == 0) {
+ found_index = n;
+ } else if (found_index == n + 1) {
+ found_index = 1;
+ }
+
+ found = values[found_index];
+
+ /* Allow near misses */
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ } else {
+ /* Min and max the same, so only one value to quantise to */
+ found = min;
+ }
+
+ /* Has quantised output changed? */
+ if (FABSF(found - last_found) > match_range) {
+ out_changed = 1.0f;
+ last_found = found;
+ } else {
+ out_changed = 0.0f;
+ }
+
+ output[s] = found;
+ output_changed[s] = out_changed;
+ }
+ } else {
+ /* Clip mode */
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ in = input[s];
+
+ if (range > 0.0f) {
+ /* Clip to range */
+ if (in < min) {
+ found_index = 1;
+ } else if (in > max) {
+ found_index = n;
+ } else {
+ /* Quantise */
+ found_index = fuzzy_bsearch(values + 1, n, in) + 1;
+ }
+
+ found = values[found_index];
+
+ /* Allow near misses */
+ if (match_range > 0.0f) {
+ if (in < (found - match_range)) {
+ found -= match_range;
+ } else if (in > (found + match_range)) {
+ found += match_range;
+ }
+ }
+ } else {
+ /* Min and max the same, so only one value to quantise to */
+ found = min;
+ }
+
+ /* Has quantised output changed? */
+ if (FABSF(found - last_found) > match_range) {
+ out_changed = 1.0f;
+ last_found = found;
+ } else {
+ out_changed = 0.0f;
+ }
+
+ output[s] = found;
+ output_changed[s] = out_changed;
+ }
+ }
+ plugin->last_found = last_found;
+}
+
+static const LV2_Descriptor descriptor = {
+ QUANTISER_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/random.c b/src/random.c
new file mode 100644
index 0000000..45d8de8
--- /dev/null
+++ b/src/random.c
@@ -0,0 +1,237 @@
+/*
+ An LV2 plugin to generate a random wave of varying frequency and smoothness.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include <time.h>
+#include "math_func.h"
+#include "common.h"
+#include "uris.h"
+
+#define RANDOM_FREQUENCY 0
+#define RANDOM_SMOOTH 1
+#define RANDOM_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* smooth;
+ float* output;
+ float nyquist;
+ float inv_nyquist;
+ float phase;
+ float value1;
+ float value2;
+ uint32_t frequency_is_cv;
+ uint32_t smooth_is_cv;
+ URIs uris;
+} Random;
+
+float inv_rand_max;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Random* plugin = (Random*)instance;
+
+ switch (port) {
+ case RANDOM_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case RANDOM_SMOOTH:
+ plugin->smooth = (const float*)data;
+ break;
+ case RANDOM_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Random* plugin = (Random*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case RANDOM_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case RANDOM_SMOOTH:
+ plugin->smooth_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Random* plugin = (Random*)malloc(sizeof(Random));
+ if (!plugin) {
+ return NULL;
+ }
+
+ srand((int)time((time_t*)0));
+
+ inv_rand_max = 2.0f / (float)RAND_MAX;
+
+ plugin->nyquist = (float)sample_rate / 2.0f;
+ plugin->inv_nyquist = 1.0f / plugin->nyquist;
+
+ plugin->value1 = rand() * inv_rand_max - 1.0f;
+ plugin->value2 = rand() * inv_rand_max - 1.0f;
+
+ plugin->frequency_is_cv = 0;
+ plugin->smooth_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Random* plugin = (Random*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Random* plugin = (Random*)instance;
+
+ /* Frequency (Hz) (array of floats of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Wave smoothness (array of floats of length 1 or sample_count) */
+ const float* smooth = plugin->smooth;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float nyquist = plugin->nyquist;
+ float inv_nyquist = plugin->inv_nyquist;
+ float phase = plugin->phase;
+ float value1 = plugin->value1;
+ float value2 = plugin->value2;
+
+ float result;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = f_clip(frequency[s * plugin->frequency_is_cv],
+ 0.0f, nyquist);
+
+ const float smth = f_clip(smooth[s * plugin->smooth_is_cv],
+ 0.0f, 1.0f);
+
+ const float interval = (1.0f - smth) * 0.5f;
+
+ if (phase < interval) {
+ result = 1.0f;
+ } else if (phase > (1.0f - interval)) {
+ result = -1.0f;
+ } else if (interval > 0.0f) {
+ result = COSF((phase - interval) / smth * M_PI);
+ } else {
+ result = COSF(phase * M_PI);
+ }
+
+ result *= (value2 - value1) * 0.5f;
+ result -= (value2 + value1) * 0.5f;
+
+ output[s] = result;
+
+ phase += freq * inv_nyquist;
+ if (phase > 1.0f) {
+ phase -= 1.0f;
+ value1 = value2;
+ value2 = (float)rand() * inv_rand_max - 1.0f;
+ }
+ }
+
+ plugin->phase = phase;
+ plugin->value1 = value1;
+ plugin->value2 = value2;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/random",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/ratio.c b/src/ratio.c
new file mode 100644
index 0000000..30873c2
--- /dev/null
+++ b/src/ratio.c
@@ -0,0 +1,202 @@
+/*
+ An LV2 plugin to calculate the ratio of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2004 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+#include "uris.h"
+
+#define RATIO_NUMERATOR 0
+#define RATIO_DENOMINATOR 1
+#define RATIO_OUTPUT 2
+
+typedef struct {
+ const float* numerator;
+ const float* denominator;
+ float* output;
+ uint32_t numerator_is_cv;
+ uint32_t denominator_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Ratio;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ switch (port) {
+ case RATIO_NUMERATOR:
+ plugin->numerator = (const float*)data;
+ break;
+ case RATIO_DENOMINATOR:
+ plugin->denominator = (const float*)data;
+ break;
+ case RATIO_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Ratio* plugin = (Ratio*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case RATIO_NUMERATOR:
+ plugin->numerator_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case RATIO_DENOMINATOR:
+ plugin->denominator_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->output_is_cv = plugin->numerator_is_cv || plugin->denominator_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Ratio* plugin = (const Ratio*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != RATIO_OUTPUT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Ratio* plugin = (Ratio*)malloc(sizeof(Ratio));
+
+ if (plugin) {
+ plugin->numerator_is_cv = 0;
+ plugin->denominator_is_cv = 0;
+ plugin->output_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Ratio* plugin = (Ratio*)instance;
+
+ /* Numerator (array of floats of length sample_count) */
+ const float* numerator = plugin->numerator;
+
+ /* Denominator (array of floats of length sample_count) */
+ const float* denominator = plugin->denominator;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ if (!plugin->output_is_cv) { /* TODO: Avoid this branch */
+ sample_count = 1;
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float n = numerator[s * plugin->numerator_is_cv];
+ float d = denominator[s * plugin->denominator_is_cv];
+
+ d = COPYSIGNF(f_max(FABSF(d), 1e-16f), d);
+
+ output[s] = n / d;
+ }
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/ratio",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sawtooth.c b/src/sawtooth.c
new file mode 100644
index 0000000..31c9057
--- /dev/null
+++ b/src/sawtooth.c
@@ -0,0 +1,194 @@
+/*
+ An LV2 plugin to generate a bandlimited sawtooth waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define SAWTOOTH_FREQUENCY 0
+#define SAWTOOTH_OUTPUT 1
+
+typedef struct {
+ const float* frequency;
+ float* output;
+ float phase;
+ uint32_t frequency_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Sawtooth;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ switch (port) {
+ case SAWTOOTH_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SAWTOOTH_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SAWTOOTH_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Sawtooth* plugin = (Sawtooth*)malloc(sizeof(Sawtooth));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "sawtooth_data",
+ BLOP_DLSYM_SAWTOOTH, sample_rate)) {
+ free(plugin);
+ return 0;
+ }
+
+ plugin->frequency_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sawtooth* plugin = (Sawtooth*)instance;
+
+ /* Frequency (array of float of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ for (uint32_t s = 0; s < sample_count; s++) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ output[s] = wavedata_get_sample(wdat, phase);
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sawtooth",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sequencer.c b/src/sequencer.c
new file mode 100644
index 0000000..939401b
--- /dev/null
+++ b/src/sequencer.c
@@ -0,0 +1,216 @@
+/*
+ An LV2 plugin to simulate an analogue style step sequencer.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "math_func.h"
+#include "common.h"
+
+#define SEQUENCER_GATE 0
+#define SEQUENCER_TRIGGER 1
+#define SEQUENCER_LOOP_POINT 2
+#define SEQUENCER_RESET 3
+#define SEQUENCER_VALUE_GATE_CLOSED 4
+#define SEQUENCER_VALUE_START 5
+#define SEQUENCER_OUTPUT (SEQUENCER_MAX_INPUTS + 5)
+
+typedef struct {
+ const float* gate;
+ const float* trigger;
+ const float* loop_steps;
+ const float* reset;
+ const float* value_gate_closed;
+ const float* values[SEQUENCER_MAX_INPUTS];
+ float* output;
+ float srate;
+ float inv_srate;
+ float last_gate;
+ float last_trigger;
+ float last_value;
+ unsigned int step_index;
+} Sequencer;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ switch (port) {
+ case SEQUENCER_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case SEQUENCER_TRIGGER:
+ plugin->trigger = (const float*)data;
+ break;
+ case SEQUENCER_LOOP_POINT:
+ plugin->loop_steps = (const float*)data;
+ break;
+ case SEQUENCER_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ case SEQUENCER_RESET:
+ plugin->reset = (const float*)data;
+ break;
+ case SEQUENCER_VALUE_GATE_CLOSED:
+ plugin->value_gate_closed = (const float*)data;
+ break;
+ default:
+ if (port >= SEQUENCER_VALUE_START && port < SEQUENCER_OUTPUT) {
+ plugin->values[port - SEQUENCER_VALUE_START] = (const float*)data;
+ }
+ break;
+ }
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Sequencer* plugin = (Sequencer*)malloc(sizeof(Sequencer));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->inv_srate = 1.0f / plugin->srate;
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ plugin->last_gate = 0.0f;
+ plugin->last_trigger = 0.0f;
+ plugin->last_value = 0.0f;
+ plugin->step_index = 0;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Sequencer* plugin = (Sequencer*)instance;
+
+ /* Gate */
+ const float* gate = plugin->gate;
+
+ /* Step Trigger */
+ const float* trigger = plugin->trigger;
+
+ /* Loop Steps */
+ const float loop_steps = *(plugin->loop_steps);
+
+ /* Reset to Value on Gate Close */
+ const float reset = *(plugin->reset);
+
+ /* Value used when gate closed */
+ float value_gate_closed = *(plugin->value_gate_closed);
+
+ /* Step Values */
+ float values[SEQUENCER_MAX_INPUTS];
+
+ /* Output */
+ float* output = plugin->output;
+
+ float last_gate = plugin->last_gate;
+ float last_trigger = plugin->last_trigger;
+ float last_value = plugin->last_value;
+
+ unsigned int step_index = plugin->step_index;
+ unsigned int loop_index = LRINTF(loop_steps);
+ int rst = reset > 0.0f;
+ int i;
+
+ loop_index = loop_index == 0 ? 1 : loop_index;
+ loop_index = (loop_index > SEQUENCER_MAX_INPUTS)
+ ? SEQUENCER_MAX_INPUTS
+ : loop_index;
+
+ for (i = 0; i < SEQUENCER_MAX_INPUTS; i++) {
+ values[i] = *(plugin->values[i]);
+ }
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ if (trigger[s] > 0.0f && !(last_trigger > 0.0f)) {
+ if (last_gate > 0.0f) {
+ step_index++;
+ if (step_index >= loop_index) {
+ step_index = 0;
+ }
+ } else {
+ step_index = 0;
+ }
+ }
+
+ output[s] = values[step_index];
+
+ last_value = values[step_index];
+ } else {
+ if (rst) {
+ output[s] = value_gate_closed;
+ } else {
+ output[s] = last_value;
+ }
+
+ step_index = 0;
+ }
+ last_gate = gate[s];
+ last_trigger = trigger[s];
+ }
+
+ plugin->last_gate = last_gate;
+ plugin->last_trigger = last_trigger;
+ plugin->last_value = last_value;
+ plugin->step_index = step_index;
+}
+
+static const LV2_Descriptor descriptor = {
+ SEQUENCER_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ NULL
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/square.c b/src/square.c
new file mode 100644
index 0000000..0136124
--- /dev/null
+++ b/src/square.c
@@ -0,0 +1,195 @@
+/*
+ An LV2 plugin to generate a bandlimited square waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define SQUARE_FREQUENCY 0
+#define SQUARE_OUTPUT 1
+
+typedef struct {
+ const float* frequency;
+ float* output;
+ float phase;
+ uint32_t frequency_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Square;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Square* plugin = (Square*)instance;
+
+ switch (port) {
+ case SQUARE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SQUARE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Square* plugin = (Square*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SQUARE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Square* plugin = (Square*)malloc(sizeof(Square));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "square_data",
+ BLOP_DLSYM_SQUARE, sample_rate)) {
+ free(plugin);
+ return NULL;
+ }
+
+ plugin->frequency_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Square* plugin = (Square*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Square* plugin = (Square*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Square* plugin = (Square*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ /* Get interpolated sample */
+ output[s] = wavedata_get_sample(wdat, phase);
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/square",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sum.c b/src/sum.c
new file mode 100644
index 0000000..3023733
--- /dev/null
+++ b/src/sum.c
@@ -0,0 +1,185 @@
+/*
+ An LV2 plugin to calculate the sum of two signals.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "vector_op.h"
+
+#define SUM_INPUT1 0
+#define SUM_INPUT2 1
+#define SUM_OUTPUT 2
+
+typedef struct {
+ const float* input1;
+ const float* input2;
+ float* output;
+ uint32_t input1_is_cv;
+ uint32_t input2_is_cv;
+ uint32_t output_is_cv;
+ URIs uris;
+} Sum;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Sum* plugin = (Sum*)instance;
+
+ switch (port) {
+ case SUM_INPUT1:
+ plugin->input1 = (const float*)data;
+ break;
+ case SUM_INPUT2:
+ plugin->input2 = (const float*)data;
+ break;
+ case SUM_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Sum* plugin = (Sum*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+ switch (o->subject) {
+ case SUM_INPUT1:
+ plugin->input1_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case SUM_INPUT2:
+ plugin->input2_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ plugin->output_is_cv = plugin->input1_is_cv || plugin->input2_is_cv;
+ return ret;
+}
+
+static uint32_t
+options_get(LV2_Handle instance,
+ LV2_Options_Option* options)
+{
+ const Sum* plugin = (const Sum*)instance;
+ uint32_t ret = 0;
+ for (LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT || o->subject != SUM_OUTPUT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else {
+ o->size = sizeof(LV2_URID);
+ o->type = plugin->uris.atom_URID;
+ o->value = (plugin->output_is_cv
+ ? &plugin->uris.lv2_CVPort
+ : &plugin->uris.lv2_ControlPort);
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Sum* plugin = (Sum*)malloc(sizeof(Sum));
+
+ if (plugin) {
+ plugin->input1_is_cv = 0;
+ plugin->input2_is_cv = 0;
+ plugin->output_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ const Sum* const plugin = (Sum*)instance;
+ const float* const input1 = plugin->input1;
+ const float* const input2 = plugin->input2;
+ float* const output = plugin->output;
+
+ VECTOR_OP(+, output,
+ input1, plugin->input1_is_cv,
+ input2, plugin->input2_is_cv);
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { options_get, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sum",
+ instantiate,
+ connect_port,
+ NULL,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sync_pulse.c b/src/sync_pulse.c
new file mode 100644
index 0000000..0f24c80
--- /dev/null
+++ b/src/sync_pulse.c
@@ -0,0 +1,213 @@
+/*
+ An LV2 plugin to generate a non-bandlimited variable-pulse waveform with gate
+ for trigger and sync.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "common.h"
+
+#define SYNCPULSE_FREQUENCY 0
+#define SYNCPULSE_PULSEWIDTH 1
+#define SYNCPULSE_GATE 2
+#define SYNCPULSE_OUTPUT 3
+
+typedef struct {
+ const float* frequency;
+ const float* pulsewidth;
+ const float* gate;
+ float* output;
+ float srate;
+ float phase;
+ uint32_t frequency_is_cv;
+ uint32_t pulsewidth_is_cv;
+ URIs uris;
+} SyncPulse;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ switch (port) {
+ case SYNCPULSE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SYNCPULSE_PULSEWIDTH:
+ plugin->pulsewidth = (const float*)data;
+ break;
+ case SYNCPULSE_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case SYNCPULSE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ SyncPulse* plugin = (SyncPulse*)malloc(sizeof(SyncPulse));
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SYNCPULSE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case SYNCPULSE_PULSEWIDTH:
+ plugin->pulsewidth_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ SyncPulse* plugin = (SyncPulse*)malloc(sizeof(SyncPulse));
+
+ if (plugin) {
+ plugin->srate = (float)sample_rate;
+ plugin->frequency_is_cv = 0;
+ plugin->pulsewidth_is_cv = 0;
+ map_uris(&plugin->uris, features);
+ }
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncPulse* plugin = (SyncPulse*)instance;
+
+ /* Frequency (array of float of length sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Pulse Width (array of float of length sample_count) */
+ const float* pulsewidth = plugin->pulsewidth;
+
+ /* Gate (array of float of length sample_count) */
+ const float* gate = plugin->gate;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float phase = plugin->phase;
+ float srate = plugin->srate;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ const float pw = pulsewidth[s * plugin->pulsewidth_is_cv];
+ const float pwidth = f_clip(pw, 0.0f, 1.0f) * srate;
+
+ if (phase < pwidth) {
+ output[s] = 1.0f;
+ } else {
+ output[s] = -1.0f;
+ }
+
+ phase += freq;
+ if (phase < 0.0f) {
+ phase += srate;
+ } else if (phase > srate) {
+ phase -= srate;
+ }
+ } else {
+ output[s] = 0.0f;
+ phase = 0.0f;
+ }
+ }
+
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sync_pulse",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/sync_square.c b/src/sync_square.c
new file mode 100644
index 0000000..38fdaf5
--- /dev/null
+++ b/src/sync_square.c
@@ -0,0 +1,201 @@
+/*
+ An LV2 plugin to generate a non-bandlimited square waveform with gate for
+ trigger and sync.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+
+#define SYNCSQUARE_FREQUENCY 0
+#define SYNCSQUARE_GATE 1
+#define SYNCSQUARE_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* gate;
+ float* output;
+ float srate;
+ float nyquist;
+ float phase;
+ uint32_t frequency_is_cv;
+ URIs uris;
+} SyncSquare;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ switch (port) {
+ case SYNCSQUARE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case SYNCSQUARE_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case SYNCSQUARE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case SYNCSQUARE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ SyncSquare* plugin = (SyncSquare*)malloc(sizeof(SyncSquare));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->srate = (float)sample_rate;
+ plugin->nyquist = (float)(sample_rate / 2.0f);
+ plugin->frequency_is_cv = 0;
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ SyncSquare* plugin = (SyncSquare*)instance;
+
+ /* Frequency (array of float of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Gate (array of float of length sample_count) */
+ const float* gate = plugin->gate;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ float phase = plugin->phase;
+ float srate = plugin->srate;
+ float nyquist = plugin->nyquist;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ if (gate[s] > 0.0f) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+
+ if (phase < nyquist) {
+ output[s] = 1.0f;
+ } else {
+ output[s] = -1.0f;
+ }
+
+ phase += freq;
+ if (phase < 0.0f) {
+ phase += srate;
+ } else if (phase > srate) {
+ phase -= srate;
+ }
+ } else {
+ output[s] = 0.0f;
+ phase = 0.0f;
+ }
+ }
+
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/sync_square",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/tracker.c b/src/tracker.c
new file mode 100644
index 0000000..dd628bc
--- /dev/null
+++ b/src/tracker.c
@@ -0,0 +1,245 @@
+/*
+ An LV2 plugin to shape a signal in various ways.
+ Copyright 2011 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "common.h"
+#include "uris.h"
+
+#define TRACKER_GATE 0
+#define TRACKER_HATTACK 1
+#define TRACKER_HDECAY 2
+#define TRACKER_LATTACK 3
+#define TRACKER_LDECAY 4
+#define TRACKER_INPUT 5
+#define TRACKER_OUTPUT 6
+
+typedef struct {
+ const float* gate;
+ const float* hattack;
+ const float* hdecay;
+ const float* lattack;
+ const float* ldecay;
+ const float* input;
+ float* output;
+ float coeff;
+ float last_value;
+ uint32_t hattack_is_cv;
+ uint32_t hdecay_is_cv;
+ uint32_t lattack_is_cv;
+ uint32_t ldecay_is_cv;
+ URIs uris;
+} Tracker;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ switch (port) {
+ case TRACKER_GATE:
+ plugin->gate = (const float*)data;
+ break;
+ case TRACKER_HATTACK:
+ plugin->hattack = (const float*)data;
+ break;
+ case TRACKER_HDECAY:
+ plugin->hdecay = (const float*)data;
+ break;
+ case TRACKER_LATTACK:
+ plugin->lattack = (const float*)data;
+ break;
+ case TRACKER_LDECAY:
+ plugin->ldecay = (const float*)data;
+ break;
+ case TRACKER_INPUT:
+ plugin->input = (const float*)data;
+ break;
+ case TRACKER_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Tracker* plugin = (Tracker*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case TRACKER_HATTACK:
+ plugin->hattack_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRACKER_HDECAY:
+ plugin->hdecay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRACKER_LATTACK:
+ plugin->lattack_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRACKER_LDECAY:
+ plugin->ldecay_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Tracker* plugin = (Tracker*)malloc(sizeof(Tracker));
+ if (!plugin) {
+ return NULL;
+ }
+
+ plugin->coeff = 2.0f * M_PI / (float)sample_rate;
+
+ plugin->hattack_is_cv = 0;
+ plugin->hdecay_is_cv = 0;
+ plugin->lattack_is_cv = 0;
+ plugin->ldecay_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ plugin->last_value = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Tracker* plugin = (Tracker*)instance;
+
+ /* Gate (array of floats of length sample_count) */
+ const float* gate = plugin->gate;
+
+ /* Gate High Attack Rate (array of floats of length 1 or sample_count) */
+ const float* hattack = plugin->hattack;
+
+ /* Gate High Decay Rate (array of floats of length 1 or sample_count) */
+ const float* hdecay = plugin->hdecay;
+
+ /* Gate Low Attack Rate (array of floats of length 1 or sample_count) */
+ const float* lattack = plugin->lattack;
+
+ /* Gate Low Decay Rate (array of floats of length 1 or sample_count) */
+ const float* ldecay = plugin->ldecay;
+
+ /* Input (array of floats of length sample_count) */
+ const float* input = plugin->input;
+
+ /* Output (array of floats of length sample_count) */
+ float* output = plugin->output;
+
+ /* Instance Data */
+ float coeff = plugin->coeff;
+ float last_value = plugin->last_value;
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float in = input[s];
+ const float ha = hattack[s * plugin->hattack_is_cv];
+ const float hd = hdecay[s * plugin->hdecay_is_cv];
+ const float la = lattack[s * plugin->lattack_is_cv];
+ const float ld = ldecay[s * plugin->ldecay_is_cv];
+
+ float rate;
+ if (gate[s] > 0.0f) {
+ rate = in > last_value ? ha : hd;
+ } else {
+ rate = in > last_value ? la : ld;
+ }
+
+ rate = f_min(1.0f, rate * coeff);
+ last_value = last_value * (1.0f - rate) + in * rate;
+
+ output[s] = last_value;
+ }
+
+ plugin->last_value = last_value;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/tracker",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/triangle.c b/src/triangle.c
new file mode 100644
index 0000000..09c01c2
--- /dev/null
+++ b/src/triangle.c
@@ -0,0 +1,232 @@
+/*
+ An LV2 plugin to generate a bandlimited slope-variable triangle waveform.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include "lv2/lv2plug.in/ns/ext/morph/morph.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "uris.h"
+#include "wavedata.h"
+
+#define TRIANGLE_FREQUENCY 0
+#define TRIANGLE_SLOPE 1
+#define TRIANGLE_OUTPUT 2
+
+typedef struct {
+ const float* frequency;
+ const float* slope;
+ float* output;
+ float phase;
+ float min_slope;
+ float max_slope;
+ uint32_t frequency_is_cv;
+ uint32_t slope_is_cv;
+ Wavedata wdat;
+ URIs uris;
+} Triangle;
+
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ switch (port) {
+ case TRIANGLE_FREQUENCY:
+ plugin->frequency = (const float*)data;
+ break;
+ case TRIANGLE_SLOPE:
+ plugin->slope = (const float*)data;
+ break;
+ case TRIANGLE_OUTPUT:
+ plugin->output = (float*)data;
+ break;
+ }
+}
+
+static uint32_t
+options_set(LV2_Handle instance,
+ const LV2_Options_Option* options)
+{
+ Triangle* plugin = (Triangle*)instance;
+ uint32_t ret = 0;
+ for (const LV2_Options_Option* o = options; o->key; ++o) {
+ if (o->context != LV2_OPTIONS_PORT) {
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ } else if (o->key != plugin->uris.morph_currentType) {
+ ret |= LV2_OPTIONS_ERR_BAD_KEY;
+ } else if (o->type != plugin->uris.atom_URID) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ } else {
+ LV2_URID port_type = *(const LV2_URID*)(o->value);
+ if (port_type != plugin->uris.lv2_ControlPort &&
+ port_type != plugin->uris.lv2_CVPort) {
+ ret |= LV2_OPTIONS_ERR_BAD_VALUE;
+ continue;
+ }
+
+ switch (o->subject) {
+ case TRIANGLE_FREQUENCY:
+ plugin->frequency_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ case TRIANGLE_SLOPE:
+ plugin->slope_is_cv = (port_type == plugin->uris.lv2_CVPort);
+ break;
+ default:
+ ret |= LV2_OPTIONS_ERR_BAD_SUBJECT;
+ }
+ }
+ }
+ return ret;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double sample_rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Triangle* plugin = (Triangle*)malloc(sizeof(Triangle));
+ if (!plugin) {
+ return NULL;
+ }
+
+ if (wavedata_load(&plugin->wdat, bundle_path, "parabola_data",
+ BLOP_DLSYM_PARABOLA, sample_rate)) {
+ free(plugin);
+ return 0;
+ }
+
+ plugin->min_slope = 2.0f / plugin->wdat.sample_rate;
+ plugin->max_slope = 1.0f - plugin->min_slope;
+
+ plugin->frequency_is_cv = 0;
+ plugin->slope_is_cv = 0;
+
+ map_uris(&plugin->uris, features);
+ wavedata_get_table(&plugin->wdat, 440.0);
+
+ return (LV2_Handle)plugin;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ wavedata_unload(&plugin->wdat);
+ free(instance);
+}
+
+static void
+activate(LV2_Handle instance)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ plugin->phase = 0.0f;
+}
+
+static void
+run(LV2_Handle instance,
+ uint32_t sample_count)
+{
+ Triangle* plugin = (Triangle*)instance;
+
+ /* Frequency (array of float of length 1 or sample_count) */
+ const float* frequency = plugin->frequency;
+
+ /* Slope (array of float of length 1 or sample_count) */
+ const float* slope = plugin->slope;
+
+ /* Output (pointer to float value) */
+ float* output = plugin->output;
+
+ /* Instance data */
+ Wavedata* wdat = &plugin->wdat;
+ float phase = plugin->phase;
+ const float min_slope = plugin->min_slope;
+ const float max_slope = plugin->max_slope;
+
+ float last_slope = slope[0];
+ float slp = f_clip(last_slope, min_slope, max_slope);
+ float phase_shift = slp * wdat->sample_rate;
+ float scale = 1.0f / (8.0f * (slp - (slp * slp)));
+
+ for (uint32_t s = 0; s < sample_count; ++s) {
+ const float freq = frequency[s * plugin->frequency_is_cv];
+ if (freq != wdat->frequency) {
+ /* Frequency changed, look up table to play */
+ wavedata_get_table(wdat, freq);
+ }
+
+ const float this_slope = slope[s * plugin->slope_is_cv];
+ if (this_slope != last_slope) {
+ /* Slope changed, recalculate */
+ last_slope = this_slope;
+ slp = f_clip(this_slope, min_slope, max_slope);
+ phase_shift = slp * wdat->sample_rate;
+ scale = 1.0f / (8.0f * (slp - (slp * slp)));
+ }
+
+ /* Get samples from parabola and phase shifted inverted parabola,
+ and scale to compensate */
+ output[s] = (wavedata_get_sample(wdat, phase)
+ - wavedata_get_sample(wdat, phase + phase_shift)) * scale;
+
+ /* Update phase, wrapping if necessary */
+ phase += wdat->frequency;
+ if (phase < 0.0f) {
+ phase += wdat->sample_rate;
+ } else if (phase > wdat->sample_rate) {
+ phase -= wdat->sample_rate;
+ }
+ }
+ plugin->phase = phase;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+ static const LV2_Options_Interface options = { NULL, options_set };
+ if (!strcmp(uri, LV2_OPTIONS__interface)) {
+ return &options;
+ }
+ return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+ "http://drobilla.net/plugins/blop/triangle",
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ NULL,
+ cleanup,
+ extension_data,
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0: return &descriptor;
+ default: return NULL;
+ }
+}
diff --git a/src/wavedata.c b/src/wavedata.c
new file mode 100644
index 0000000..910577f
--- /dev/null
+++ b/src/wavedata.c
@@ -0,0 +1,79 @@
+/*
+ Oscillator wave data loading.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "blop_config.h"
+#include "wavedata.h"
+
+#ifdef _WIN32
+# include <windows.h>
+# define dlopen(path, flags) LoadLibrary(path)
+# define dlclose(lib) FreeLibrary((HMODULE)lib)
+# define dlsym(lib, sym) GetProcAddress((HMODULE)lib, sym)
+# define snprintf _snprintf
+#else
+# include <dlfcn.h>
+#endif
+
+int
+wavedata_load(Wavedata* w,
+ const char* bundle_path,
+ const char* lib_name,
+ const char* wdat_descriptor_name,
+ double sample_rate)
+{
+ const size_t bundle_len = strlen(bundle_path);
+ const size_t lib_name_len = strlen(lib_name);
+ const size_t ext_len = strlen(BLOP_SHLIB_EXT);
+ const size_t path_len = bundle_len + lib_name_len + ext_len + 2;
+ int retval = -1;
+
+ char* lib_path = (char*)malloc(path_len);
+ snprintf(lib_path, path_len, "%s%s%s",
+ bundle_path, lib_name, BLOP_SHLIB_EXT);
+
+ void* handle = dlopen(lib_path, RTLD_NOW);
+ free(lib_path);
+
+ if (handle) {
+ // Avoid pedantic warnings about fetching functions with dlsym
+ typedef void (*VoidFunc)(void);
+ typedef VoidFunc (*VoidFuncGetter)(void*, const char*);
+ VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym;
+
+ typedef int (*DescFunc)(Wavedata*, unsigned long);
+ DescFunc desc_func = (DescFunc)dlfunc(handle, wdat_descriptor_name);
+ if (desc_func) {
+ retval = desc_func(w, (unsigned long)sample_rate);
+ w->data_handle = handle;
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+void
+wavedata_unload(Wavedata* w)
+{
+ dlclose(w->data_handle);
+}
diff --git a/src/wavegen.c b/src/wavegen.c
new file mode 100644
index 0000000..c722e04
--- /dev/null
+++ b/src/wavegen.c
@@ -0,0 +1,310 @@
+/*
+ A program to generate c header files containing pre-calculated wavedata.
+ Copyright 2011 David Robillard
+ Copyright 2002 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include "wdatutil.h"
+#include "wavedata.h"
+#include "common.h"
+
+static void
+usage(void)
+{
+ int i;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Generate bandlimited wavedata and write as c header file\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Usage: wavegen -w <Wavename> -r <Sample Rate> -f <Note> -s <Note Step>\n");
+ fprintf(stderr, " -m <Samples> [-o <Output Filename>] [-p <Prefix>]\n");
+ fprintf(stderr, " [-g <Factor>] [-q] [-t] [-h]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -w, --wave Name of wave to generate (case insensitive)\n");
+ fprintf(stderr, " -r, --rate Intended playback rate in Samples/Second\n");
+ fprintf(stderr, " -f, --first First MIDI note to generate table for\n");
+ fprintf(stderr, " -s, --step Number of MIDI notes to skip for next table\n");
+ fprintf(stderr, " -m, --min Minimum table size in samples\n");
+ fprintf(stderr, " -o, --output Output Filename, name of file to output\n");
+ fprintf(stderr, " If not given, output is to stdout\n");
+ fprintf(stderr, " -p, --prefix Prefix for declarations in header\n");
+ fprintf(stderr, " -g, --gibbs Compensate for Gibbs' effect\n");
+ fprintf(stderr, " -q, --quiet Surpress stderr output\n");
+ fprintf(stderr, " -t, --test Don't actually generate data\n");
+ fprintf(stderr, " -h, --help Print this text and exit\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Supported waves:\n");
+
+ for (i = 0; i < WAVE_TYPE_COUNT; i++) {
+ fprintf(stderr, " %s (%s)\n", wave_names[i], wave_descriptions[i]);
+ }
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Gibbs' Effect\n");
+ fprintf(stderr, " Gibbs' effect causes overshoot in waves generated from finite\n");
+ fprintf(stderr, " Fourier Series. Compensation can be applied, which will result in\n");
+ fprintf(stderr, " a waveform that sounds slightly less bright.\n");
+ fprintf(stderr, " Use the --gibbs option to set degree of compensatation, from 0.0\n");
+ fprintf(stderr, " (no compensation) to 1.0 (full compensation)\n");
+ fprintf(stderr, "\n");
+}
+
+/**
+ Create bandlimited wavedata header files for various
+ waveforms
+*/
+int
+main(int argc,
+ char** argv)
+{
+ int option_index;
+ int opt;
+ const char* options = "w:r:f:s:m:o:p:g:qth";
+ struct option long_options[] = {
+ { "wave", 1, 0, 'w' },
+ { "rate", 1, 0, 'r' },
+ { "first", 1, 0, 'f' },
+ { "step", 1, 0, 's' },
+ { "min", 1, 0, 'm' },
+ { "output", 1, 0, 'o' },
+ { "prefix", 0, 0, 'p' },
+ { "gibbs", 1, 0, 'g' },
+ { "quiet", 0, 0, 'q' },
+ { "test", 0, 0, 't' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+ int wavetype = -1;
+ long sample_rate = -1;
+ long first_note = -1;
+ long note_step = -1;
+ long min_table_size = -1;
+ const char* filename = NULL;
+ FILE* file;
+ const char* prefix = NULL;
+ float gibbs = 0.0f;
+ int quiet = 0;
+ int test = 0;
+
+ Wavedata* w;
+ float freq;
+ uint32_t sample_count;
+ unsigned long max_harmonic_hf;
+ unsigned long max_harmonic_lf;
+ unsigned long i;
+
+ size_t strcmplen;
+ size_t len1;
+ size_t len2;
+
+ /* Parse arguments */
+ if (argc == 1) {
+ usage();
+ exit(-1);
+ }
+
+ opterr = 0;
+ while ((opt = getopt_long(argc, argv, options, long_options, &option_index)) != -1) {
+ switch (opt) {
+ case 'w':
+ for (i = 0; i < WAVE_TYPE_COUNT; i++) {
+ len1 = strlen(optarg);
+ len2 = strlen(wave_names[i]);
+ strcmplen = len1 < len2 ? len1 : len2;
+
+ if (!strncmp(optarg, wave_names[i], strcmplen)) {
+ wavetype = i;
+ }
+ }
+ if (wavetype == -1) {
+ fprintf(stderr, "Unrecognised option for Wave: %s\n", optarg);
+ exit(-1);
+ }
+ break;
+ case 'r':
+ sample_rate = (long)atoi(optarg);
+ break;
+ case 'f':
+ first_note = (long)atoi(optarg);
+ break;
+ case 's':
+ note_step = (long)atoi(optarg);
+ break;
+ case 'm':
+ min_table_size = (long)atoi(optarg);
+ break;
+ case 'o':
+ filename = optarg;
+ break;
+ case 'p':
+ prefix = optarg;
+ break;
+ case 'g':
+ gibbs = atof(optarg);
+ break;
+ case 'q':
+ quiet = -1;
+ break;
+ case 't':
+ test = -1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ /* Check basic arguments */
+ if (wavetype == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No wavetype specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (sample_rate == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No sample rate specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (first_note == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No first note specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (note_step == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No note step specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (min_table_size == -1) {
+ if (!quiet) {
+ fprintf(stderr, "No minimum table size specified.\n");
+ }
+ exit(-1);
+ }
+
+ if (gibbs < 0.0f || gibbs > 1.0f) {
+ if (!quiet) {
+ fprintf(stderr, "Gibbs compensation clamped to [0.0, 1.0]\n");
+ fprintf(stderr, " Supplied value: %.2f\n", gibbs);
+ }
+ gibbs = gibbs < 0.0f ? 0.0f : gibbs;
+ gibbs = gibbs > 1.0f ? 1.0f : gibbs;
+ if (!quiet) {
+ fprintf(stderr, " Clamped to: %.2f\n", gibbs);
+ }
+ }
+
+ if (note_step < 1) {
+ if (!quiet) {
+ fprintf(stderr, "Using minimum note step of 1\n");
+ }
+ note_step = 1;
+ }
+
+ /* Get file to write to */
+ if (!filename) {
+ file = stdout;
+ } else {
+ file = fopen(filename, "w");
+ }
+
+ w = wavedata_new(sample_rate);
+
+ if (!w) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to create wavedata\n");
+ }
+
+ exit(-1);
+ }
+
+ freq = FREQ_FROM_NOTE(first_note);
+ max_harmonic_lf = HARM_FROM_FREQ(freq, sample_rate);
+ max_harmonic_hf = max_harmonic_lf;
+
+ for (i = 0; max_harmonic_hf > MIN_HARM(wavetype); i += note_step) {
+ freq = FREQ_FROM_NOTE(first_note + i + note_step);
+ max_harmonic_hf = HARM_FROM_FREQ(freq, sample_rate);
+
+ max_harmonic_hf = ACTUAL_HARM(max_harmonic_hf, wavetype);
+ max_harmonic_lf = ACTUAL_HARM(max_harmonic_lf, wavetype);
+
+ while (max_harmonic_lf == max_harmonic_hf) {
+ i += note_step;
+ freq = FREQ_FROM_NOTE(first_note + i + note_step);
+ max_harmonic_hf = HARM_FROM_FREQ(freq, sample_rate);
+ max_harmonic_hf = ACTUAL_HARM(max_harmonic_hf, wavetype);
+ }
+
+ if (max_harmonic_lf > MIN_EXTRA_HARM(wavetype)) {
+ sample_count = max_harmonic_lf * 2;
+ sample_count = sample_count < min_table_size ? min_table_size : sample_count;
+
+ if (wavedata_add_table(w, sample_count, max_harmonic_lf)) {
+ if (!quiet) {
+ fprintf(stderr, "Could not add wavetable to wavedata\n");
+ }
+
+ wavedata_cleanup(w);
+ exit(-1);
+ }
+ }
+ max_harmonic_lf = max_harmonic_hf;
+ }
+
+ if (!quiet) {
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Generating %s wave\n", wave_names[wavetype]);
+ fprintf(stderr, " Sample Rate: %ld\n", sample_rate);
+ if (gibbs > 0.0f) {
+ fprintf(stderr, " Gibbs' compensation factor: %+.2f\n\n", gibbs);
+ }
+ }
+
+ wavedata_generate_tables(w, (Wavetype)wavetype, gibbs);
+
+ if (!test) {
+ if (wavedata_write(w, file, prefix)) {
+ if (!quiet) {
+ fprintf(stderr, "Could not write to file %s!\n\n", filename);
+ }
+ } else {
+ if (!quiet) {
+ fprintf(stderr, "Written to file %s\n\n", filename);
+ }
+ }
+ }
+
+ wavedata_cleanup(w);
+
+ return 0;
+}
diff --git a/src/wdatutil.c b/src/wdatutil.c
new file mode 100644
index 0000000..86b5fb0
--- /dev/null
+++ b/src/wdatutil.c
@@ -0,0 +1,679 @@
+/*
+ Code to generate wavedata for bandlimited waveforms.
+ Copyright 2011-2014 David Robillard
+ Copyright 2003 Mike Rawes
+
+ This 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, either version 3 of the License, or
+ (at your option) any later version.
+
+ This software 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 software. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "common.h"
+#include "math_func.h"
+#include "wavedata.h"
+#include "wdatutil.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void generate_sine(float* samples,
+ uint32_t sample_count);
+void generate_sawtooth(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp);
+void generate_square(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp);
+void generate_parabola(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp);
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+const char* wave_names[] = {
+ "saw",
+ "square",
+ "parabola"
+};
+
+const char* wave_descriptions[] = {
+ "Sawtooth Wave",
+ "Square Wave",
+ "Parabola Wave"
+};
+
+unsigned long wave_first_harmonics[] = {
+ 1,
+ 1,
+ 1
+};
+
+unsigned long wave_harmonic_intervals[] = {
+ 1,
+ 2,
+ 1
+};
+
+Wavedata*
+wavedata_new(double sample_rate)
+{
+ Wavedata* w;
+
+ w = (Wavedata*)malloc(sizeof(Wavedata));
+
+ if (!w) {
+ return 0;
+ }
+
+ w->data_handle = 0;
+ w->table_count = 0;
+ w->tables = 0;
+ w->lookup = 0;
+ w->lookup_max = 0;
+ w->sample_rate = (float)sample_rate;
+ w->nyquist = w->sample_rate * 0.5f;
+
+ return w;
+}
+
+void
+wavedata_cleanup(Wavedata* w)
+{
+ unsigned long ti;
+ Wavetable* t;
+
+ for (ti = 0; ti < w->table_count; ti++) {
+ t = w->tables[ti];
+ if (t) {
+ if (t->samples_hf) {
+ free(t->samples_hf);
+ }
+
+ if (t->samples_lf) {
+ free(t->samples_lf);
+ }
+
+ free(t);
+ }
+ }
+
+ free(w);
+}
+
+int
+wavedata_add_table(Wavedata* w,
+ uint32_t sample_count,
+ unsigned long harmonics)
+{
+ Wavetable** tables;
+ Wavetable* t;
+ size_t bytes;
+
+ t = (Wavetable*)malloc(sizeof(Wavetable));
+
+ if (!t) {
+ return -1;
+ }
+
+ /* Extra 3 samples for interpolation */
+ bytes = (sample_count + 3) * sizeof(float);
+
+ t->samples_lf = (float*)malloc(bytes);
+
+ if (!t->samples_lf) {
+ free(t);
+ return -1;
+ }
+
+ t->samples_hf = (float*)malloc(bytes);
+
+ if (!t->samples_hf) {
+ free(t->samples_lf);
+ free(t);
+ return -1;
+ }
+
+ bytes = (w->table_count + 1) * sizeof(Wavetable*);
+ if (w->table_count == 0) {
+ tables = (Wavetable**)malloc(bytes);
+ } else {
+ tables = (Wavetable**)realloc(w->tables, bytes);
+ }
+
+ if (!tables) {
+ free(t);
+ return -1;
+ }
+
+ t->sample_count = sample_count;
+ t->harmonics = harmonics;
+
+ if (w->lookup_max < harmonics) {
+ w->lookup_max = harmonics;
+ }
+
+ tables[w->table_count] = t;
+ w->tables = tables;
+ w->table_count++;
+
+ return 0;
+}
+
+void
+wavedata_generate_tables(Wavedata* w,
+ Wavetype wavetype,
+ float gibbs_comp)
+{
+ Wavetable* t;
+ float* samples_lf;
+ float* samples_hf;
+ unsigned long h_lf;
+ unsigned long h_hf;
+ unsigned long i;
+
+ for (i = 0; i < w->table_count; i++) {
+ t = w->tables[i];
+
+ h_lf = t->harmonics;
+
+ if (i < w->table_count - 1) {
+ h_hf = w->tables[i + 1]->harmonics;
+ } else {
+ h_hf = 1;
+ }
+
+ samples_lf = t->samples_lf;
+ samples_hf = t->samples_hf;
+ samples_lf++;
+ samples_hf++;
+
+ switch (wavetype) {
+ case SAW:
+ generate_sawtooth(samples_lf, t->sample_count, h_lf, gibbs_comp);
+ generate_sawtooth(samples_hf, t->sample_count, h_hf, gibbs_comp);
+ break;
+ case SQUARE:
+ generate_square(samples_lf, t->sample_count, h_lf, gibbs_comp);
+ generate_square(samples_hf, t->sample_count, h_hf, gibbs_comp);
+ break;
+ case PARABOLA:
+ generate_parabola(samples_lf, t->sample_count, h_lf, gibbs_comp);
+ generate_parabola(samples_hf, t->sample_count, h_hf, gibbs_comp);
+ break;
+ }
+
+ /* Basic denormalization */
+ for (uint32_t s = 0; s < t->sample_count; s++) {
+ samples_lf[s] = FABSF(samples_lf[s]) < SMALLEST_FLOAT ? 0.0 : samples_lf[s];
+ }
+
+ samples_lf--;
+ samples_lf[0] = samples_lf[t->sample_count];
+ samples_lf[t->sample_count + 1] = samples_hf[1];
+ samples_lf[t->sample_count + 2] = samples_hf[2];
+
+ for (uint32_t s = 0; s < t->sample_count; s++) {
+ samples_hf[s] = FABSF(samples_hf[s]) < SMALLEST_FLOAT ? 0.0 : samples_hf[s];
+ }
+
+ samples_hf--;
+ samples_hf[0] = samples_hf[t->sample_count];
+ samples_hf[t->sample_count + 1] = samples_hf[1];
+ samples_hf[t->sample_count + 2] = samples_hf[2];
+ }
+}
+
+static void
+wavedata_write_prototype(FILE* wdat_fp,
+ const char* data_name)
+{
+ fprintf(wdat_fp, "__attribute__((visibility(\"default\")))\n");
+ fprintf(wdat_fp, "int\n");
+ fprintf(
+ wdat_fp,
+ "blop_get_%s (Wavedata * w, unsigned long sample_rate)",
+ data_name);
+}
+
+int
+wavedata_write(Wavedata* w,
+ FILE* wdat_fp,
+ const char* data_name)
+{
+ Wavetable* t = 0;
+ unsigned long table_count;
+ unsigned long i;
+ unsigned long j;
+ unsigned long s;
+ int column;
+ /*
+ * Extra table at end
+ */
+ table_count = w->table_count + 1;
+
+ fprintf(wdat_fp, "#include \"lv2/lv2plug.in/ns/lv2core/lv2.h\"\n");
+ fprintf(wdat_fp, "#include <stdio.h>\n");
+ fprintf(wdat_fp, "#include \"wavedata.h\"\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * Function prototype
+ */
+ wavedata_write_prototype(wdat_fp, data_name);
+ fprintf(wdat_fp, ";\n\n");
+ /*
+ * Fixed data and tables
+ */
+ fprintf(wdat_fp, "unsigned long ref_count = 0;\n");
+ fprintf(wdat_fp, "unsigned long first_sample_rate = 0;\n");
+ fprintf(wdat_fp, "unsigned long table_count = %ld;\n", table_count);
+ fprintf(wdat_fp, "Wavetable tables[%ld];\n", table_count);
+ fprintf(wdat_fp, "Wavetable * ptables[%ld];\n", table_count);
+ fprintf(wdat_fp, "unsigned long lookup[%ld];\n", w->lookup_max + 1);
+ fprintf(wdat_fp, "unsigned long lookup_max = %ld;\n", w->lookup_max);
+ fprintf(wdat_fp, "\n");
+ /*
+ * Sample data
+ * Each table has an extra 3 samples for interpolation
+ */
+ for (i = 0; i < w->table_count; i++) {
+ t = w->tables[i];
+
+ fprintf(wdat_fp, "static float samples_lf_%ld[%ld] = {\n", i, t->sample_count + 3);
+
+ column = 0;
+ for (s = 0; s < t->sample_count + 3 - 1; s++, column++) {
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ column = 0;
+ }
+ fprintf(wdat_fp, "%+.8ef,", t->samples_lf[s]);
+ }
+
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ }
+
+ fprintf(wdat_fp, "%+.8ef\n", t->samples_lf[s]);
+ fprintf(wdat_fp, "};\n");
+ fprintf(wdat_fp, "\n");
+
+ fprintf(wdat_fp, "static float samples_hf_%ld[%ld] = {\n", i, t->sample_count + 3);
+
+ column = 0;
+ for (s = 0; s < t->sample_count + 3 - 1; s++, column++) {
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ column = 0;
+ }
+ fprintf(wdat_fp, "%+.8ef,", t->samples_hf[s]);
+ }
+
+ if (column == 5) {
+ fprintf(wdat_fp, "\n");
+ }
+
+ fprintf(wdat_fp, "%+.8ef\n", t->samples_hf[s]);
+ fprintf(wdat_fp, "};\n");
+ fprintf(wdat_fp, "\n");
+ }
+
+ fprintf(wdat_fp, "float samples_zero[%ld];\n", t->sample_count + 3);
+ fprintf(wdat_fp, "\n");
+ /*
+ * Function to get Wavedata - the sample rate is needed to calculate
+ * frequencies and related things
+ */
+ wavedata_write_prototype(wdat_fp, data_name);
+ fprintf(wdat_fp, "\n{\n");
+ fprintf(wdat_fp, "\tWavetable * t;\n");
+ fprintf(wdat_fp, "\tunsigned long ti;\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * Sample rate must be > 0
+ */
+ fprintf(wdat_fp, "\tif (sample_rate == 0)\n");
+ fprintf(wdat_fp, "\t\treturn -1;\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * First time call - set up all sample rate dependent data
+ */
+ fprintf(wdat_fp, "\tif (first_sample_rate == 0)\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\tfirst_sample_rate = sample_rate;\n");
+ fprintf(wdat_fp, "\t\tw->sample_rate = (float) sample_rate;\n");
+ fprintf(wdat_fp, "\t\tw->nyquist = w->sample_rate / 2.0f;\n");
+ fprintf(wdat_fp, "\t\tw->table_count = table_count;\n");
+ fprintf(wdat_fp, "\t\tw->tables = ptables;\n");
+ fprintf(wdat_fp, "\t\tw->lookup = lookup;\n");
+ fprintf(wdat_fp, "\t\tw->lookup_max = lookup_max;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tfor (ti = 1; ti < table_count - 1; ti++)\n");
+ fprintf(wdat_fp, "\t\t{\n");
+ fprintf(wdat_fp, "\t\t\tt = ptables[ti];\n");
+ fprintf(wdat_fp,
+ "\t\t\tt->min_frequency = w->nyquist / (float) (ptables[ti - 1]->harmonics);\n");
+ fprintf(wdat_fp, "\t\t\tt->max_frequency = w->nyquist / (float) (t->harmonics);\n");
+ fprintf(wdat_fp, "\t\t}\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tt = w->tables[0];\n");
+ fprintf(wdat_fp, "\t\tt->min_frequency = 0.0f;\n");
+ fprintf(wdat_fp, "\t\tt->max_frequency = ptables[1]->min_frequency;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tt = ptables[table_count - 1];\n");
+ fprintf(wdat_fp, "\t\tt->min_frequency = ptables[w->table_count - 2]->max_frequency;\n");
+ fprintf(wdat_fp, "\t\tt->max_frequency = w->nyquist;\n");
+ fprintf(wdat_fp, "\t\n");
+ fprintf(wdat_fp, "\t\tfor (ti = 0; ti < w->table_count; ti++)\n");
+ fprintf(wdat_fp, "\t\t{\n");
+ fprintf(wdat_fp, "\t\t\tt = w->tables[ti];\n");
+ fprintf(wdat_fp, "\t\t\tt->phase_scale_factor = (float) (t->sample_count) / w->sample_rate;\n");
+ fprintf(wdat_fp,
+ "\t\t\tt->range_scale_factor = 1.0f / (t->max_frequency - t->min_frequency);\n");
+ fprintf(wdat_fp, "\t\t}\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\treturn 0;\n");
+ fprintf(wdat_fp, "\t}\n");
+ /*
+ * Already called at least once, so just set up wavedata
+ */
+ fprintf(wdat_fp, "\telse if (sample_rate == first_sample_rate)\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\tw->sample_rate = (float) sample_rate;\n");
+ fprintf(wdat_fp, "\t\tw->nyquist = w->sample_rate / 2.0f;\n");
+ fprintf(wdat_fp, "\t\tw->table_count = table_count;\n");
+ fprintf(wdat_fp, "\t\tw->tables = ptables;\n");
+ fprintf(wdat_fp, "\t\tw->lookup = lookup;\n");
+ fprintf(wdat_fp, "\t\tw->lookup_max = lookup_max;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\treturn 0;\n");
+ fprintf(wdat_fp, "\t}\n");
+ /*
+ * Sample rate does not match, so fail
+ *
+ * NOTE: This means multiple sample rates are not supported
+ * This should not present any problems
+ */
+ fprintf(wdat_fp, "\telse\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\treturn -1;\n");
+ fprintf(wdat_fp, "\t}\n");
+ fprintf(wdat_fp, "}\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * _init()
+ * Assemble tables and lookup
+ */
+ fprintf(wdat_fp, "static void\n");
+ fprintf(wdat_fp, "__attribute__ ((constructor))\n");
+ fprintf(wdat_fp, "init (void)\n");
+ fprintf(wdat_fp, "{\n");
+ fprintf(wdat_fp, "\tunsigned long max_harmonic;\n");
+ fprintf(wdat_fp, "\tunsigned long ti;\n");
+ fprintf(wdat_fp, "\tunsigned long li;\n");
+ fprintf(wdat_fp, "\n");
+
+ for (i = 0; i < w->table_count; i++) {
+ t = w->tables[i];
+
+ fprintf(wdat_fp, "\ttables[%ld].sample_count = %ld;\n", i, t->sample_count);
+ fprintf(wdat_fp, "\ttables[%ld].samples_lf = samples_lf_%ld;\n", i, i);
+ fprintf(wdat_fp, "\ttables[%ld].samples_hf = samples_hf_%ld;\n", i, i);
+ fprintf(wdat_fp, "\ttables[%ld].harmonics = %ld;\n", i, t->harmonics);
+ fprintf(wdat_fp, "\n");
+ }
+ /*
+ * Last table - uses same sample data as previous table for lf data,
+ * and zeroes for hf data
+ */
+ i = w->table_count - 1;
+ j = i + 1;
+ t = w->tables[i];
+ /*
+ * Zero silent samples
+ */
+ fprintf(wdat_fp, "\tfor (uint32_t s = 0; s < %ld; s++)\n", t->sample_count + 3);
+ fprintf(wdat_fp, "\t\tsamples_zero[s] = 0.0f;\n");
+ fprintf(wdat_fp, "\n");
+
+ fprintf(wdat_fp, "\ttables[%ld].sample_count = %ld;\n", j, t->sample_count);
+ fprintf(wdat_fp, "\ttables[%ld].samples_lf = samples_hf_%ld;\n", j, i);
+ fprintf(wdat_fp, "\ttables[%ld].samples_hf = samples_zero;\n", j);
+ fprintf(wdat_fp, "\ttables[%ld].harmonics = 1;\n", j);
+ fprintf(wdat_fp, "\n");
+ /*
+ * Get pointers to each wavetable and put them in the pointer array
+ */
+ fprintf(wdat_fp, "\tfor (ti = 0; ti < table_count; ti++)\n");
+ fprintf(wdat_fp, "\t\tptables[ti] = &tables[ti];\n");
+ fprintf(wdat_fp, "\n");
+ /*
+ * Shift all sample offsets forward by one sample
+ * !!! NO! Don't!
+ fprintf (wdat_fp, "\tfor (ti = 0; ti < table_count; ti++)\n");
+ fprintf (wdat_fp, "\t{\n");
+ fprintf (wdat_fp, "\t\tptables[ti]->samples_lf++;\n");
+ fprintf (wdat_fp, "\t\tptables[ti]->samples_hf++;\n");
+ fprintf (wdat_fp, "\t}\n");
+ fprintf (wdat_fp, "\n");
+ */
+ /*
+ * Table lookup vector indexed by harmonic
+ * Add lookup data to vector
+ */
+ fprintf(wdat_fp, "\tli = 0;");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\tfor (ti = table_count - 1; ti > 0; ti--)\n");
+ fprintf(wdat_fp, "\t{\n");
+ fprintf(wdat_fp, "\t\tmax_harmonic = ptables[ti]->harmonics;\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\t\tfor ( ; li <= max_harmonic; li++)\n");
+ fprintf(wdat_fp, "\t\t\tlookup[li] = ti;\n");
+ fprintf(wdat_fp, "\t}\n");
+ fprintf(wdat_fp, "\n");
+ fprintf(wdat_fp, "\tfor ( ; li <= lookup_max; li++)\n");
+ fprintf(wdat_fp, "\t\tlookup[li] = 0;\n");
+ fprintf(wdat_fp, "}\n");
+
+ return 0;
+}
+
+void
+generate_sawtooth(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp)
+{
+ double phase_scale = 2.0 * M_PI / (double)sample_count;
+ float scale = 2.0f / M_PI;
+ unsigned long i;
+ unsigned long h;
+ double mhf;
+ double hf;
+ double k;
+ double m;
+ double phase;
+ double partial;
+
+ if (gibbs_comp > 0.0f) {
+ /* Degree of Gibbs Effect compensation */
+ mhf = (double)harmonics;
+ k = M_PI * (double)gibbs_comp / mhf;
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] = 0.0f;
+ }
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+
+ /* Gibbs Effect compensation - Hamming window */
+ /* Modified slightly for smoother fade at highest frequencies */
+ m = 0.54 + 0.46 * cos((hf - 0.5 / mhf) * k);
+
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ partial = (m / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ }
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] *= scale;
+ }
+ } else {
+ /* Allow overshoot */
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ samples[i] = 0.0f;
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+ partial = (1.0 / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ samples[i] *= scale;
+ }
+ }
+}
+
+void
+generate_square(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp)
+{
+ double phase_scale = 2.0 * M_PI / (double)sample_count;
+ float scale = 4.0f / M_PI;
+ unsigned long i;
+ unsigned long h;
+ double mhf;
+ double hf;
+ double k;
+ double m;
+ double phase;
+ double partial;
+
+ if (gibbs_comp > 0.0f) {
+ /* Degree of Gibbs Effect compensation */
+ mhf = (double)harmonics;
+ k = M_PI * (double)gibbs_comp / mhf;
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] = 0.0f;
+ }
+
+ for (h = 1; h <= harmonics; h += 2) {
+ hf = (double)h;
+
+ /* Gibbs Effect compensation - Hamming window */
+ /* Modified slightly for smoother fade at highest frequencies */
+ m = 0.54 + 0.46 * cos((hf - 0.5 / pow(mhf, 2.2)) * k);
+
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ partial = (m / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ }
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] *= scale;
+ }
+ } else {
+ /* Allow overshoot */
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ samples[i] = 0.0f;
+
+ for (h = 1; h <= harmonics; h += 2) {
+ hf = (double)h;
+ partial = (1.0 / hf) * sin(phase * hf);
+ samples[i] += (float)partial;
+ }
+ samples[i] *= scale;
+ }
+ }
+}
+
+void
+generate_parabola(float* samples,
+ uint32_t sample_count,
+ unsigned long harmonics,
+ float gibbs_comp)
+{
+ double phase_scale = 2.0 * M_PI / (double)sample_count;
+ float scale = 2.0f / (M_PI * M_PI);
+ unsigned long i;
+ unsigned long h;
+ //double mhf;
+ double hf;
+ //double k;
+ //double m;
+ double phase;
+ double partial;
+ double sign;
+
+ if (gibbs_comp > 0.0f) {
+ /* Degree of Gibbs Effect compensation */
+ //mhf = (double)harmonics;
+ //k = M_PI * (double)gibbs_comp / mhf;
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] = 0.0f;
+ }
+
+ sign = -1.0;
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+
+ /* Gibbs Effect compensation - Hamming window */
+ /* Modified slightly for smoother fade at highest frequencies */
+ //m = 0.54 + 0.46 * cos((hf - 0.5 / mhf) * k);
+
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ partial = (sign * 4.0 / (hf * hf)) * cos(phase * hf);
+ samples[i] += (float)partial;
+ }
+ sign = -sign;
+ }
+
+ for (i = 0; i < sample_count; i++) {
+ samples[i] *= scale;
+ }
+ } else {
+ /* Allow overshoot */
+ for (i = 0; i < sample_count; i++) {
+ phase = (double)i * phase_scale;
+ samples[i] = 0.0f;
+ sign = -1.0;
+
+ for (h = 1; h <= harmonics; h++) {
+ hf = (double)h;
+ partial = (sign * 4.0 / (hf * hf)) * cos(phase * hf);
+ samples[i] += (float)partial;
+ sign = -sign;
+ }
+ samples[i] *= scale;
+ }
+ }
+}
diff --git a/.gitignore b/waflib/.gitignore
index 8d35cb3..8d35cb3 100644
--- a/.gitignore
+++ b/waflib/.gitignore
diff --git a/Build.py b/waflib/Build.py
index 8143dbc..8143dbc 100644
--- a/Build.py
+++ b/waflib/Build.py
diff --git a/waflib/COPYING b/waflib/COPYING
new file mode 100644
index 0000000..a4147d2
--- /dev/null
+++ b/waflib/COPYING
@@ -0,0 +1,25 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/ConfigSet.py b/waflib/ConfigSet.py
index 901fba6..901fba6 100644
--- a/ConfigSet.py
+++ b/waflib/ConfigSet.py
diff --git a/Configure.py b/waflib/Configure.py
index db09c0e..db09c0e 100644
--- a/Configure.py
+++ b/waflib/Configure.py
diff --git a/Context.py b/waflib/Context.py
index 876ea46..876ea46 100644
--- a/Context.py
+++ b/waflib/Context.py
diff --git a/Errors.py b/waflib/Errors.py
index bf75c1b..bf75c1b 100644
--- a/Errors.py
+++ b/waflib/Errors.py
diff --git a/Logs.py b/waflib/Logs.py
index 11dc34f..11dc34f 100644
--- a/Logs.py
+++ b/waflib/Logs.py
diff --git a/Node.py b/waflib/Node.py
index 4ac1ea8..4ac1ea8 100644
--- a/Node.py
+++ b/waflib/Node.py
diff --git a/Options.py b/waflib/Options.py
index ad802d4..ad802d4 100644
--- a/Options.py
+++ b/waflib/Options.py
diff --git a/README.md b/waflib/README.md
index c5361b9..c5361b9 100644
--- a/README.md
+++ b/waflib/README.md
diff --git a/Runner.py b/waflib/Runner.py
index 5d27669..5d27669 100644
--- a/Runner.py
+++ b/waflib/Runner.py
diff --git a/Scripting.py b/waflib/Scripting.py
index ae17a8b..ae17a8b 100644
--- a/Scripting.py
+++ b/waflib/Scripting.py
diff --git a/Task.py b/waflib/Task.py
index cb49a73..cb49a73 100644
--- a/Task.py
+++ b/waflib/Task.py
diff --git a/TaskGen.py b/waflib/TaskGen.py
index 532b7d5..532b7d5 100644
--- a/TaskGen.py
+++ b/waflib/TaskGen.py
diff --git a/Tools/__init__.py b/waflib/Tools/__init__.py
index 079df35..079df35 100644
--- a/Tools/__init__.py
+++ b/waflib/Tools/__init__.py
diff --git a/Tools/ar.py b/waflib/Tools/ar.py
index b39b645..b39b645 100644
--- a/Tools/ar.py
+++ b/waflib/Tools/ar.py
diff --git a/Tools/asm.py b/waflib/Tools/asm.py
index b6f26fb..b6f26fb 100644
--- a/Tools/asm.py
+++ b/waflib/Tools/asm.py
diff --git a/Tools/bison.py b/waflib/Tools/bison.py
index eef56dc..eef56dc 100644
--- a/Tools/bison.py
+++ b/waflib/Tools/bison.py
diff --git a/Tools/c.py b/waflib/Tools/c.py
index effd6b6..effd6b6 100644
--- a/Tools/c.py
+++ b/waflib/Tools/c.py
diff --git a/Tools/c_aliases.py b/waflib/Tools/c_aliases.py
index c9d5369..c9d5369 100644
--- a/Tools/c_aliases.py
+++ b/waflib/Tools/c_aliases.py
diff --git a/Tools/c_config.py b/waflib/Tools/c_config.py
index d546be9..d546be9 100644
--- a/Tools/c_config.py
+++ b/waflib/Tools/c_config.py
diff --git a/Tools/c_osx.py b/waflib/Tools/c_osx.py
index f70b128..f70b128 100644
--- a/Tools/c_osx.py
+++ b/waflib/Tools/c_osx.py
diff --git a/Tools/c_preproc.py b/waflib/Tools/c_preproc.py
index 68e5f5a..68e5f5a 100644
--- a/Tools/c_preproc.py
+++ b/waflib/Tools/c_preproc.py
diff --git a/Tools/c_tests.py b/waflib/Tools/c_tests.py
index f858df5..f858df5 100644
--- a/Tools/c_tests.py
+++ b/waflib/Tools/c_tests.py
diff --git a/Tools/ccroot.py b/waflib/Tools/ccroot.py
index 579d5b2..579d5b2 100644
--- a/Tools/ccroot.py
+++ b/waflib/Tools/ccroot.py
diff --git a/Tools/clang.py b/waflib/Tools/clang.py
index 3828e39..3828e39 100644
--- a/Tools/clang.py
+++ b/waflib/Tools/clang.py
diff --git a/Tools/clangxx.py b/waflib/Tools/clangxx.py
index 152013c..152013c 100644
--- a/Tools/clangxx.py
+++ b/waflib/Tools/clangxx.py
diff --git a/Tools/compiler_c.py b/waflib/Tools/compiler_c.py
index 2dba3f8..2dba3f8 100644
--- a/Tools/compiler_c.py
+++ b/waflib/Tools/compiler_c.py
diff --git a/Tools/compiler_cxx.py b/waflib/Tools/compiler_cxx.py
index 1af65a2..1af65a2 100644
--- a/Tools/compiler_cxx.py
+++ b/waflib/Tools/compiler_cxx.py
diff --git a/Tools/compiler_d.py b/waflib/Tools/compiler_d.py
index 43bb1f6..43bb1f6 100644
--- a/Tools/compiler_d.py
+++ b/waflib/Tools/compiler_d.py
diff --git a/Tools/compiler_fc.py b/waflib/Tools/compiler_fc.py
index 96b58e7..96b58e7 100644
--- a/Tools/compiler_fc.py
+++ b/waflib/Tools/compiler_fc.py
diff --git a/Tools/cs.py b/waflib/Tools/cs.py
index aecca6d..aecca6d 100644
--- a/Tools/cs.py
+++ b/waflib/Tools/cs.py
diff --git a/Tools/cxx.py b/waflib/Tools/cxx.py
index 194fad7..194fad7 100644
--- a/Tools/cxx.py
+++ b/waflib/Tools/cxx.py
diff --git a/Tools/d.py b/waflib/Tools/d.py
index e4cf73b..e4cf73b 100644
--- a/Tools/d.py
+++ b/waflib/Tools/d.py
diff --git a/Tools/d_config.py b/waflib/Tools/d_config.py
index 6637556..6637556 100644
--- a/Tools/d_config.py
+++ b/waflib/Tools/d_config.py
diff --git a/Tools/d_scan.py b/waflib/Tools/d_scan.py
index 4e807a6..4e807a6 100644
--- a/Tools/d_scan.py
+++ b/waflib/Tools/d_scan.py
diff --git a/Tools/dbus.py b/waflib/Tools/dbus.py
index d520f1c..d520f1c 100644
--- a/Tools/dbus.py
+++ b/waflib/Tools/dbus.py
diff --git a/Tools/dmd.py b/waflib/Tools/dmd.py
index 8917ca1..8917ca1 100644
--- a/Tools/dmd.py
+++ b/waflib/Tools/dmd.py
diff --git a/Tools/errcheck.py b/waflib/Tools/errcheck.py
index de8d75a..de8d75a 100644
--- a/Tools/errcheck.py
+++ b/waflib/Tools/errcheck.py
diff --git a/Tools/fc.py b/waflib/Tools/fc.py
index fd4d39c..fd4d39c 100644
--- a/Tools/fc.py
+++ b/waflib/Tools/fc.py
diff --git a/Tools/fc_config.py b/waflib/Tools/fc_config.py
index dc5e5c9..dc5e5c9 100644
--- a/Tools/fc_config.py
+++ b/waflib/Tools/fc_config.py
diff --git a/Tools/fc_scan.py b/waflib/Tools/fc_scan.py
index 0824c92..0824c92 100644
--- a/Tools/fc_scan.py
+++ b/waflib/Tools/fc_scan.py
diff --git a/Tools/flex.py b/waflib/Tools/flex.py
index 2256657..2256657 100644
--- a/Tools/flex.py
+++ b/waflib/Tools/flex.py
diff --git a/Tools/g95.py b/waflib/Tools/g95.py
index f69ba4f..f69ba4f 100644
--- a/Tools/g95.py
+++ b/waflib/Tools/g95.py
diff --git a/Tools/gas.py b/waflib/Tools/gas.py
index 77afed7..77afed7 100644
--- a/Tools/gas.py
+++ b/waflib/Tools/gas.py
diff --git a/Tools/gcc.py b/waflib/Tools/gcc.py
index acdd473..acdd473 100644
--- a/Tools/gcc.py
+++ b/waflib/Tools/gcc.py
diff --git a/Tools/gdc.py b/waflib/Tools/gdc.py
index d89a66d..d89a66d 100644
--- a/Tools/gdc.py
+++ b/waflib/Tools/gdc.py
diff --git a/Tools/gfortran.py b/waflib/Tools/gfortran.py
index 1050667..1050667 100644
--- a/Tools/gfortran.py
+++ b/waflib/Tools/gfortran.py
diff --git a/Tools/glib2.py b/waflib/Tools/glib2.py
index 949fe37..949fe37 100644
--- a/Tools/glib2.py
+++ b/waflib/Tools/glib2.py
diff --git a/Tools/gnu_dirs.py b/waflib/Tools/gnu_dirs.py
index 2847071..2847071 100644
--- a/Tools/gnu_dirs.py
+++ b/waflib/Tools/gnu_dirs.py
diff --git a/Tools/gxx.py b/waflib/Tools/gxx.py
index 22c5d26..22c5d26 100644
--- a/Tools/gxx.py
+++ b/waflib/Tools/gxx.py
diff --git a/Tools/icc.py b/waflib/Tools/icc.py
index b6492c8..b6492c8 100644
--- a/Tools/icc.py
+++ b/waflib/Tools/icc.py
diff --git a/Tools/icpc.py b/waflib/Tools/icpc.py
index 8a6cc6c..8a6cc6c 100644
--- a/Tools/icpc.py
+++ b/waflib/Tools/icpc.py
diff --git a/Tools/ifort.py b/waflib/Tools/ifort.py
index 17d3052..17d3052 100644
--- a/Tools/ifort.py
+++ b/waflib/Tools/ifort.py
diff --git a/Tools/intltool.py b/waflib/Tools/intltool.py
index af95ba8..af95ba8 100644
--- a/Tools/intltool.py
+++ b/waflib/Tools/intltool.py
diff --git a/Tools/irixcc.py b/waflib/Tools/irixcc.py
index c3ae1ac..c3ae1ac 100644
--- a/Tools/irixcc.py
+++ b/waflib/Tools/irixcc.py
diff --git a/Tools/javaw.py b/waflib/Tools/javaw.py
index 9daed39..9daed39 100644
--- a/Tools/javaw.py
+++ b/waflib/Tools/javaw.py
diff --git a/Tools/ldc2.py b/waflib/Tools/ldc2.py
index a51c344..a51c344 100644
--- a/Tools/ldc2.py
+++ b/waflib/Tools/ldc2.py
diff --git a/Tools/lua.py b/waflib/Tools/lua.py
index 15a333a..15a333a 100644
--- a/Tools/lua.py
+++ b/waflib/Tools/lua.py
diff --git a/Tools/md5_tstamp.py b/waflib/Tools/md5_tstamp.py
index 2a58792..2a58792 100644
--- a/Tools/md5_tstamp.py
+++ b/waflib/Tools/md5_tstamp.py
diff --git a/Tools/msvc.py b/waflib/Tools/msvc.py
index ff58449..ff58449 100644
--- a/Tools/msvc.py
+++ b/waflib/Tools/msvc.py
diff --git a/Tools/nasm.py b/waflib/Tools/nasm.py
index 411d582..411d582 100644
--- a/Tools/nasm.py
+++ b/waflib/Tools/nasm.py
diff --git a/Tools/nobuild.py b/waflib/Tools/nobuild.py
index 2e4b055..2e4b055 100644
--- a/Tools/nobuild.py
+++ b/waflib/Tools/nobuild.py
diff --git a/Tools/perl.py b/waflib/Tools/perl.py
index 32b03fb..32b03fb 100644
--- a/Tools/perl.py
+++ b/waflib/Tools/perl.py
diff --git a/Tools/python.py b/waflib/Tools/python.py
index 01a2c9a..01a2c9a 100644
--- a/Tools/python.py
+++ b/waflib/Tools/python.py
diff --git a/Tools/qt5.py b/waflib/Tools/qt5.py
index 9f43280..9f43280 100644
--- a/Tools/qt5.py
+++ b/waflib/Tools/qt5.py
diff --git a/Tools/ruby.py b/waflib/Tools/ruby.py
index 8d92a79..8d92a79 100644
--- a/Tools/ruby.py
+++ b/waflib/Tools/ruby.py
diff --git a/Tools/suncc.py b/waflib/Tools/suncc.py
index 33d34fc..33d34fc 100644
--- a/Tools/suncc.py
+++ b/waflib/Tools/suncc.py
diff --git a/Tools/suncxx.py b/waflib/Tools/suncxx.py
index 3b384f6..3b384f6 100644
--- a/Tools/suncxx.py
+++ b/waflib/Tools/suncxx.py
diff --git a/Tools/tex.py b/waflib/Tools/tex.py
index eaf9fdb..eaf9fdb 100644
--- a/Tools/tex.py
+++ b/waflib/Tools/tex.py
diff --git a/Tools/vala.py b/waflib/Tools/vala.py
index 822ec50..822ec50 100644
--- a/Tools/vala.py
+++ b/waflib/Tools/vala.py
diff --git a/Tools/waf_unit_test.py b/waflib/Tools/waf_unit_test.py
index 74d6c05..74d6c05 100644
--- a/Tools/waf_unit_test.py
+++ b/waflib/Tools/waf_unit_test.py
diff --git a/Tools/winres.py b/waflib/Tools/winres.py
index 9be1ed6..9be1ed6 100644
--- a/Tools/winres.py
+++ b/waflib/Tools/winres.py
diff --git a/Tools/xlc.py b/waflib/Tools/xlc.py
index 134dd41..134dd41 100644
--- a/Tools/xlc.py
+++ b/waflib/Tools/xlc.py
diff --git a/Tools/xlcxx.py b/waflib/Tools/xlcxx.py
index 76aa59b..76aa59b 100644
--- a/Tools/xlcxx.py
+++ b/waflib/Tools/xlcxx.py
diff --git a/Utils.py b/waflib/Utils.py
index 4b808a8..4b808a8 100644
--- a/Utils.py
+++ b/waflib/Utils.py
diff --git a/__init__.py b/waflib/__init__.py
index 079df35..079df35 100644
--- a/__init__.py
+++ b/waflib/__init__.py
diff --git a/ansiterm.py b/waflib/ansiterm.py
index 027f0ad..027f0ad 100644
--- a/ansiterm.py
+++ b/waflib/ansiterm.py
diff --git a/extras/__init__.py b/waflib/extras/__init__.py
index c8a3c34..c8a3c34 100644
--- a/extras/__init__.py
+++ b/waflib/extras/__init__.py
diff --git a/extras/autowaf.py b/waflib/extras/autowaf.py
index 51077d1..51077d1 100644
--- a/extras/autowaf.py
+++ b/waflib/extras/autowaf.py
diff --git a/extras/batched_cc.py b/waflib/extras/batched_cc.py
index aad2872..aad2872 100644
--- a/extras/batched_cc.py
+++ b/waflib/extras/batched_cc.py
diff --git a/extras/biber.py b/waflib/extras/biber.py
index fd9db4e..fd9db4e 100644
--- a/extras/biber.py
+++ b/waflib/extras/biber.py
diff --git a/extras/bjam.py b/waflib/extras/bjam.py
index 8e04d3a..8e04d3a 100644
--- a/extras/bjam.py
+++ b/waflib/extras/bjam.py
diff --git a/extras/blender.py b/waflib/extras/blender.py
index e5efc28..e5efc28 100644
--- a/extras/blender.py
+++ b/waflib/extras/blender.py
diff --git a/extras/boo.py b/waflib/extras/boo.py
index 06623d4..06623d4 100644
--- a/extras/boo.py
+++ b/waflib/extras/boo.py
diff --git a/extras/boost.py b/waflib/extras/boost.py
index c2aaaa9..c2aaaa9 100644
--- a/extras/boost.py
+++ b/waflib/extras/boost.py
diff --git a/extras/build_file_tracker.py b/waflib/extras/build_file_tracker.py
index c4f26fd..c4f26fd 100644
--- a/extras/build_file_tracker.py
+++ b/waflib/extras/build_file_tracker.py
diff --git a/extras/build_logs.py b/waflib/extras/build_logs.py
index cdf8ed0..cdf8ed0 100644
--- a/extras/build_logs.py
+++ b/waflib/extras/build_logs.py
diff --git a/extras/buildcopy.py b/waflib/extras/buildcopy.py
index eaff7e6..eaff7e6 100644
--- a/extras/buildcopy.py
+++ b/waflib/extras/buildcopy.py
diff --git a/extras/c_bgxlc.py b/waflib/extras/c_bgxlc.py
index 6e3eaf7..6e3eaf7 100644
--- a/extras/c_bgxlc.py
+++ b/waflib/extras/c_bgxlc.py
diff --git a/extras/c_dumbpreproc.py b/waflib/extras/c_dumbpreproc.py
index ce9e1a4..ce9e1a4 100644
--- a/extras/c_dumbpreproc.py
+++ b/waflib/extras/c_dumbpreproc.py
diff --git a/extras/c_emscripten.py b/waflib/extras/c_emscripten.py
index e1ac494..e1ac494 100644
--- a/extras/c_emscripten.py
+++ b/waflib/extras/c_emscripten.py
diff --git a/extras/c_nec.py b/waflib/extras/c_nec.py
index 96bfae4..96bfae4 100644
--- a/extras/c_nec.py
+++ b/waflib/extras/c_nec.py
diff --git a/extras/cabal.py b/waflib/extras/cabal.py
index e10a0d1..e10a0d1 100644
--- a/extras/cabal.py
+++ b/waflib/extras/cabal.py
diff --git a/extras/cfg_altoptions.py b/waflib/extras/cfg_altoptions.py
index 47b1189..47b1189 100644
--- a/extras/cfg_altoptions.py
+++ b/waflib/extras/cfg_altoptions.py
diff --git a/extras/clang_compilation_database.py b/waflib/extras/clang_compilation_database.py
index 4d9b5e2..4d9b5e2 100644
--- a/extras/clang_compilation_database.py
+++ b/waflib/extras/clang_compilation_database.py
diff --git a/extras/codelite.py b/waflib/extras/codelite.py
index 523302c..523302c 100644
--- a/extras/codelite.py
+++ b/waflib/extras/codelite.py
diff --git a/extras/color_gcc.py b/waflib/extras/color_gcc.py
index b68c5eb..b68c5eb 100644
--- a/extras/color_gcc.py
+++ b/waflib/extras/color_gcc.py
diff --git a/extras/color_rvct.py b/waflib/extras/color_rvct.py
index f89ccbd..f89ccbd 100644
--- a/extras/color_rvct.py
+++ b/waflib/extras/color_rvct.py
diff --git a/extras/compat15.py b/waflib/extras/compat15.py
index 0e74df8..0e74df8 100644
--- a/extras/compat15.py
+++ b/waflib/extras/compat15.py
diff --git a/extras/cppcheck.py b/waflib/extras/cppcheck.py
index 13ff424..13ff424 100644
--- a/extras/cppcheck.py
+++ b/waflib/extras/cppcheck.py
diff --git a/extras/cpplint.py b/waflib/extras/cpplint.py
index 8cdd6dd..8cdd6dd 100644
--- a/extras/cpplint.py
+++ b/waflib/extras/cpplint.py
diff --git a/extras/cross_gnu.py b/waflib/extras/cross_gnu.py
index 309f53b..309f53b 100644
--- a/extras/cross_gnu.py
+++ b/waflib/extras/cross_gnu.py
diff --git a/extras/cython.py b/waflib/extras/cython.py
index 591c274..591c274 100644
--- a/extras/cython.py
+++ b/waflib/extras/cython.py
diff --git a/extras/dcc.py b/waflib/extras/dcc.py
index c1a57c0..c1a57c0 100644
--- a/extras/dcc.py
+++ b/waflib/extras/dcc.py
diff --git a/extras/distnet.py b/waflib/extras/distnet.py
index ff3ed8e..ff3ed8e 100644
--- a/extras/distnet.py
+++ b/waflib/extras/distnet.py
diff --git a/extras/doxygen.py b/waflib/extras/doxygen.py
index 28f56e9..28f56e9 100644
--- a/extras/doxygen.py
+++ b/waflib/extras/doxygen.py
diff --git a/extras/dpapi.py b/waflib/extras/dpapi.py
index b94d482..b94d482 100644
--- a/extras/dpapi.py
+++ b/waflib/extras/dpapi.py
diff --git a/extras/eclipse.py b/waflib/extras/eclipse.py
index bb78741..bb78741 100644
--- a/extras/eclipse.py
+++ b/waflib/extras/eclipse.py
diff --git a/extras/erlang.py b/waflib/extras/erlang.py
index 0b93d9a..0b93d9a 100644
--- a/extras/erlang.py
+++ b/waflib/extras/erlang.py
diff --git a/extras/fast_partial.py b/waflib/extras/fast_partial.py
index d5b6144..d5b6144 100644
--- a/extras/fast_partial.py
+++ b/waflib/extras/fast_partial.py
diff --git a/extras/fc_bgxlf.py b/waflib/extras/fc_bgxlf.py
index cca1810..cca1810 100644
--- a/extras/fc_bgxlf.py
+++ b/waflib/extras/fc_bgxlf.py
diff --git a/extras/fc_cray.py b/waflib/extras/fc_cray.py
index da733fa..da733fa 100644
--- a/extras/fc_cray.py
+++ b/waflib/extras/fc_cray.py
diff --git a/extras/fc_nag.py b/waflib/extras/fc_nag.py
index edcb218..edcb218 100644
--- a/extras/fc_nag.py
+++ b/waflib/extras/fc_nag.py
diff --git a/extras/fc_nec.py b/waflib/extras/fc_nec.py
index 67c8680..67c8680 100644
--- a/extras/fc_nec.py
+++ b/waflib/extras/fc_nec.py
diff --git a/extras/fc_nfort.py b/waflib/extras/fc_nfort.py
index c25886b..c25886b 100644
--- a/extras/fc_nfort.py
+++ b/waflib/extras/fc_nfort.py
diff --git a/extras/fc_open64.py b/waflib/extras/fc_open64.py
index 413719f..413719f 100644
--- a/extras/fc_open64.py
+++ b/waflib/extras/fc_open64.py
diff --git a/extras/fc_pgfortran.py b/waflib/extras/fc_pgfortran.py
index afb2817..afb2817 100644
--- a/extras/fc_pgfortran.py
+++ b/waflib/extras/fc_pgfortran.py
diff --git a/extras/fc_solstudio.py b/waflib/extras/fc_solstudio.py
index 53766df..53766df 100644
--- a/extras/fc_solstudio.py
+++ b/waflib/extras/fc_solstudio.py
diff --git a/extras/fc_xlf.py b/waflib/extras/fc_xlf.py
index 5a3da03..5a3da03 100644
--- a/extras/fc_xlf.py
+++ b/waflib/extras/fc_xlf.py
diff --git a/extras/file_to_object.py b/waflib/extras/file_to_object.py
index 1393b51..1393b51 100644
--- a/extras/file_to_object.py
+++ b/waflib/extras/file_to_object.py
diff --git a/extras/fluid.py b/waflib/extras/fluid.py
index 4814a35..4814a35 100644
--- a/extras/fluid.py
+++ b/waflib/extras/fluid.py
diff --git a/extras/freeimage.py b/waflib/extras/freeimage.py
index f27e525..f27e525 100644
--- a/extras/freeimage.py
+++ b/waflib/extras/freeimage.py
diff --git a/extras/fsb.py b/waflib/extras/fsb.py
index 1b8f398..1b8f398 100644
--- a/extras/fsb.py
+++ b/waflib/extras/fsb.py
diff --git a/extras/fsc.py b/waflib/extras/fsc.py
index c67e70b..c67e70b 100644
--- a/extras/fsc.py
+++ b/waflib/extras/fsc.py
diff --git a/extras/gccdeps.py b/waflib/extras/gccdeps.py
index bfabe72..bfabe72 100644
--- a/extras/gccdeps.py
+++ b/waflib/extras/gccdeps.py
diff --git a/extras/gdbus.py b/waflib/extras/gdbus.py
index 0e0476e..0e0476e 100644
--- a/extras/gdbus.py
+++ b/waflib/extras/gdbus.py
diff --git a/extras/gob2.py b/waflib/extras/gob2.py
index b4fa3b9..b4fa3b9 100644
--- a/extras/gob2.py
+++ b/waflib/extras/gob2.py
diff --git a/extras/halide.py b/waflib/extras/halide.py
index 6078e38..6078e38 100644
--- a/extras/halide.py
+++ b/waflib/extras/halide.py
diff --git a/extras/javatest.py b/waflib/extras/javatest.py
index 979b8d8..979b8d8 100755
--- a/extras/javatest.py
+++ b/waflib/extras/javatest.py
diff --git a/extras/kde4.py b/waflib/extras/kde4.py
index aed9bfb..aed9bfb 100644
--- a/extras/kde4.py
+++ b/waflib/extras/kde4.py
diff --git a/extras/local_rpath.py b/waflib/extras/local_rpath.py
index b2507e1..b2507e1 100644
--- a/extras/local_rpath.py
+++ b/waflib/extras/local_rpath.py
diff --git a/extras/lv2.py b/waflib/extras/lv2.py
index ffcb2e7..ffcb2e7 100644
--- a/extras/lv2.py
+++ b/waflib/extras/lv2.py
diff --git a/extras/make.py b/waflib/extras/make.py
index 933d9ca..933d9ca 100644
--- a/extras/make.py
+++ b/waflib/extras/make.py
diff --git a/extras/midl.py b/waflib/extras/midl.py
index 43e6cf9..43e6cf9 100644
--- a/extras/midl.py
+++ b/waflib/extras/midl.py
diff --git a/extras/msvcdeps.py b/waflib/extras/msvcdeps.py
index fc1ecd4..fc1ecd4 100644
--- a/extras/msvcdeps.py
+++ b/waflib/extras/msvcdeps.py
diff --git a/extras/msvs.py b/waflib/extras/msvs.py
index 8aa2db0..8aa2db0 100644
--- a/extras/msvs.py
+++ b/waflib/extras/msvs.py
diff --git a/extras/netcache_client.py b/waflib/extras/netcache_client.py
index dc49048..dc49048 100644
--- a/extras/netcache_client.py
+++ b/waflib/extras/netcache_client.py
diff --git a/extras/objcopy.py b/waflib/extras/objcopy.py
index 82d8359..82d8359 100644
--- a/extras/objcopy.py
+++ b/waflib/extras/objcopy.py
diff --git a/extras/ocaml.py b/waflib/extras/ocaml.py
index 7d785c6..7d785c6 100644
--- a/extras/ocaml.py
+++ b/waflib/extras/ocaml.py
diff --git a/extras/package.py b/waflib/extras/package.py
index c06498e..c06498e 100644
--- a/extras/package.py
+++ b/waflib/extras/package.py
diff --git a/extras/parallel_debug.py b/waflib/extras/parallel_debug.py
index 4ffec5e..4ffec5e 100644
--- a/extras/parallel_debug.py
+++ b/waflib/extras/parallel_debug.py
diff --git a/extras/pch.py b/waflib/extras/pch.py
index 103e752..103e752 100644
--- a/extras/pch.py
+++ b/waflib/extras/pch.py
diff --git a/extras/pep8.py b/waflib/extras/pep8.py
index 676beed..676beed 100644
--- a/extras/pep8.py
+++ b/waflib/extras/pep8.py
diff --git a/extras/pgicc.py b/waflib/extras/pgicc.py
index f8068d5..f8068d5 100644
--- a/extras/pgicc.py
+++ b/waflib/extras/pgicc.py
diff --git a/extras/pgicxx.py b/waflib/extras/pgicxx.py
index eae121c..eae121c 100644
--- a/extras/pgicxx.py
+++ b/waflib/extras/pgicxx.py
diff --git a/extras/proc.py b/waflib/extras/proc.py
index 764abec..764abec 100644
--- a/extras/proc.py
+++ b/waflib/extras/proc.py
diff --git a/extras/protoc.py b/waflib/extras/protoc.py
index 839c510..839c510 100644
--- a/extras/protoc.py
+++ b/waflib/extras/protoc.py
diff --git a/extras/pyqt5.py b/waflib/extras/pyqt5.py
index 80f43b8..80f43b8 100644
--- a/extras/pyqt5.py
+++ b/waflib/extras/pyqt5.py
diff --git a/extras/pytest.py b/waflib/extras/pytest.py
index 7dd5a1a..7dd5a1a 100644
--- a/extras/pytest.py
+++ b/waflib/extras/pytest.py
diff --git a/extras/qnxnto.py b/waflib/extras/qnxnto.py
index 1158124..1158124 100644
--- a/extras/qnxnto.py
+++ b/waflib/extras/qnxnto.py
diff --git a/extras/qt4.py b/waflib/extras/qt4.py
index d19a4dd..d19a4dd 100644
--- a/extras/qt4.py
+++ b/waflib/extras/qt4.py
diff --git a/extras/relocation.py b/waflib/extras/relocation.py
index 7e821f4..7e821f4 100644
--- a/extras/relocation.py
+++ b/waflib/extras/relocation.py
diff --git a/extras/remote.py b/waflib/extras/remote.py
index f43b600..f43b600 100644
--- a/extras/remote.py
+++ b/waflib/extras/remote.py
diff --git a/extras/resx.py b/waflib/extras/resx.py
index caf4d31..caf4d31 100644
--- a/extras/resx.py
+++ b/waflib/extras/resx.py
diff --git a/extras/review.py b/waflib/extras/review.py
index 561e062..561e062 100644
--- a/extras/review.py
+++ b/waflib/extras/review.py
diff --git a/extras/rst.py b/waflib/extras/rst.py
index f3c3a5e..f3c3a5e 100644
--- a/extras/rst.py
+++ b/waflib/extras/rst.py
diff --git a/extras/run_do_script.py b/waflib/extras/run_do_script.py
index 07e3aa2..07e3aa2 100644
--- a/extras/run_do_script.py
+++ b/waflib/extras/run_do_script.py
diff --git a/extras/run_m_script.py b/waflib/extras/run_m_script.py
index b5f27eb..b5f27eb 100644
--- a/extras/run_m_script.py
+++ b/waflib/extras/run_m_script.py
diff --git a/extras/run_py_script.py b/waflib/extras/run_py_script.py
index 3670381..3670381 100644
--- a/extras/run_py_script.py
+++ b/waflib/extras/run_py_script.py
diff --git a/extras/run_r_script.py b/waflib/extras/run_r_script.py
index b0d8f2b..b0d8f2b 100644
--- a/extras/run_r_script.py
+++ b/waflib/extras/run_r_script.py
diff --git a/extras/sas.py b/waflib/extras/sas.py
index 754c614..754c614 100644
--- a/extras/sas.py
+++ b/waflib/extras/sas.py
diff --git a/extras/satellite_assembly.py b/waflib/extras/satellite_assembly.py
index 005eb07..005eb07 100644
--- a/extras/satellite_assembly.py
+++ b/waflib/extras/satellite_assembly.py
diff --git a/extras/scala.py b/waflib/extras/scala.py
index a9880f0..a9880f0 100644
--- a/extras/scala.py
+++ b/waflib/extras/scala.py
diff --git a/extras/slow_qt4.py b/waflib/extras/slow_qt4.py
index ec7880b..ec7880b 100644
--- a/extras/slow_qt4.py
+++ b/waflib/extras/slow_qt4.py
diff --git a/extras/softlink_libs.py b/waflib/extras/softlink_libs.py
index 50c777f..50c777f 100644
--- a/extras/softlink_libs.py
+++ b/waflib/extras/softlink_libs.py
diff --git a/extras/stale.py b/waflib/extras/stale.py
index cac3f46..cac3f46 100644
--- a/extras/stale.py
+++ b/waflib/extras/stale.py
diff --git a/extras/stracedeps.py b/waflib/extras/stracedeps.py
index 37d82cb..37d82cb 100644
--- a/extras/stracedeps.py
+++ b/waflib/extras/stracedeps.py
diff --git a/extras/swig.py b/waflib/extras/swig.py
index 740ab46..740ab46 100644
--- a/extras/swig.py
+++ b/waflib/extras/swig.py
diff --git a/extras/syms.py b/waflib/extras/syms.py
index dfa0059..dfa0059 100644
--- a/extras/syms.py
+++ b/waflib/extras/syms.py
diff --git a/extras/ticgt.py b/waflib/extras/ticgt.py
index f43a7ea..f43a7ea 100644
--- a/extras/ticgt.py
+++ b/waflib/extras/ticgt.py
diff --git a/extras/unity.py b/waflib/extras/unity.py
index 78128ed..78128ed 100644
--- a/extras/unity.py
+++ b/waflib/extras/unity.py
diff --git a/extras/use_config.py b/waflib/extras/use_config.py
index ef5129f..ef5129f 100644
--- a/extras/use_config.py
+++ b/waflib/extras/use_config.py
diff --git a/extras/valadoc.py b/waflib/extras/valadoc.py
index c50f69e..c50f69e 100644
--- a/extras/valadoc.py
+++ b/waflib/extras/valadoc.py
diff --git a/extras/waf_xattr.py b/waflib/extras/waf_xattr.py
index 351dd63..351dd63 100644
--- a/extras/waf_xattr.py
+++ b/waflib/extras/waf_xattr.py
diff --git a/extras/why.py b/waflib/extras/why.py
index 1bb941f..1bb941f 100644
--- a/extras/why.py
+++ b/waflib/extras/why.py
diff --git a/extras/win32_opts.py b/waflib/extras/win32_opts.py
index 9f7443c..9f7443c 100644
--- a/extras/win32_opts.py
+++ b/waflib/extras/win32_opts.py
diff --git a/extras/wix.py b/waflib/extras/wix.py
index d87bfbb..d87bfbb 100644
--- a/extras/wix.py
+++ b/waflib/extras/wix.py
diff --git a/extras/xcode6.py b/waflib/extras/xcode6.py
index 91bbff1..91bbff1 100644
--- a/extras/xcode6.py
+++ b/waflib/extras/xcode6.py
diff --git a/fixpy2.py b/waflib/fixpy2.py
index 24176e0..24176e0 100644
--- a/fixpy2.py
+++ b/waflib/fixpy2.py
diff --git a/processor.py b/waflib/processor.py
index 2eecf3b..2eecf3b 100755
--- a/processor.py
+++ b/waflib/processor.py
diff --git a/waflib/waf b/waflib/waf
new file mode 100755
index 0000000..e22930a
--- /dev/null
+++ b/waflib/waf
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+# Minimal waf script for projects that include waflib directly
+
+from waflib import Context, Scripting
+
+import inspect
+import os
+
+def main():
+ script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
+ project_path = os.path.dirname(script_path)
+ Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+
+if __name__ == '__main__':
+ main()
diff --git a/wscript b/wscript
new file mode 100644
index 0000000..2b69029
--- /dev/null
+++ b/wscript
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+
+import os
+import shutil
+import subprocess
+
+from waflib import Options
+from waflib.extras import autowaf
+
+BLOP_VERSION = '1.0.1'
+
+# Mandatory waf variables
+APPNAME = 'blop-lv2' # Package name for waf dist
+VERSION = BLOP_VERSION # Package version for waf dist
+top = '.' # Source directory
+out = 'build' # Build directory
+
+def options(ctx):
+ ctx.load('compiler_c')
+ ctx.load('lv2')
+ opt = ctx.configuration_options()
+ opt.add_option('--rate', type='int', default=48000,
+ dest='rate',
+ help='ideal sample rate for oscillator wave tables [Default: 48000]')
+
+def configure(conf):
+ conf.load('compiler_c', cache=True)
+ conf.load('lv2', cache=True)
+ conf.load('autowaf', cache=True)
+ autowaf.set_c_lang(conf, 'c99')
+
+ autowaf.check_pkg(conf, 'lv2', atleast_version='1.2.0', uselib_store='LV2')
+
+ autowaf.check_function(conf, 'c', 'sinf',
+ header_name = 'math.h',
+ lib = 'm',
+ define_name = 'HAVE_SINF',
+ mandatory = False)
+
+ conf.check_cc(define_name = 'HAVE_LIBDL',
+ lib = 'dl',
+ mandatory = False)
+
+ autowaf.check_function(conf, 'c', 'getopt_long',
+ header_name = 'getopt.h')
+
+
+ conf.write_config_header('blop_config.h', remove=False)
+
+ # Set env.pluginlib_PATTERN
+ pat = conf.env.cshlib_PATTERN
+ if pat[0:3] == 'lib':
+ pat = pat[3:]
+ conf.env.pluginlib_PATTERN = pat
+ conf.env.pluginlib_EXT = pat[pat.rfind('.'):]
+
+ conf.define('BLOP_SHLIB_EXT', conf.env.pluginlib_EXT)
+ conf.run_env.append_unique('LV2_PATH', [conf.build_path('lv2')])
+
+ autowaf.display_summary(conf,
+ {'LV2 bundle directory': conf.env.LV2DIR,
+ 'Ideal sampling rate': Options.options.rate})
+
+def build_plugin(bld, lang, bundle, name, source, defines=None, lib=[]):
+ # Build plugin library
+ penv = bld.env.derive()
+ penv.cshlib_PATTERN = bld.env.pluginlib_PATTERN
+ obj = bld(features = '%s %sshlib' % (lang,lang),
+ env = penv,
+ source = source,
+ includes = ['.', 'src/include'],
+ name = name,
+ target = os.path.join('lv2', bundle, name),
+ uselib = ['LV2'],
+ lib = ['m'] + lib,
+ install_path = '${LV2DIR}/' + bundle)
+ if defines != None:
+ obj.defines = defines
+
+ # Install data file
+ data_file = '%s.ttl' % name
+ bld.install_files('${LV2DIR}/' + bundle, os.path.join(bundle, data_file))
+
+def build(bld):
+ for i in bld.path.ant_glob('blop.lv2/*.ttl'):
+ bld(features = 'subst',
+ is_copy = True,
+ source = i,
+ target = 'lv2/blop.lv2/%s' % i.name,
+ install_path = '${LV2DIR}/blop.lv2')
+
+ bld(features = 'subst',
+ source = 'blop.lv2/manifest.ttl.in',
+ target = 'lv2/blop.lv2/manifest.ttl',
+ LIB_EXT = bld.env.pluginlib_EXT,
+ install_path = '${LV2DIR}/blop.lv2')
+
+ plugins = '''
+ adsr
+ adsr_gt
+ amp
+ branch
+ dahdsr
+ difference
+ fmod
+ interpolator
+ product
+ random
+ ratio
+ sum
+ sync_pulse
+ sync_square
+ tracker
+ '''.split()
+
+ # Simple (single source file) plugins
+ for i in plugins:
+ build_plugin(bld, 'c', 'blop.lv2', i,
+ ['src/%s.c' % i])
+
+ # Low pass filter
+ build_plugin(bld, 'c', 'blop.lv2', 'lp4pole',
+ ['src/lp4pole.c', 'src/lp4pole_filter.c'])
+
+ # Oscillators
+ for i in ['pulse', 'sawtooth', 'square', 'triangle']:
+ lib = []
+ if bld.is_defined('HAVE_LIBDL'):
+ lib += ['dl']
+ build_plugin(bld, 'c', 'blop.lv2', i,
+ ['src/%s.c' % i, 'src/wavedata.c'],
+ lib=lib)
+
+ # Sequencers
+ for i in [16, 32, 64]:
+ uri = 'http://drobilla.net/plugins/blop/sequencer_%d' % i
+ build_plugin(bld, 'c', 'blop.lv2', 'sequencer_%d' % i,
+ ['src/sequencer.c'],
+ defines=['SEQUENCER_MAX_INPUTS=%d' % i,
+ 'SEQUENCER_URI="%s"' % uri])
+
+ # Quantisers
+ for i in [20, 50, 100]:
+ uri = 'http://drobilla.net/plugins/blop/quantiser_%d' % i
+ build_plugin(bld, 'c', 'blop.lv2', 'quantiser_%d' % i,
+ ['src/quantiser.c'],
+ defines=['QUANTISER_MAX_INPUTS=%d' % i,
+ 'QUANTISER_URI="%s"' % uri])
+
+ # Wavegen
+ wavegen = bld(features = 'c cprogram',
+ source = ['src/wavegen.c', 'src/wdatutil.c'],
+ target = 'src/wavegen',
+ name = 'wavegen',
+ includes = ['.', 'src/include'],
+ lib = ['m'],
+ install_path = None)
+
+ wavegen.post()
+
+ # Waveform data source
+ for i in ['parabola', 'sawtooth', 'square']:
+ bld(rule = '${SRC} -r %d -f 12 -s 1 -m 128 -g 1.0 -w %s -p %s -o ${TGT}' % (
+ Options.options.rate, i, i),
+ source = wavegen.link_task.outputs[0],
+ target = 'src/%s_data.c' % i,
+ name = i)
+
+ penv = bld.env.derive()
+ penv.cshlib_PATTERN = bld.env.pluginlib_PATTERN
+
+ bld(features = 'c cshlib',
+ source = bld.path.get_bld().make_node('src/%s_data.c' % i),
+ target = 'lv2/blop.lv2/%s_data' % i,
+ includes = ['.', 'src/include'],
+ env = penv,
+ install_path = '${LV2DIR}/blop.lv2',
+ uselib = ['LV2'])
+
+def lint(ctx):
+ subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* serd/*', shell=True)
+
+def posts(ctx):
+ path = str(ctx.path.abspath())
+ autowaf.news_to_posts(
+ os.path.join(path, 'NEWS'),
+ {'title' : 'BLOP.LV2',
+ 'description' : autowaf.get_blurb(os.path.join(path, 'README')),
+ 'dist_pattern' : 'http://download.drobilla.net/blop-lv2-%s.tar.bz2'},
+ { 'Author' : 'drobilla',
+ 'Tags' : 'LV2, Blop.lv2' },
+ os.path.join(out, 'posts'))