shithub: soundpipe

Download patch

ref: a901d5647d30cf20fc0a096a612c58b2f99b8551
author: Paul Batchelor <thisispaulbatchelor@gmail.com>
date: Sat Sep 5 12:30:21 EDT 2020

import

--- /dev/null
+++ b/.gitignore
@@ -1,0 +1,20 @@
+*.o
+*.a
+*.wav
+*.raw
+*.swp
+*.dSYM
+soundpipe.h
+soundpipe.c
+.DS_Store
+docs
+*.html
+examples/plot.dat
+examples/*.bin
+config.mk
+test/*.bin
+lib/fft/fft.c
+
+# Generated files
+util/wav2smp
+sp_dict.lua
--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2020 Paul Batchelor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+++ b/Makefile
@@ -1,0 +1,93 @@
+.PHONY: all clean install docs bootstrap util
+
+default: all
+
+VERSION = 2.0.0
+
+PREFIX ?= /usr/local
+
+LIBSOUNDPIPE = libsoundpipe.a
+
+ifndef CONFIG
+CONFIG = config.mk
+endif
+
+HPATHS += $(addprefix h/, $(addsuffix .h, $(MODULES)))
+MPATHS += $(addprefix modules/, $(addsuffix .o, $(MODULES)))
+
+include $(CONFIG)
+
+ifeq ($(USE_DOUBLE), 1)
+CFLAGS+=-DUSE_DOUBLE
+SPFLOAT=double
+else
+SPFLOAT=float
+endif
+
+CFLAGS += -DSP_VERSION=$(VERSION) -O3 -DSPFLOAT=${SPFLOAT}
+CFLAGS += -Ih -I/usr/local/include -fPIC
+CFLAGS += -I.
+CFLAGS += -Wall -pedantic
+UTIL += util/wav2smp
+
+C89=$(CC) -std=c89
+C99=$(CC) -std=c99
+
+libsoundpipe.a: $(MPATHS) $(LPATHS) $(TANGLED)
+	@echo "Creating $@"
+	@$(AR) rcs $@ $(MPATHS) $(LPATHS) $(TANGLED)
+
+h/soundpipe.h: $(HPATHS)
+	echo "#ifndef SOUNDPIPE_H" > $@
+ifdef USE_DOUBLE
+	echo "#define USE_DOUBLE" >> $@
+endif
+	echo "#define SOUNDPIPE_H" >> $@
+	cat $(HPATHS) >> $@
+	echo "#endif" >> $@
+
+h/sp_base.h: h/base.h
+	>$@
+	echo "#ifndef SOUNDPIPE_H" >> $@
+ifdef USE_DOUBLE
+	echo "#define USE_DOUBLE" >> $@
+endif
+	echo "#define SOUNDPIPE_H" >> $@
+	cat $< >> $@
+	echo "#endif" >> $@
+
+modules/%.o: modules/%.c h/soundpipe.h
+	$(C89) $(CFLAGS) -c -static $< -o $@
+
+tangled/%.o: %.c h/soundpipe.h
+	$(C89) $(CFLAGS) -c -static $< -o $@
+
+config.mk: config.def.mk
+	cp $< $@
+
+sp_dict.lua:
+	cat modules/data/*.lua > $@
+
+docs:
+	util/gendocs.sh
+
+all: config.mk libsoundpipe.a sp_dict.lua h/sp_base.h
+
+install: \
+	h/soundpipe.h \
+	h/sp_base.h \
+	libsoundpipe.a
+	install h/soundpipe.h /usr/local/include/
+	install h/sp_base.h /usr/local/include/
+	install libsoundpipe.a /usr/local/lib/
+
+clean:
+	$(RM) h/soundpipe.h
+	$(RM) -r docs
+	$(RM) libsoundpipe.a
+	$(RM) soundpipe.c
+	$(RM) sp_dict.lua
+	$(RM) $(LPATHS)
+	$(RM) $(MPATHS)
+	$(RM) $(TANGLED)
+	$(RM) h/sp_base.h
--- /dev/null
+++ b/README.md
@@ -1,0 +1,63 @@
+Soundpipe
+=========
+
+Soundpipe is a lightweight music DSP library written in C. It aims to provide
+a set of high-quality DSP modules for composers, sound designers,
+and creative coders.
+
+
+Compilation
+-----------
+
+To compile:
+
+make
+
+sudo make install
+
+
+Tests
+-----
+
+Tests in Soundpipe are used to determine whether or not modules behave as 
+expected. Tests write the output of a module to memory, and check the MD5 hash 
+value of the output against the MD5 value of a reference signal.
+
+To build a test file, go into the test folder, and run "make". Then, run 
+"./run.bin", which runs the tests. As the tests are run, an "ok" will appear in 
+the log if a test passes, and a "not ok" will appear if a test fails. 
+
+It is possible to hear the output of a particular test if you know the test 
+number. You will need to have sox installed. For example, 
+to hear what test 11 sounds like, run the following
+commands:
+
+./run.bin render 11
+
+./write_wave.sh 0011.raw
+
+This will generate a file called out.wav.
+
+The testing utility has a few optional arguments. To see all possible arguments,
+run "./run.bin help".
+
+The Soundpipe Model
+-------------------
+
+Soundpipe is callback driven. Every time Soundpipe needs a frame, it will
+call upon a single function specified by the user. Soundpipe modules are
+designed to process a signal one sample at a time.  Every module follows the
+same life cycle:
+
+1. Create: Memory is allocated for the data struct.
+2. Initialize: Buffers are allocated, and initial variables and constants
+are set.
+3. Compute: the module takes in inputs (if applicable), and generates a
+single sample of output.
+4. Destroy: All memory allocated is freed.
+
+Documentation
+-------------
+If you have lua installed on your computer, you can generate the current html
+documentation for soundpipe by running "make docs". A folder called "docs"
+will be created. The top page for the documentation is docs/index.html.
\ No newline at end of file
--- /dev/null
+++ b/config.def.mk
@@ -1,0 +1,130 @@
+MODULES= \
+base \
+ftbl \
+adsr \
+autowah \
+biscale \
+blsaw \
+blsquare \
+bltriangle \
+butlp \
+butbp \
+buthp \
+butbr \
+brown \
+clamp \
+clock \
+compressor \
+count \
+crossfade \
+delay \
+diode \
+dmetro \
+dtrig \
+expon \
+in \
+incr \
+jcrev \
+line \
+loadwav \
+lpc \
+maygate \
+metro \
+noise \
+nsmp \
+osc \
+paulstretch \
+peaklim \
+phaser \
+phasor \
+pinknoise \
+prop \
+pshift \
+randmt \
+random \
+randh \
+reverse \
+rpt \
+saturator \
+samphold \
+scale \
+scrambler \
+sdelay \
+slice \
+smoothdelay \
+smoother \
+spa \
+sparec \
+switch \
+tadsr \
+talkbox \
+tblrec \
+tdiv \
+tenv \
+tenv2 \
+tenvx \
+tgate \
+thresh \
+timer \
+tin \
+trand \
+tseg \
+tseq \
+voc \
+wavin \
+wavout \
+wpkorg35 \
+zitarev \
+bitcrush \
+bigverb \
+dcblocker \
+fmpair \
+rline \
+vardelay \
+peakeq \
+modalres \
+phasewarp \
+
+TANGLED += \
+tangled/osc.o \
+tangled/bigverb.o \
+tangled/dcblocker.o \
+tangled/fmpair.o \
+tangled/rline.o \
+tangled/vardelay.o \
+tangled/peakeq.o \
+tangled/modalres.o \
+tangled/phasewarp.o \
+
+NO_LIBSNDFILE=1
+CFLAGS += -DNO_LIBSNDFILE
+
+# ini parser needed for nsmp module
+include lib/inih/Makefile
+
+# Header files needed for modules generated with FAUST
+CFLAGS += -Ilib/faust
+
+# fft library
+include lib/fft/Makefile
+
+include lib/kissfft/Makefile
+MODULES += fftwrapper
+MODULES += padsynth
+
+# Uncomment to use FFTW3 instead of kissfft.
+# CFLAGS += -DUSE_FFTW3
+
+# Soundpipe audio
+include lib/spa/Makefile
+
+# openlpc
+include lib/openlpc/Makefile
+
+# drwav
+include lib/dr_wav/Makefile
+
+CFLAGS += -fPIC -g
+
+# Uncomment this to use double precision
+#USE_DOUBLE=1
--- /dev/null
+++ b/h/adsr.h
@@ -1,0 +1,19 @@
+typedef struct {
+    SPFLOAT atk;
+    SPFLOAT dec;
+    SPFLOAT sus;
+    SPFLOAT rel;
+    uint32_t timer;
+    uint32_t atk_time;
+    SPFLOAT a;
+    SPFLOAT b;
+    SPFLOAT y;
+    SPFLOAT x;
+    SPFLOAT prev;
+    int mode;
+} sp_adsr;
+
+int sp_adsr_create(sp_adsr **p);
+int sp_adsr_destroy(sp_adsr **p);
+int sp_adsr_init(sp_data *sp, sp_adsr *p);
+int sp_adsr_compute(sp_data *sp, sp_adsr *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/autowah.h
@@ -1,0 +1,13 @@
+typedef struct {
+    void *faust;
+    int argpos;
+    SPFLOAT *args[3];
+    SPFLOAT *level;
+    SPFLOAT *wah;
+    SPFLOAT *mix;
+} sp_autowah;
+
+int sp_autowah_create(sp_autowah **p);
+int sp_autowah_destroy(sp_autowah **p);
+int sp_autowah_init(sp_data *sp, sp_autowah *p);
+int sp_autowah_compute(sp_data *sp, sp_autowah *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/base.h
@@ -1,0 +1,97 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#define SP_BUFSIZE 4096
+#ifndef SPFLOAT
+#define SPFLOAT float
+#endif
+#define SP_OK 1
+#define SP_NOT_OK 0
+
+#define SP_RANDMAX 2147483648
+
+typedef unsigned long sp_frame;
+
+typedef struct sp_auxdata {
+    size_t size;
+    void *ptr;
+} sp_auxdata;
+
+typedef struct sp_data {
+    SPFLOAT *out;
+    int sr;
+    int nchan;
+    unsigned long len;
+    unsigned long pos;
+    char filename[200];
+    uint32_t rand;
+} sp_data;
+
+typedef struct {
+    char state;
+    SPFLOAT val;
+} sp_param;
+
+int sp_auxdata_alloc(sp_auxdata *aux, size_t size);
+int sp_auxdata_free(sp_auxdata *aux);
+
+int sp_create(sp_data **spp);
+int sp_createn(sp_data **spp, int nchan);
+
+int sp_destroy(sp_data **spp);
+int sp_process(sp_data *sp, void *ud, void (*callback)(sp_data *, void *));
+int sp_process_raw(sp_data *sp, void *ud, void (*callback)(sp_data *, void *));
+int sp_process_plot(sp_data *sp, void *ud, void (*callback)(sp_data *, void *));
+int sp_process_spa(sp_data *sp, void *ud, void (*callback)(sp_data *, void *));
+
+SPFLOAT sp_midi2cps(SPFLOAT nn);
+
+int sp_set(sp_param *p, SPFLOAT val);
+
+int sp_out(sp_data *sp, uint32_t chan, SPFLOAT val);
+
+uint32_t sp_rand(sp_data *sp);
+void sp_srand(sp_data *sp, uint32_t val);
+
+
+typedef struct {
+    SPFLOAT *utbl;
+    int16_t *BRLow;
+    int16_t *BRLowCpx;
+} sp_fft;
+
+void sp_fft_create(sp_fft **fft);
+void sp_fft_init(sp_fft *fft, int M);
+void sp_fftr(sp_fft *fft, SPFLOAT *buf, int FFTsize);
+void sp_fft_cpx(sp_fft *fft, SPFLOAT *buf, int FFTsize);
+void sp_ifftr(sp_fft *fft, SPFLOAT *buf, int FFTsize);
+void sp_fft_destroy(sp_fft *fft);
+#ifndef kiss_fft_scalar
+#define kiss_fft_scalar SPFLOAT
+#endif
+typedef struct {
+    kiss_fft_scalar r;
+    kiss_fft_scalar i;
+}kiss_fft_cpx;
+
+typedef struct kiss_fft_state* kiss_fft_cfg;
+typedef struct kiss_fftr_state* kiss_fftr_cfg;
+
+/* SPA: Soundpipe Audio */
+
+enum { SPA_READ, SPA_WRITE, SPA_NULL };
+
+typedef struct {
+    char magic;
+    char nchan;
+    uint16_t sr;
+    uint32_t len;
+} spa_header;
+
+typedef struct {
+    spa_header header;
+    size_t offset;
+    int mode;
+    FILE *fp;
+    uint32_t pos;
+} sp_audio;
--- /dev/null
+++ b/h/bigverb.h
@@ -1,0 +1,18 @@
+#ifndef SK_BIGVERB_H
+typedef struct sk_bigverb sk_bigverb;
+#endif
+
+typedef struct {
+    SPFLOAT feedback, lpfreq;
+    sk_bigverb *bv;
+} sp_bigverb;
+
+int sp_bigverb_create(sp_bigverb **p);
+int sp_bigverb_destroy(sp_bigverb **p);
+int sp_bigverb_init(sp_data *sp, sp_bigverb *p);
+int sp_bigverb_compute(sp_data *sp,
+                       sp_bigverb *p,
+                       SPFLOAT *in1,
+                       SPFLOAT *in2,
+                       SPFLOAT *out1,
+                       SPFLOAT *out2);
--- /dev/null
+++ b/h/biscale.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT min, max;
+} sp_biscale;
+
+int sp_biscale_create(sp_biscale **p);
+int sp_biscale_destroy(sp_biscale **p);
+int sp_biscale_init(sp_data *sp, sp_biscale *p);
+int sp_biscale_compute(sp_data *sp, sp_biscale *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/bitcrush.h
@@ -1,0 +1,14 @@
+typedef struct {
+    SPFLOAT bitdepth;
+    SPFLOAT srate;
+
+    SPFLOAT incr;
+    SPFLOAT index;
+    int32_t sample_index;
+    SPFLOAT value;
+} sp_bitcrush;
+
+int sp_bitcrush_create(sp_bitcrush **p);
+int sp_bitcrush_destroy(sp_bitcrush **p);
+int sp_bitcrush_init(sp_data *sp, sp_bitcrush *p);
+int sp_bitcrush_compute(sp_data *sp, sp_bitcrush *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/blsaw.h
@@ -1,0 +1,12 @@
+typedef struct {
+    void *ud;
+    int argpos;
+    SPFLOAT *args[2];
+    SPFLOAT *freq;
+    SPFLOAT *amp;
+} sp_blsaw;
+
+int sp_blsaw_create(sp_blsaw **p);
+int sp_blsaw_destroy(sp_blsaw **p);
+int sp_blsaw_init(sp_data *sp, sp_blsaw *p);
+int sp_blsaw_compute(sp_data *sp, sp_blsaw *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/blsquare.h
@@ -1,0 +1,13 @@
+typedef struct {
+    void *ud;
+    int argpos;
+    SPFLOAT *args[3];
+    SPFLOAT *freq;
+    SPFLOAT *amp;
+    SPFLOAT *width;
+} sp_blsquare;
+
+int sp_blsquare_create(sp_blsquare **p);
+int sp_blsquare_destroy(sp_blsquare **p);
+int sp_blsquare_init(sp_data *sp, sp_blsquare *p);
+int sp_blsquare_compute(sp_data *sp, sp_blsquare *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/bltriangle.h
@@ -1,0 +1,12 @@
+typedef struct {
+    void *ud;
+    int argpos;
+    SPFLOAT *args[2];
+    SPFLOAT *freq;
+    SPFLOAT *amp;
+} sp_bltriangle;
+
+int sp_bltriangle_create(sp_bltriangle **p);
+int sp_bltriangle_destroy(sp_bltriangle **p);
+int sp_bltriangle_init(sp_data *sp, sp_bltriangle *p);
+int sp_bltriangle_compute(sp_data *sp, sp_bltriangle *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/brown.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT brown;
+} sp_brown;
+
+int sp_brown_create(sp_brown **p);
+int sp_brown_destroy(sp_brown **p);
+int sp_brown_init(sp_data *sp, sp_brown *p);
+int sp_brown_compute(sp_data *sp, sp_brown *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/butbp.h
@@ -1,0 +1,11 @@
+typedef struct {
+    SPFLOAT freq, bw;
+    SPFLOAT lfreq, lbw;
+    SPFLOAT a[7];
+    SPFLOAT pidsr, tpidsr;
+} sp_butbp;
+
+int sp_butbp_create(sp_butbp **p);
+int sp_butbp_destroy(sp_butbp **p);
+int sp_butbp_init(sp_data *sp, sp_butbp *p);
+int sp_butbp_compute(sp_data *sp, sp_butbp *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/butbr.h
@@ -1,0 +1,11 @@
+typedef struct {
+    SPFLOAT freq, bw;
+    SPFLOAT lfreq, lbw;
+    SPFLOAT a[7];
+    SPFLOAT pidsr, tpidsr;
+} sp_butbr;
+
+int sp_butbr_create(sp_butbr **p);
+int sp_butbr_destroy(sp_butbr **p);
+int sp_butbr_init(sp_data *sp, sp_butbr *p);
+int sp_butbr_compute(sp_data *sp, sp_butbr *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/buthp.h
@@ -1,0 +1,11 @@
+typedef struct  {
+    SPFLOAT freq;
+    SPFLOAT lfreq;
+    SPFLOAT a[7];
+    SPFLOAT pidsr;
+} sp_buthp;
+
+int sp_buthp_create(sp_buthp **p);
+int sp_buthp_destroy(sp_buthp **p);
+int sp_buthp_init(sp_data *sp, sp_buthp *p);
+int sp_buthp_compute(sp_data *sp, sp_buthp *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/butlp.h
@@ -1,0 +1,11 @@
+typedef struct  {
+    SPFLOAT sr, freq;
+    SPFLOAT lfreq;
+    SPFLOAT a[7];
+    SPFLOAT pidsr;
+} sp_butlp;
+
+int sp_butlp_create(sp_butlp **p);
+int sp_butlp_destroy(sp_butlp **p);
+int sp_butlp_init(sp_data *sp, sp_butlp *p);
+int sp_butlp_compute(sp_data *sp, sp_butlp *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/clamp.h
@@ -1,0 +1,9 @@
+typedef struct {
+    SPFLOAT min;
+    SPFLOAT max;
+} sp_clamp;
+
+int sp_clamp_create(sp_clamp **p);
+int sp_clamp_destroy(sp_clamp **p);
+int sp_clamp_init(sp_data *sp, sp_clamp *p);
+int sp_clamp_compute(sp_data *sp, sp_clamp *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/clock.h
@@ -1,0 +1,10 @@
+typedef struct {
+    SPFLOAT bpm;
+    SPFLOAT subdiv;
+    uint32_t counter;
+} sp_clock;
+
+int sp_clock_create(sp_clock **p);
+int sp_clock_destroy(sp_clock **p);
+int sp_clock_init(sp_data *sp, sp_clock *p);
+int sp_clock_compute(sp_data *sp, sp_clock *p, SPFLOAT *trig, SPFLOAT *out);
--- /dev/null
+++ b/h/compressor.h
@@ -1,0 +1,14 @@
+typedef struct {
+    void *faust;
+    int argpos;
+    SPFLOAT *args[4];
+    SPFLOAT *ratio;
+    SPFLOAT *thresh;
+    SPFLOAT *atk;
+    SPFLOAT *rel;
+} sp_compressor;
+
+int sp_compressor_create(sp_compressor **p);
+int sp_compressor_destroy(sp_compressor **p);
+int sp_compressor_init(sp_data *sp, sp_compressor *p);
+int sp_compressor_compute(sp_data *sp, sp_compressor *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/count.h
@@ -1,0 +1,9 @@
+typedef struct sp_count{
+    int32_t count, curcount;
+    int mode;
+} sp_count;
+
+int sp_count_create(sp_count **p);
+int sp_count_destroy(sp_count **p);
+int sp_count_init(sp_data *sp, sp_count *p);
+int sp_count_compute(sp_data *sp, sp_count *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/crossfade.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT pos;
+} sp_crossfade;
+
+int sp_crossfade_create(sp_crossfade **p);
+int sp_crossfade_destroy(sp_crossfade **p);
+int sp_crossfade_init(sp_data *sp, sp_crossfade *p);
+int sp_crossfade_compute(sp_data *sp, sp_crossfade *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out);
--- /dev/null
+++ b/h/dcblocker.h
@@ -1,0 +1,13 @@
+#ifndef SK_DCBLOCKER_H
+typedef struct sk_dcblocker sk_dcblocker;
+#endif
+
+typedef struct {
+    sk_dcblocker *dcblocker;
+} sp_dcblocker;
+
+int sp_dcblocker_create(sp_dcblocker **p);
+int sp_dcblocker_destroy(sp_dcblocker **p);
+int sp_dcblocker_init(sp_data *sp, sp_dcblocker *p);
+int sp_dcblocker_compute(sp_data *sp, sp_dcblocker *p,
+                         SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/delay.h
@@ -1,0 +1,13 @@
+typedef struct {
+    SPFLOAT time;
+    SPFLOAT feedback;
+    SPFLOAT last;
+    sp_auxdata buf;
+    uint32_t bufsize;
+    uint32_t bufpos;
+} sp_delay;
+
+int sp_delay_create(sp_delay **p);
+int sp_delay_destroy(sp_delay **p);
+int sp_delay_init(sp_data *sp, sp_delay *p, SPFLOAT time);
+int sp_delay_compute(sp_data *sp, sp_delay *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/diode.h
@@ -1,0 +1,23 @@
+typedef struct {
+    /* 4 one-pole filters */
+    SPFLOAT opva_alpha[4];
+    SPFLOAT opva_beta[4];
+    SPFLOAT opva_gamma[4];
+    SPFLOAT opva_delta[4];
+    SPFLOAT opva_eps[4];
+    SPFLOAT opva_a0[4];
+    SPFLOAT opva_fdbk[4];
+    SPFLOAT opva_z1[4];
+    /* end one-pole filters */
+
+    SPFLOAT SG[4];
+    SPFLOAT gamma;
+    SPFLOAT freq;
+    SPFLOAT K;
+    SPFLOAT res;
+} sp_diode;
+
+int sp_diode_create(sp_diode **p);
+int sp_diode_destroy(sp_diode **p);
+int sp_diode_init(sp_data *sp, sp_diode *p);
+int sp_diode_compute(sp_data *sp, sp_diode *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/dmetro.h
@@ -1,0 +1,9 @@
+typedef struct {
+    SPFLOAT time;
+    uint32_t counter;
+} sp_dmetro;
+
+int sp_dmetro_create(sp_dmetro **p);
+int sp_dmetro_destroy(sp_dmetro **p);
+int sp_dmetro_init(sp_data *sp, sp_dmetro *p);
+int sp_dmetro_compute(sp_data *sp, sp_dmetro *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/dtrig.h
@@ -1,0 +1,14 @@
+typedef struct sp_dtrig{
+    sp_ftbl *ft;
+    uint32_t counter;
+    uint32_t pos;
+    int running;
+    int loop;
+    SPFLOAT delay;
+    SPFLOAT scale;
+} sp_dtrig;
+
+int sp_dtrig_create(sp_dtrig **p);
+int sp_dtrig_destroy(sp_dtrig **p);
+int sp_dtrig_init(sp_data *sp, sp_dtrig *p, sp_ftbl *ft);
+int sp_dtrig_compute(sp_data *sp, sp_dtrig *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/expon.h
@@ -1,0 +1,11 @@
+typedef struct {
+    SPFLOAT a, dur, b;
+    SPFLOAT val, incr;
+    uint32_t sdur, stime;
+    int init;
+} sp_expon;
+
+int sp_expon_create(sp_expon **p);
+int sp_expon_destroy(sp_expon **p);
+int sp_expon_init(sp_data *sp, sp_expon *p);
+int sp_expon_compute(sp_data *sp, sp_expon *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/fftwrapper.h
@@ -1,0 +1,31 @@
+#ifdef USE_FFTW3
+#include <fftw3.h>
+#endif
+
+#define fftw_real double
+#define rfftw_plan fftw_plan
+
+typedef struct FFTFREQS {
+    int size;
+    SPFLOAT *s,*c;
+} FFTFREQS;
+
+typedef struct {
+    int fftsize;
+#ifdef USE_FFTW3
+    fftw_real *tmpfftdata1, *tmpfftdata2;
+    rfftw_plan planfftw,planfftw_inv;
+#else
+    kiss_fftr_cfg fft, ifft;
+    kiss_fft_cpx *tmp1, *tmp2;
+#endif
+} FFTwrapper;
+
+void FFTwrapper_create(FFTwrapper **fw, int fftsize);
+void FFTwrapper_destroy(FFTwrapper **fw);
+
+void newFFTFREQS(FFTFREQS *f, int size);
+void deleteFFTFREQS(FFTFREQS *f);
+
+void smps2freqs(FFTwrapper *ft, SPFLOAT *smps, FFTFREQS *freqs);
+void freqs2smps(FFTwrapper *ft, FFTFREQS *freqs, SPFLOAT *smps);
--- /dev/null
+++ b/h/fmpair.h
@@ -1,0 +1,14 @@
+#ifndef SK_FMPAIR_H
+typedef struct sk_fmpair sk_fmpair;
+#endif
+
+typedef struct {
+    SPFLOAT amp, freq, car, mod, indx;
+    sk_fmpair *fmpair;
+} sp_fmpair;
+
+int sp_fmpair_create(sp_fmpair **p);
+int sp_fmpair_destroy(sp_fmpair **p);
+int sp_fmpair_init(sp_data *sp, sp_fmpair *p, sp_ftbl *ft);
+int sp_fmpair_compute(sp_data *sp, sp_fmpair *p,
+                      SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/foo.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT bar;
+} sp_foo;
+
+int sp_foo_create(sp_foo **p);
+int sp_foo_destroy(sp_foo **p);
+int sp_foo_init(sp_data *sp, sp_foo *p);
+int sp_foo_compute(sp_data *sp, sp_foo *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/ftbl.h
@@ -1,0 +1,21 @@
+#define SP_FT_MAXLEN 0x1000000L
+#define SP_FT_PHMASK 0x0FFFFFFL
+
+typedef struct sp_ftbl{
+    size_t size;
+    SPFLOAT *tbl;
+    /* uint32_t lobits; */
+    /* uint32_t lomask; */
+    /* SPFLOAT lodiv; */
+    /* SPFLOAT sicvt; */
+    /* char del; */
+} sp_ftbl;
+
+int sp_ftbl_create(sp_data *sp, sp_ftbl **ft, size_t size);
+int sp_ftbl_init(sp_data *sp, sp_ftbl *ft, size_t size);
+int sp_ftbl_bind(sp_data *sp, sp_ftbl **ft, SPFLOAT *tbl, size_t size);
+int sp_ftbl_destroy(sp_ftbl **ft);
+int sp_ftbl_loadfile(sp_data *sp, sp_ftbl **ft, const char *filename);
+int sp_ftbl_loadspa(sp_data *sp, sp_ftbl **ft, const char *filename);
+int sp_gen_vals(sp_data *sp, sp_ftbl *ft, const char *string);
+int sp_gen_sine(sp_data *sp, sp_ftbl *ft);
--- /dev/null
+++ b/h/in.h
@@ -1,0 +1,8 @@
+typedef struct {
+    FILE *fp;
+} sp_in;
+
+int sp_in_create(sp_in **p);
+int sp_in_destroy(sp_in **p);
+int sp_in_init(sp_data *sp, sp_in *p);
+int sp_in_compute(sp_data *sp, sp_in *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/incr.h
@@ -1,0 +1,11 @@
+typedef struct {
+    SPFLOAT step;
+    SPFLOAT min;
+    SPFLOAT max;
+    SPFLOAT val;
+} sp_incr;
+
+int sp_incr_create(sp_incr **p);
+int sp_incr_destroy(sp_incr **p);
+int sp_incr_init(sp_data *sp, sp_incr *p, SPFLOAT val);
+int sp_incr_compute(sp_data *sp, sp_incr *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/jack.h
@@ -1,0 +1,1 @@
+int sp_jack_process(sp_data *sp, void *ud, void (*callback)(sp_data *, void *));
--- /dev/null
+++ b/h/jcrev.h
@@ -1,0 +1,8 @@
+typedef struct {
+    void *ud;
+} sp_jcrev;
+
+int sp_jcrev_create(sp_jcrev **p);
+int sp_jcrev_destroy(sp_jcrev **p);
+int sp_jcrev_init(sp_data *sp, sp_jcrev *p);
+int sp_jcrev_compute(sp_data *sp, sp_jcrev *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/line.h
@@ -1,0 +1,11 @@
+typedef struct {
+    SPFLOAT a, dur, b;
+    SPFLOAT val, incr;
+    uint32_t sdur, stime;
+    int init;
+} sp_line;
+
+int sp_line_create(sp_line **p);
+int sp_line_destroy(sp_line **p);
+int sp_line_init(sp_data *sp, sp_line *p);
+int sp_line_compute(sp_data *sp, sp_line *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/loadwav.h
@@ -1,0 +1,1 @@
+int sp_ftbl_loadwav(sp_data *sp, sp_ftbl **ft, const char *filename);
--- /dev/null
+++ b/h/lpc.h
@@ -1,0 +1,26 @@
+typedef struct {
+    struct openlpc_e_state *e;
+    struct openlpc_d_state *d;
+    int counter;
+    short *in;
+    short *out;
+    unsigned char data[7];
+    SPFLOAT y[7];
+    SPFLOAT smooth;
+    SPFLOAT samp;
+    unsigned int clock;
+    unsigned int block;
+    int framesize;
+    sp_auxdata m_in;
+    sp_auxdata m_out;
+    sp_auxdata m_e;
+    sp_auxdata m_d;
+    int mode;
+    sp_ftbl *ft;
+} sp_lpc;
+
+int sp_lpc_create(sp_lpc **lpc);
+int sp_lpc_destroy(sp_lpc **lpc);
+int sp_lpc_init(sp_data *sp, sp_lpc *lpc, int framesize);
+int sp_lpc_synth(sp_data *sp, sp_lpc *lpc, sp_ftbl *ft);
+int sp_lpc_compute(sp_data *sp, sp_lpc *lpc, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/maygate.h
@@ -1,0 +1,10 @@
+typedef struct sp_maygate{
+    SPFLOAT prob;
+    SPFLOAT gate;
+    int mode;
+} sp_maygate;
+
+int sp_maygate_create(sp_maygate **p);
+int sp_maygate_destroy(sp_maygate **p);
+int sp_maygate_init(sp_data *sp, sp_maygate *p);
+int sp_maygate_compute(sp_data *sp, sp_maygate *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/metro.h
@@ -1,0 +1,11 @@
+typedef struct sp_metro{
+    SPFLOAT freq;
+    SPFLOAT phs;
+    int init;
+    SPFLOAT onedsr;
+} sp_metro;
+
+int sp_metro_create(sp_metro **p);
+int sp_metro_destroy(sp_metro **p);
+int sp_metro_init(sp_data *sp, sp_metro *p);
+int sp_metro_compute(sp_data *sp, sp_metro *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/modalres.h
@@ -1,0 +1,14 @@
+#ifndef SK_MODALRES_H
+typedef struct sk_modalres sk_modalres;
+#endif
+
+typedef struct {
+    SPFLOAT freq, q;
+    sk_modalres *modalres;
+} sp_modalres;
+
+int sp_modalres_create(sp_modalres **p);
+int sp_modalres_destroy(sp_modalres **p);
+int sp_modalres_init(sp_data *sp, sp_modalres *p);
+int sp_modalres_compute(sp_data *sp, sp_modalres *p,
+                      SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/noise.h
@@ -1,0 +1,8 @@
+typedef struct{
+    SPFLOAT amp;
+}sp_noise;
+
+int sp_noise_create(sp_noise **ns);
+int sp_noise_init(sp_data *sp, sp_noise *ns);
+int sp_noise_compute(sp_data *sp, sp_noise *ns, SPFLOAT *in, SPFLOAT *out);
+int sp_noise_destroy(sp_noise **ns);
--- /dev/null
+++ b/h/nsmp.h
@@ -1,0 +1,38 @@
+typedef struct nano_entry {
+    char name[50];
+    uint32_t pos;
+    uint32_t size;
+    SPFLOAT speed;
+    struct nano_entry *next;
+} nano_entry;
+
+typedef struct {
+    int nval;
+    int init;
+    nano_entry root;
+    nano_entry *last;
+} nano_dict;
+
+typedef struct {
+    char ini[100];
+    SPFLOAT curpos;
+    nano_dict dict;
+    int selected;
+    nano_entry *sample;
+    nano_entry **index;
+    sp_ftbl *ft;
+    int sr;
+} nanosamp;
+
+typedef struct {
+    nanosamp *smp;
+    uint32_t index;
+    int triggered;
+} sp_nsmp;
+
+int sp_nsmp_create(sp_nsmp **p);
+int sp_nsmp_destroy(sp_nsmp **p);
+int sp_nsmp_init(sp_data *sp, sp_nsmp *p, sp_ftbl *ft, int sr, const char *ini);
+int sp_nsmp_compute(sp_data *sp, sp_nsmp *p, SPFLOAT *in, SPFLOAT *out);
+
+int sp_nsmp_print_index(sp_data *sp, sp_nsmp *p);
--- /dev/null
+++ b/h/osc.h
@@ -1,0 +1,13 @@
+#ifndef SK_OSC_H
+typedef struct sk_osc sk_osc;
+#endif
+
+typedef struct {
+    SPFLOAT freq, amp, iphs;
+    sk_osc *osc;
+} sp_osc;
+
+int sp_osc_create(sp_osc **osc);
+int sp_osc_destroy(sp_osc **osc);
+int sp_osc_init(sp_data *sp, sp_osc *osc, sp_ftbl *ft, SPFLOAT iphs);
+int sp_osc_compute(sp_data *sp, sp_osc *osc, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/oscmorph.h
@@ -1,0 +1,13 @@
+typedef struct {
+    SPFLOAT freq, amp, iphs;
+    int32_t lphs;
+    sp_ftbl **tbl;
+    int inc;
+    SPFLOAT wtpos;
+    int nft;
+} sp_oscmorph;
+
+int sp_oscmorph_create(sp_oscmorph **p);
+int sp_oscmorph_destroy(sp_oscmorph **p);
+int sp_oscmorph_init(sp_data *sp, sp_oscmorph *osc, sp_ftbl **ft, int nft, SPFLOAT iphs);
+int sp_oscmorph_compute(sp_data *sp, sp_oscmorph *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/padsynth.h
@@ -1,0 +1,14 @@
+typedef struct sp_padsynth {
+    SPFLOAT cps;
+    SPFLOAT bw;
+    sp_ftbl *amps;
+} sp_padsynth;
+
+int sp_gen_padsynth(sp_data *sp, sp_ftbl *ps, sp_ftbl *amps, SPFLOAT f, SPFLOAT bw);
+
+SPFLOAT sp_padsynth_profile(SPFLOAT fi, SPFLOAT bwi);
+
+int sp_padsynth_ifft(int N, SPFLOAT *freq_amp,
+        SPFLOAT *freq_phase, SPFLOAT *smp);
+
+int sp_padsynth_normalize(int N, SPFLOAT *smp);
--- /dev/null
+++ b/h/paulstretch.h
@@ -1,0 +1,27 @@
+typedef struct {
+    uint32_t windowsize;
+    uint32_t half_windowsize;
+    SPFLOAT stretch;
+    SPFLOAT start_pos;
+    SPFLOAT displace_pos;
+    SPFLOAT *window;
+    SPFLOAT *old_windowed_buf;
+    SPFLOAT *hinv_buf;
+    SPFLOAT *buf;
+    SPFLOAT *output;
+    sp_ftbl *ft;
+    kiss_fftr_cfg fft, ifft;
+    kiss_fft_cpx *tmp1, *tmp2;
+    uint32_t counter;
+    sp_auxdata m_window;
+    sp_auxdata m_old_windowed_buf;
+    sp_auxdata m_hinv_buf;
+    sp_auxdata m_buf;
+    sp_auxdata m_output;
+    unsigned char wrap;
+} sp_paulstretch;
+
+int sp_paulstretch_create(sp_paulstretch **p);
+int sp_paulstretch_destroy(sp_paulstretch **p);
+int sp_paulstretch_init(sp_data *sp, sp_paulstretch *p, sp_ftbl *ft, SPFLOAT windowsize, SPFLOAT stretch);
+int sp_paulstretch_compute(sp_data *sp, sp_paulstretch *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/peakeq.h
@@ -1,0 +1,14 @@
+#ifndef SK_PEAKEQ_H
+typedef struct sk_peakeq sk_peakeq;
+#endif
+
+typedef struct {
+    SPFLOAT freq, bw, gain;
+    sk_peakeq *peakeq;
+} sp_peakeq;
+
+int sp_peakeq_create(sp_peakeq **p);
+int sp_peakeq_destroy(sp_peakeq **p);
+int sp_peakeq_init(sp_data *sp, sp_peakeq *p);
+int sp_peakeq_compute(sp_data *sp, sp_peakeq *p,
+                      SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/peaklim.h
@@ -1,0 +1,10 @@
+typedef struct {
+    SPFLOAT atk, rel, thresh;
+    SPFLOAT patk, prel;
+	SPFLOAT b0_r, a1_r, b0_a, a1_a, level;
+} sp_peaklim;
+
+int sp_peaklim_create(sp_peaklim **p);
+int sp_peaklim_destroy(sp_peaklim **p);
+int sp_peaklim_init(sp_data *sp, sp_peaklim *p);
+int sp_peaklim_compute(sp_data *sp, sp_peaklim *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/phaser.h
@@ -1,0 +1,21 @@
+typedef struct {
+    void *faust;
+    int argpos;
+    SPFLOAT *args[10];
+    SPFLOAT *MaxNotch1Freq;
+    SPFLOAT *MinNotch1Freq;
+    SPFLOAT *Notch_width;
+    SPFLOAT *NotchFreq;
+    SPFLOAT *VibratoMode;
+    SPFLOAT *depth;
+    SPFLOAT *feedback_gain;
+    SPFLOAT *invert;
+    SPFLOAT *level;
+    SPFLOAT *lfobpm;
+} sp_phaser;
+
+int sp_phaser_create(sp_phaser **p);
+int sp_phaser_destroy(sp_phaser **p);
+int sp_phaser_init(sp_data *sp, sp_phaser *p);
+int sp_phaser_compute(sp_data *sp, sp_phaser *p,
+	SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2);
--- /dev/null
+++ b/h/phasewarp.h
@@ -1,0 +1,13 @@
+#ifndef SK_PHASEWARP_H
+typedef struct sk_phasewarp sk_phasewarp;
+#endif
+
+typedef struct {
+    SPFLOAT amount;
+} sp_phasewarp;
+
+int sp_phasewarp_create(sp_phasewarp **p);
+int sp_phasewarp_destroy(sp_phasewarp **p);
+int sp_phasewarp_init(sp_data *sp, sp_phasewarp *p);
+int sp_phasewarp_compute(sp_data *sp, sp_phasewarp *p,
+                      SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/phasor.h
@@ -1,0 +1,9 @@
+typedef struct sp_phasor{
+    SPFLOAT freq, phs;
+    SPFLOAT onedsr;
+} sp_phasor;
+
+int sp_phasor_create(sp_phasor **p);
+int sp_phasor_destroy(sp_phasor **p);
+int sp_phasor_init(sp_data *sp, sp_phasor *p, SPFLOAT iphs);
+int sp_phasor_compute(sp_data *sp, sp_phasor *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/pinknoise.h
@@ -1,0 +1,15 @@
+typedef struct {
+    SPFLOAT amp;
+    unsigned int newrand;
+    unsigned int prevrand;
+    unsigned int k;
+    unsigned int seed;
+    unsigned int total;
+    uint32_t counter;
+    unsigned int dice[7];
+} sp_pinknoise;
+
+int sp_pinknoise_create(sp_pinknoise **p);
+int sp_pinknoise_destroy(sp_pinknoise **p);
+int sp_pinknoise_init(sp_data *sp, sp_pinknoise *p);
+int sp_pinknoise_compute(sp_data *sp, sp_pinknoise *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/prop.h
@@ -1,0 +1,59 @@
+typedef struct {
+    char type;
+    uint32_t pos;
+    uint32_t val;
+    uint32_t cons;
+} prop_event;
+
+typedef struct {
+    char type;
+    void *ud;
+} prop_val;
+
+typedef struct prop_entry {
+    prop_val val;
+    struct prop_entry *next;
+} prop_entry;
+
+typedef struct prop_list {
+    prop_entry root;
+    prop_entry *last;
+    uint32_t size;
+    uint32_t pos;
+    struct prop_list *top;
+    uint32_t lvl;
+} prop_list;
+
+typedef struct {
+    uint32_t stack[16];
+    int pos;
+} prop_stack;
+
+typedef struct {
+    uint32_t mul;
+    uint32_t div;
+    uint32_t tmp;
+    uint32_t cons_mul;
+    uint32_t cons_div;
+    SPFLOAT scale;
+    int mode;
+    uint32_t pos;
+    prop_list top;
+    prop_list *main;
+    prop_stack mstack;
+    prop_stack cstack;
+} prop_data;
+
+typedef struct {
+   prop_data *prp;
+   prop_event evt;
+   uint32_t count;
+   SPFLOAT bpm;
+   SPFLOAT lbpm;
+} sp_prop;
+
+int sp_prop_create(sp_prop **p);
+int sp_prop_destroy(sp_prop **p);
+int sp_prop_reset(sp_data *sp, sp_prop *p);
+int sp_prop_init(sp_data *sp, sp_prop *p, const char *str);
+int sp_prop_compute(sp_data *sp, sp_prop *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/pshift.h
@@ -1,0 +1,13 @@
+typedef struct {
+    void *faust;
+    int argpos;
+    SPFLOAT *args[3];
+    SPFLOAT *shift;
+    SPFLOAT *window;
+    SPFLOAT *xfade;
+} sp_pshift;
+
+int sp_pshift_create(sp_pshift **p);
+int sp_pshift_destroy(sp_pshift **p);
+int sp_pshift_init(sp_data *sp, sp_pshift *p);
+int sp_pshift_compute(sp_data *sp, sp_pshift *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/randh.h
@@ -1,0 +1,11 @@
+typedef struct {
+    SPFLOAT freq;
+    SPFLOAT min, max;
+    SPFLOAT val;
+    uint32_t counter, dur;
+} sp_randh;
+
+int sp_randh_create(sp_randh **p);
+int sp_randh_destroy(sp_randh **p);
+int sp_randh_init(sp_data *sp, sp_randh *p);
+int sp_randh_compute(sp_data *sp, sp_randh *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/randmt.h
@@ -1,0 +1,10 @@
+typedef struct {
+    int mti;
+    /* do not change value 624 */
+    uint32_t mt[624];
+} sp_randmt;
+
+void sp_randmt_seed(sp_randmt *p,
+    const uint32_t *initKey, uint32_t keyLength);
+
+uint32_t sp_randmt_compute(sp_randmt *p);
--- /dev/null
+++ b/h/random.h
@@ -1,0 +1,9 @@
+typedef struct {
+    SPFLOAT min;
+    SPFLOAT max;
+} sp_random;
+
+int sp_random_create(sp_random **p);
+int sp_random_destroy(sp_random **p);
+int sp_random_init(sp_data *sp, sp_random *p);
+int sp_random_compute(sp_data *sp, sp_random *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/reverse.h
@@ -1,0 +1,11 @@
+typedef struct  {
+    SPFLOAT delay;
+    uint32_t bufpos;
+    uint32_t bufsize;
+    sp_auxdata buf;
+} sp_reverse;
+
+int sp_reverse_create(sp_reverse **p);
+int sp_reverse_destroy(sp_reverse **p);
+int sp_reverse_init(sp_data *sp, sp_reverse *p, SPFLOAT delay);
+int sp_reverse_compute(sp_data *sp, sp_reverse *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/rline.h
@@ -1,0 +1,14 @@
+#ifndef SK_RLINE_H
+typedef struct sk_rline sk_rline;
+#endif
+
+typedef struct {
+    SPFLOAT min, max, cps;
+    sk_rline *rline;
+} sp_rline;
+
+int sp_rline_create(sp_rline **p);
+int sp_rline_destroy(sp_rline **p);
+int sp_rline_init(sp_data *sp, sp_rline *p);
+int sp_rline_compute(sp_data *sp, sp_rline *p,
+                         SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/rpi.h
@@ -1,0 +1,6 @@
+typedef struct sp_rpi{
+    sp_data *sp;
+    void *ud;
+    void (*callback)(sp_data *, void *);
+}sp_rpi;
+int sp_rpi_process(sp_data *sp, void *ud, void (*callback)(sp_data *, void *));
--- /dev/null
+++ b/h/rpt.h
@@ -1,0 +1,18 @@
+typedef struct sp_rpt{
+    uint32_t playpos;
+    uint32_t bufpos;
+    int running;
+    int count, reps;
+    SPFLOAT sr;
+    uint32_t size;
+    SPFLOAT bpm;
+    int div, rep;
+    sp_auxdata aux;
+    int rc;
+} sp_rpt;
+
+int sp_rpt_create(sp_rpt **p);
+int sp_rpt_destroy(sp_rpt **p);
+int sp_rpt_init(sp_data *sp, sp_rpt *p, SPFLOAT maxdur);
+int sp_rpt_compute(sp_data *sp, sp_rpt *p, SPFLOAT *trig,
+        SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/samphold.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT val;
+} sp_samphold;
+
+int sp_samphold_create(sp_samphold **p);
+int sp_samphold_destroy(sp_samphold **p);
+int sp_samphold_init(sp_data *sp, sp_samphold *p);
+int sp_samphold_compute(sp_data *sp, sp_samphold *p, SPFLOAT *trig, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/saturator.h
@@ -1,0 +1,15 @@
+typedef struct
+{
+    SPFLOAT drive;
+    SPFLOAT dcoffset;
+
+    SPFLOAT dcblocker[2][7];
+
+    SPFLOAT ai[6][7];
+    SPFLOAT aa[6][7];
+} sp_saturator;
+
+int sp_saturator_create(sp_saturator **p);
+int sp_saturator_destroy(sp_saturator **p);
+int sp_saturator_init(sp_data *sp, sp_saturator *p);
+int sp_saturator_compute(sp_data *sp, sp_saturator *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/scale.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT min, max;
+} sp_scale;
+
+int sp_scale_create(sp_scale **p);
+int sp_scale_destroy(sp_scale **p);
+int sp_scale_init(sp_data *sp, sp_scale *p);
+int sp_scale_compute(sp_data *sp, sp_scale *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/scrambler.h
@@ -1,0 +1,1 @@
+int sp_gen_scrambler(sp_data *sp, sp_ftbl *src, sp_ftbl **dest);
--- /dev/null
+++ b/h/sdelay.h
@@ -1,0 +1,9 @@
+typedef struct {
+    int size, pos;
+    SPFLOAT *buf;
+} sp_sdelay;
+
+int sp_sdelay_create(sp_sdelay **p);
+int sp_sdelay_destroy(sp_sdelay **p);
+int sp_sdelay_init(sp_data *sp, sp_sdelay *p, int size);
+int sp_sdelay_compute(sp_data *sp, sp_sdelay *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/slice.h
@@ -1,0 +1,12 @@
+typedef struct {
+    sp_ftbl *vals;
+    sp_ftbl *buf;
+    uint32_t id;
+    uint32_t pos;
+    uint32_t nextpos;
+} sp_slice;
+
+int sp_slice_create(sp_slice **p);
+int sp_slice_destroy(sp_slice **p);
+int sp_slice_init(sp_data *sp, sp_slice *p, sp_ftbl *vals, sp_ftbl *buf);
+int sp_slice_compute(sp_data *sp, sp_slice *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/smoothdelay.h
@@ -1,0 +1,25 @@
+typedef struct {
+    SPFLOAT del, maxdel, pdel;
+    SPFLOAT sr;
+    SPFLOAT feedback;
+
+    int counter;
+    int maxcount;
+
+    uint32_t maxbuf;
+
+    sp_auxdata buf1;
+    uint32_t bufpos1;
+    uint32_t deltime1;
+
+    sp_auxdata buf2;
+    uint32_t bufpos2;
+    uint32_t deltime2;
+    int curbuf;
+} sp_smoothdelay;
+
+int sp_smoothdelay_create(sp_smoothdelay **p);
+int sp_smoothdelay_destroy(sp_smoothdelay **p);
+int sp_smoothdelay_init(sp_data *sp, sp_smoothdelay *p,
+        SPFLOAT maxdel, uint32_t interp);
+int sp_smoothdelay_compute(sp_data *sp, sp_smoothdelay *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/smoother.h
@@ -1,0 +1,11 @@
+typedef struct{
+    SPFLOAT smooth;
+    SPFLOAT a1, b0, y0, psmooth;
+    SPFLOAT onedsr;
+}sp_smoother;
+
+int sp_smoother_create(sp_smoother **p);
+int sp_smoother_destroy(sp_smoother **p);
+int sp_smoother_init(sp_data *sp, sp_smoother *p);
+int sp_smoother_compute(sp_data *sp, sp_smoother *p, SPFLOAT *in, SPFLOAT *out);
+int sp_smoother_reset(sp_data *sp, sp_smoother *p, SPFLOAT *in);
--- /dev/null
+++ b/h/spa.h
@@ -1,0 +1,12 @@
+typedef struct {
+    SPFLOAT *buf;
+    uint32_t pos;
+    uint32_t bufsize;
+    sp_audio spa;
+    sp_auxdata aux;
+} sp_spa;
+
+int sp_spa_create(sp_spa **p);
+int sp_spa_destroy(sp_spa **p);
+int sp_spa_init(sp_data *sp, sp_spa *p, const char *filename);
+int sp_spa_compute(sp_data *sp, sp_spa *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/sparec.h
@@ -1,0 +1,13 @@
+typedef struct {
+    SPFLOAT *buf;
+    uint32_t pos;
+    uint32_t bufsize;
+    sp_audio spa;
+    sp_auxdata aux;
+} sp_sparec;
+
+int sp_sparec_create(sp_sparec **p);
+int sp_sparec_destroy(sp_sparec **p);
+int sp_sparec_init(sp_data *sp, sp_sparec *p, const char *filename);
+int sp_sparec_compute(sp_data *sp, sp_sparec *p, SPFLOAT *in, SPFLOAT *out);
+int sp_sparec_close(sp_data *sp, sp_sparec *p);
--- /dev/null
+++ b/h/switch.h
@@ -1,0 +1,9 @@
+typedef struct {
+    SPFLOAT mode;
+} sp_switch;
+
+int sp_switch_create(sp_switch **p);
+int sp_switch_destroy(sp_switch **p);
+int sp_switch_init(sp_data *sp, sp_switch *p);
+int sp_switch_compute(sp_data *sp, sp_switch *p, SPFLOAT *trig,
+    SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out);
--- /dev/null
+++ b/h/tadsr.h
@@ -1,0 +1,20 @@
+typedef struct {
+    SPFLOAT value;
+    SPFLOAT target;
+    SPFLOAT rate;
+    int state;
+    SPFLOAT attackRate;
+    SPFLOAT decayRate;
+    SPFLOAT sustainLevel;
+    SPFLOAT releaseRate;
+    SPFLOAT atk;
+    SPFLOAT rel;
+    SPFLOAT sus;
+    SPFLOAT dec;
+    int mode;
+} sp_tadsr;
+
+int sp_tadsr_create(sp_tadsr **p);
+int sp_tadsr_destroy(sp_tadsr **p);
+int sp_tadsr_init(sp_data *sp, sp_tadsr *p);
+int sp_tadsr_compute(sp_data *sp, sp_tadsr *p, SPFLOAT *trig, SPFLOAT *out);
--- /dev/null
+++ b/h/talkbox.h
@@ -1,0 +1,23 @@
+
+#ifndef SP_TALKBOX_BUFMAX
+#define SP_TALKBOX_BUFMAX 1600
+#endif
+
+typedef struct {
+    SPFLOAT quality;
+    SPFLOAT d0, d1, d2, d3, d4;
+    SPFLOAT u0, u1, u2, u3, u4;
+    SPFLOAT FX;
+    SPFLOAT emphasis;
+    SPFLOAT car0[SP_TALKBOX_BUFMAX];
+    SPFLOAT car1[SP_TALKBOX_BUFMAX];
+    SPFLOAT window[SP_TALKBOX_BUFMAX];
+    SPFLOAT buf0[SP_TALKBOX_BUFMAX];
+    SPFLOAT buf1[SP_TALKBOX_BUFMAX];
+    uint32_t K, N, O, pos;
+} sp_talkbox;
+
+int sp_talkbox_create(sp_talkbox **p);
+int sp_talkbox_destroy(sp_talkbox **p);
+int sp_talkbox_init(sp_data *sp, sp_talkbox *p);
+int sp_talkbox_compute(sp_data *sp, sp_talkbox *p, SPFLOAT *src, SPFLOAT *exc, SPFLOAT *out);
--- /dev/null
+++ b/h/tblrec.h
@@ -1,0 +1,10 @@
+typedef struct {
+    sp_ftbl *ft;
+    uint32_t index;
+    int record;
+} sp_tblrec;
+
+int sp_tblrec_create(sp_tblrec **p);
+int sp_tblrec_destroy(sp_tblrec **p);
+int sp_tblrec_init(sp_data *sp, sp_tblrec *p, sp_ftbl *ft);
+int sp_tblrec_compute(sp_data *sp, sp_tblrec *p, SPFLOAT *in, SPFLOAT *trig, SPFLOAT *out);
--- /dev/null
+++ b/h/tdiv.h
@@ -1,0 +1,8 @@
+typedef struct {
+    uint32_t num, counter, offset;
+} sp_tdiv;
+
+int sp_tdiv_create(sp_tdiv **p);
+int sp_tdiv_destroy(sp_tdiv **p);
+int sp_tdiv_init(sp_data *sp, sp_tdiv *p);
+int sp_tdiv_compute(sp_data *sp, sp_tdiv *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tenv.h
@@ -1,0 +1,14 @@
+typedef struct sp_tenv{
+    uint32_t pos, atk_end, rel_start, sr, totaldur;
+    SPFLOAT atk, rel, hold;
+    SPFLOAT atk_slp, rel_slp;
+    SPFLOAT last;
+    int sigmode;
+    SPFLOAT input;
+    int started;
+} sp_tenv;
+
+int sp_tenv_create(sp_tenv **p);
+int sp_tenv_destroy(sp_tenv **p);
+int sp_tenv_init(sp_data *sp, sp_tenv *p);
+int sp_tenv_compute(sp_data *sp, sp_tenv *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tenv2.h
@@ -1,0 +1,13 @@
+typedef struct {
+    int state;
+    SPFLOAT atk, rel;
+    uint32_t totaltime;
+    uint32_t timer;
+    SPFLOAT slope;
+    SPFLOAT last;
+} sp_tenv2;
+
+int sp_tenv2_create(sp_tenv2 **p);
+int sp_tenv2_destroy(sp_tenv2 **p);
+int sp_tenv2_init(sp_data *sp, sp_tenv2 *p);
+int sp_tenv2_compute(sp_data *sp, sp_tenv2 *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tenvx.h
@@ -1,0 +1,13 @@
+typedef struct sp_tenvx{
+    SPFLOAT atk, rel, hold;
+    SPFLOAT patk, prel;
+    uint32_t count;
+    SPFLOAT a_a, b_a;
+    SPFLOAT a_r, b_r;
+    SPFLOAT y;
+} sp_tenvx;
+
+int sp_tenvx_create(sp_tenvx **p);
+int sp_tenvx_destroy(sp_tenvx **p);
+int sp_tenvx_init(sp_data *sp, sp_tenvx *p);
+int sp_tenvx_compute(sp_data *sp, sp_tenvx *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tgate.h
@@ -1,0 +1,9 @@
+typedef struct {
+    SPFLOAT time;
+    uint32_t timer;
+} sp_tgate;
+
+int sp_tgate_create(sp_tgate **p);
+int sp_tgate_destroy(sp_tgate **p);
+int sp_tgate_init(sp_data *sp, sp_tgate *p);
+int sp_tgate_compute(sp_data *sp, sp_tgate *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/thresh.h
@@ -1,0 +1,9 @@
+typedef struct {
+    int init;
+    SPFLOAT prev, thresh, mode;
+} sp_thresh;
+
+int sp_thresh_create(sp_thresh **p);
+int sp_thresh_destroy(sp_thresh **p);
+int sp_thresh_init(sp_data *sp, sp_thresh *p);
+int sp_thresh_compute(sp_data *sp, sp_thresh *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/timer.h
@@ -1,0 +1,10 @@
+typedef struct {
+    int mode;
+    uint32_t pos;
+    SPFLOAT time;
+} sp_timer;
+
+int sp_timer_create(sp_timer **p);
+int sp_timer_destroy(sp_timer **p);
+int sp_timer_init(sp_data *sp, sp_timer *p);
+int sp_timer_compute(sp_data *sp, sp_timer *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tin.h
@@ -1,0 +1,9 @@
+typedef struct {
+    FILE *fp;
+    SPFLOAT val;
+} sp_tin;
+
+int sp_tin_create(sp_tin **p);
+int sp_tin_destroy(sp_tin **p);
+int sp_tin_init(sp_data *sp, sp_tin *p);
+int sp_tin_compute(sp_data *sp, sp_tin *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/trand.h
@@ -1,0 +1,8 @@
+typedef struct {
+    SPFLOAT min, max, val;
+} sp_trand;
+
+int sp_trand_create(sp_trand **p);
+int sp_trand_destroy(sp_trand **p);
+int sp_trand_init(sp_data *sp, sp_trand *p);
+int sp_trand_compute(sp_data *sp, sp_trand *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tseg.h
@@ -1,0 +1,14 @@
+typedef struct {
+    SPFLOAT beg,dur,end;
+    uint32_t steps;
+    uint32_t count;
+    SPFLOAT val;
+    SPFLOAT type;
+    SPFLOAT slope;
+    SPFLOAT tdivnsteps;
+} sp_tseg;
+
+int sp_tseg_create(sp_tseg **p);
+int sp_tseg_destroy(sp_tseg **p);
+int sp_tseg_init(sp_data *sp, sp_tseg *p, SPFLOAT ibeg);
+int sp_tseg_compute(sp_data *sp, sp_tseg *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/tseq.h
@@ -1,0 +1,11 @@
+typedef struct sp_tseq {
+    sp_ftbl *ft;
+    SPFLOAT val;
+    int32_t pos;
+    int shuf;
+} sp_tseq;
+
+int sp_tseq_create(sp_tseq **p);
+int sp_tseq_destroy(sp_tseq **p);
+int sp_tseq_init(sp_data *sp, sp_tseq *p, sp_ftbl *ft);
+int sp_tseq_compute(sp_data *sp, sp_tseq *p, SPFLOAT *trig, SPFLOAT *val);
--- /dev/null
+++ b/h/vardelay.h
@@ -1,0 +1,18 @@
+#ifndef SK_VARDELAY_H
+typedef struct sk_vardelay sk_vardelay;
+#endif
+
+typedef struct {
+    SPFLOAT del, maxdel;
+    SPFLOAT feedback;
+    sk_vardelay *v;
+    SPFLOAT *buf;
+} sp_vardelay;
+
+int sp_vardelay_create(sp_vardelay **p);
+int sp_vardelay_destroy(sp_vardelay **p);
+int sp_vardelay_init(sp_data *sp, sp_vardelay *p, SPFLOAT maxdel);
+int sp_vardelay_compute(sp_data *sp,
+                       sp_vardelay *p,
+                       SPFLOAT *in,
+                       SPFLOAT *out);
--- /dev/null
+++ b/h/voc.h
@@ -1,0 +1,39 @@
+
+#ifndef SP_VOC
+#define SP_VOC
+typedef struct sp_voc sp_voc;
+
+int sp_voc_create(sp_voc **voc);
+int sp_voc_destroy(sp_voc **voc);
+int sp_voc_init(sp_data *sp, sp_voc *voc);
+int sp_voc_compute(sp_data *sp, sp_voc *voc, SPFLOAT *out);
+int sp_voc_tract_compute(sp_data *sp, sp_voc *voc, SPFLOAT *in, SPFLOAT *out);
+
+void sp_voc_set_frequency(sp_voc *voc, SPFLOAT freq);
+SPFLOAT * sp_voc_get_frequency_ptr(sp_voc *voc);
+
+SPFLOAT* sp_voc_get_tract_diameters(sp_voc *voc);
+SPFLOAT* sp_voc_get_current_tract_diameters(sp_voc *voc);
+int sp_voc_get_tract_size(sp_voc *voc);
+SPFLOAT* sp_voc_get_nose_diameters(sp_voc *voc);
+int sp_voc_get_nose_size(sp_voc *voc);
+void sp_voc_set_tongue_shape(sp_voc *voc,
+    SPFLOAT tongue_index,
+    SPFLOAT tongue_diameter);
+void sp_voc_set_tenseness(sp_voc *voc, SPFLOAT breathiness);
+SPFLOAT * sp_voc_get_tenseness_ptr(sp_voc *voc);
+void sp_voc_set_velum(sp_voc *voc, SPFLOAT velum);
+SPFLOAT * sp_voc_get_velum_ptr(sp_voc *voc);
+
+void sp_voc_set_diameters(sp_voc *voc,
+    int blade_start,
+    int lip_start,
+    int tip_start,
+    SPFLOAT tongue_index,
+    SPFLOAT tongue_diameter,
+    SPFLOAT *diameters);
+
+int sp_voc_get_counter(sp_voc *voc);
+
+
+#endif
--- /dev/null
+++ b/h/wavin.h
@@ -1,0 +1,8 @@
+typedef struct sp_wavin sp_wavin;
+int sp_wavin_create(sp_wavin **p);
+int sp_wavin_destroy(sp_wavin **p);
+int sp_wavin_init(sp_data *sp, sp_wavin *p, const char *filename);
+int sp_wavin_compute(sp_data *sp, sp_wavin *p, SPFLOAT *in, SPFLOAT *out);
+int sp_wavin_get_sample(sp_data *sp, sp_wavin *p, SPFLOAT *out, SPFLOAT pos);
+int sp_wavin_reset_to_start(sp_data *sp, sp_wavin *p);
+int sp_wavin_seek(sp_data *sp, sp_wavin *p, unsigned long sample);
--- /dev/null
+++ b/h/wavout.h
@@ -1,0 +1,5 @@
+typedef struct sp_wavout sp_wavout;
+int sp_wavout_create(sp_wavout **p);
+int sp_wavout_destroy(sp_wavout **p);
+int sp_wavout_init(sp_data *sp, sp_wavout *p, const char *filename);
+int sp_wavout_compute(sp_data *sp, sp_wavout *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/wpkorg35.h
@@ -1,0 +1,31 @@
+typedef struct {
+    /* LPF1 */
+    SPFLOAT lpf1_a;
+    SPFLOAT lpf1_z;
+
+    /* LPF2 */
+    SPFLOAT lpf2_a;
+    SPFLOAT lpf2_b;
+    SPFLOAT lpf2_z;
+
+    /* HPF */
+    SPFLOAT hpf_a;
+    SPFLOAT hpf_b;
+    SPFLOAT hpf_z;
+
+    SPFLOAT alpha;
+
+    SPFLOAT cutoff;
+    SPFLOAT res;
+    SPFLOAT saturation;
+
+    SPFLOAT pcutoff;
+    SPFLOAT pres;
+
+    uint32_t nonlinear;
+} sp_wpkorg35;
+
+int sp_wpkorg35_create(sp_wpkorg35 **p);
+int sp_wpkorg35_destroy(sp_wpkorg35 **p);
+int sp_wpkorg35_init(sp_data *sp, sp_wpkorg35 *p);
+int sp_wpkorg35_compute(sp_data *sp, sp_wpkorg35 *p, SPFLOAT *in, SPFLOAT *out);
--- /dev/null
+++ b/h/zitarev.h
@@ -1,0 +1,21 @@
+typedef struct {
+    void *faust;
+    int argpos;
+    SPFLOAT *args[11];
+    SPFLOAT *in_delay;
+    SPFLOAT *lf_x;
+    SPFLOAT *rt60_low;
+    SPFLOAT *rt60_mid;
+    SPFLOAT *hf_damping;
+    SPFLOAT *eq1_freq;
+    SPFLOAT *eq1_level;
+    SPFLOAT *eq2_freq;
+    SPFLOAT *eq2_level;
+    SPFLOAT *mix;
+    SPFLOAT *level;
+} sp_zitarev;
+
+int sp_zitarev_create(sp_zitarev **p);
+int sp_zitarev_destroy(sp_zitarev **p);
+int sp_zitarev_init(sp_data *sp, sp_zitarev *p);
+int sp_zitarev_compute(sp_data *sp, sp_zitarev *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2);
--- /dev/null
+++ b/lib/dr_wav/Makefile
@@ -1,0 +1,5 @@
+CFLAGS += -Ilib/dr_wav/
+LPATHS += lib/dr_wav/dr_wav.o
+
+lib/dr_wav/dr_wav.o: lib/dr_wav/dr_wav.c
+	$(C99) $(CFLAGS) $< -c -o $@
--- /dev/null
+++ b/lib/dr_wav/dr_wav.c
@@ -1,0 +1,56 @@
+#define DR_WAV_IMPLEMENTATION
+#include "dr_wav.h"
+
+size_t sp_drwav_size(void)
+{
+    return sizeof(drwav);
+}
+
+int sp_drwav_init_file(drwav* pWav, const char* filename)
+{
+    return drwav_init_file(pWav, filename);
+}
+
+size_t sp_drwav_read_f32(drwav* pWav,
+                         size_t samplesToRead,
+                         float* pBufferOut)
+{
+    return drwav_read_f32(pWav, samplesToRead, pBufferOut);
+}
+
+size_t sp_drwav_sampcount(drwav *wav)
+{
+    return wav->totalSampleCount;
+}
+
+void sp_drwav_uninit(drwav *wav)
+{
+    drwav_uninit(wav);
+}
+
+int sp_drwav_seek_to_sample(drwav* pWav, size_t sample)
+{
+    return drwav_seek_to_sample(pWav, sample);
+}
+
+drwav * sp_drwav_open_mono_write(const char *filename, int sr)
+{
+    drwav_data_format format;
+    format.container = drwav_container_riff;
+    format.format = DR_WAVE_FORMAT_IEEE_FLOAT;
+    format.channels = 1;
+    format.sampleRate = sr;
+    format.bitsPerSample = 32;
+
+    return drwav_open_file_write(filename, &format);
+}
+
+size_t sp_drwav_write(drwav* pWav, size_t samplesToWrite, const void* pData)
+{
+    return drwav_write(pWav, samplesToWrite, pData);
+}
+
+void sp_drwav_close(drwav* pWav)
+{
+    drwav_close(pWav);
+}
--- /dev/null
+++ b/lib/dr_wav/dr_wav.h
@@ -1,0 +1,3490 @@
+// WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file.
+// dr_wav - v0.7 - 2017-11-04
+//
+// David Reid - mackron@gmail.com
+
+// USAGE
+//
+// This is a single-file library. To use it, do something like the following in one .c file.
+//     #define DR_WAV_IMPLEMENTATION
+//     #include "dr_wav.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. Do something
+// like the following to read audio data:
+//
+//     drwav wav;
+//     if (!drwav_init_file(&wav, "my_song.wav")) {
+//         // Error opening WAV file.
+//     }
+//
+//     drwav_int32* pDecodedInterleavedSamples = malloc(wav.totalSampleCount * sizeof(drwav_int32));
+//     size_t numberOfSamplesActuallyDecoded = drwav_read_s32(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
+//
+//     ...
+//
+//     drwav_uninit(&wav);
+//
+// You can also use drwav_open() to allocate and initialize the loader for you:
+//
+//     drwav* pWav = drwav_open_file("my_song.wav");
+//     if (pWav == NULL) {
+//         // Error opening WAV file.
+//     }
+//
+//     ...
+//
+//     drwav_close(pWav);
+//
+// If you just want to quickly open and read the audio data in a single operation you can do something like this:
+//
+//     unsigned int channels;
+//     unsigned int sampleRate;
+//     drwav_uint64 totalSampleCount;
+//     float* pSampleData = drwav_open_and_read_file_s32("my_song.wav", &channels, &sampleRate, &totalSampleCount);
+//     if (pSampleData == NULL) {
+//         // Error opening and reading WAV file.
+//     }
+//
+//     ...
+//
+//     drwav_free(pSampleData);
+//
+// The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in
+// this case), but you can still output the audio data in it's internal format (see notes below for supported formats):
+//
+//     size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
+//
+// You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for
+// a particular data format:
+//
+//     size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
+//
+//
+// dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work
+// without any manual intervention.
+//
+//
+// dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
+// drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write() to write samples, or drwav_write_raw() to write
+// raw data in the "data" chunk.
+//
+//     drwav_data_format format;
+//     format.container = drwav_container_riff;     // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
+//     format.format = DR_WAVE_FORMAT_PCM;          // <-- Any of the DR_WAVE_FORMAT_* codes.
+//     format.channels = 2;
+//     format.sampleRate = 44100;
+//     format.bitsPerSample = 16;
+//     drwav* pWav = drwav_open_file_write("data/recording.wav", &format);
+//
+//     ...
+//
+//     drwav_uint64 samplesWritten = drwav_write(pWav, sampleCount, pSamples);
+//
+//
+//
+// OPTIONS
+// #define these options before including this file.
+//
+// #define DR_WAV_NO_CONVERSION_API
+//   Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32().
+//
+// #define DR_WAV_NO_STDIO
+//   Disables drwav_open_file(), drwav_open_file_write(), etc.
+//
+//
+//
+// QUICK NOTES
+// - Samples are always interleaved.
+// - The default read function does not do any data conversion. Use drwav_read_f32() to read and convert audio data
+//   to IEEE 32-bit floating point samples, drwav_read_s32() to read samples as signed 32-bit PCM and drwav_read_s16()
+//   to read samples as signed 16-bit PCM. Tested and supported internal formats include the following:
+//   - Unsigned 8-bit PCM
+//   - Signed 12-bit PCM
+//   - Signed 16-bit PCM
+//   - Signed 24-bit PCM
+//   - Signed 32-bit PCM
+//   - IEEE 32-bit floating point.
+//   - IEEE 64-bit floating point.
+//   - A-law and u-law
+//   - Microsoft ADPCM
+//   - IMA ADPCM (DVI, format code 0x11)
+// - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
+
+
+#ifndef dr_wav_h
+#define dr_wav_h
+
+#include <stddef.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef   signed char    drwav_int8;
+typedef unsigned char    drwav_uint8;
+typedef   signed short   drwav_int16;
+typedef unsigned short   drwav_uint16;
+typedef   signed int     drwav_int32;
+typedef unsigned int     drwav_uint32;
+typedef   signed __int64 drwav_int64;
+typedef unsigned __int64 drwav_uint64;
+#else
+#include <stdint.h>
+typedef int8_t           drwav_int8;
+typedef uint8_t          drwav_uint8;
+typedef int16_t          drwav_int16;
+typedef uint16_t         drwav_uint16;
+typedef int32_t          drwav_int32;
+typedef uint32_t         drwav_uint32;
+typedef int64_t          drwav_int64;
+typedef uint64_t         drwav_uint64;
+#endif
+typedef drwav_uint8      drwav_bool8;
+typedef drwav_uint32     drwav_bool32;
+#define DRWAV_TRUE       1
+#define DRWAV_FALSE      0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Common data formats.
+#define DR_WAVE_FORMAT_PCM          0x1
+#define DR_WAVE_FORMAT_ADPCM        0x2
+#define DR_WAVE_FORMAT_IEEE_FLOAT   0x3
+#define DR_WAVE_FORMAT_ALAW         0x6
+#define DR_WAVE_FORMAT_MULAW        0x7
+#define DR_WAVE_FORMAT_DVI_ADPCM    0x11
+#define DR_WAVE_FORMAT_EXTENSIBLE   0xFFFE
+
+typedef enum
+{
+    drwav_seek_origin_start,
+    drwav_seek_origin_current
+} drwav_seek_origin;
+
+typedef enum
+{
+    drwav_container_riff,
+    drwav_container_w64
+} drwav_container;
+
+// Callback for when data is read. Return value is the number of bytes actually read.
+//
+// pUserData   [in]  The user data that was passed to drwav_init(), drwav_open() and family.
+// pBufferOut  [out] The output buffer.
+// bytesToRead [in]  The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+//
+// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data is written. Returns value is the number of bytes actually written.
+//
+// pUserData    [in]  The user data that was passed to drwav_init_write(), drwav_open_write() and family.
+// pData        [out] A pointer to the data to write.
+// bytesToWrite [in]  The number of bytes to write.
+//
+// Returns the number of bytes actually written.
+//
+// If the return value differs from bytesToWrite, it indicates an error.
+typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+// offset    [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin    [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+// will be either drwav_seek_origin_start or drwav_seek_origin_current.
+typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
+
+// Structure for internal use. Only used for loaders opened with drwav_open_memory().
+typedef struct
+{
+    const drwav_uint8* data;
+    size_t dataSize;
+    size_t currentReadPos;
+} drwav__memory_stream;
+
+// Structure for internal use. Only used for writers opened with drwav_open_memory_write().
+typedef struct
+{
+    void** ppData;
+    size_t* pDataSize;
+    size_t dataSize;
+    size_t dataCapacity;
+    size_t currentWritePos;
+} drwav__memory_stream_write;
+
+typedef struct
+{
+    drwav_container container;  // RIFF, W64.
+    drwav_uint32 format;        // DR_WAVE_FORMAT_*
+    drwav_uint32 channels;
+    drwav_uint32 sampleRate;
+    drwav_uint32 bitsPerSample;
+} drwav_data_format;
+
+typedef struct
+{
+    // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
+    // that require support for data formats not natively supported by dr_wav.
+    drwav_uint16 formatTag;
+
+    // The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc.
+    drwav_uint16 channels;
+
+    // The sample rate. Usually set to something like 44100.
+    drwav_uint32 sampleRate;
+
+    // Average bytes per second. You probably don't need this, but it's left here for informational purposes.
+    drwav_uint32 avgBytesPerSec;
+
+    // Block align. This is equal to the number of channels * bytes per sample.
+    drwav_uint16 blockAlign;
+
+    // Bit's per sample.
+    drwav_uint16 bitsPerSample;
+
+    // The size of the extended data. Only used internally for validation, but left here for informational purposes.
+    drwav_uint16 extendedSize;
+
+    // The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
+    // is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
+    // many bits a valid per sample. Mainly used for informational purposes.
+    drwav_uint16 validBitsPerSample;
+
+    // The channel mask. Not used at the moment.
+    drwav_uint32 channelMask;
+
+    // The sub-format, exactly as specified by the wave file.
+    drwav_uint8 subFormat[16];
+} drwav_fmt;
+
+typedef struct drwav
+{
+    // A pointer to the function to call when more data is needed.
+    drwav_read_proc onRead;
+
+    // A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode.
+    drwav_write_proc onWrite;
+
+    // A pointer to the function to call when the wav file needs to be seeked.
+    drwav_seek_proc onSeek;
+
+    // The user data to pass to callbacks.
+    void* pUserData;
+
+
+    // Whether or not the WAV file is formatted as a standard RIFF file or W64.
+    drwav_container container;
+
+
+    // Structure containing format information exactly as specified by the wav file.
+    drwav_fmt fmt;
+
+    // The sample rate. Will be set to something like 44100.
+    drwav_uint32 sampleRate;
+
+    // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc.
+    drwav_uint16 channels;
+
+    // The bits per sample. Will be set to somthing like 16, 24, etc.
+    drwav_uint16 bitsPerSample;
+
+    // The number of bytes per sample.
+    drwav_uint16 bytesPerSample;
+
+    // Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE).
+    drwav_uint16 translatedFormatTag;
+
+    // The total number of samples making up the audio data. Use <totalSampleCount> * <bytesPerSample> to calculate
+    // the required size of a buffer to hold the entire audio data.
+    drwav_uint64 totalSampleCount;
+
+
+    // The size in bytes of the data chunk.
+    drwav_uint64 dataChunkDataSize;
+
+    // The position in the stream of the first byte of the data chunk. This is used for seeking.
+    drwav_uint64 dataChunkDataPos;
+
+    // The number of bytes remaining in the data chunk.
+    drwav_uint64 bytesRemaining;
+
+
+    // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory().
+    drwav__memory_stream memoryStream;
+    drwav__memory_stream_write memoryStreamWrite;
+
+    // Generic data for compressed formats. This data is shared across all block-compressed formats.
+    struct
+    {
+        drwav_uint64 iCurrentSample;    // The index of the next sample that will be read by drwav_read_*(). This is used with "totalSampleCount" to ensure we don't read excess samples at the end of the last block.
+    } compressed;
+
+    // Microsoft ADPCM specific data.
+    struct
+    {
+        drwav_uint32 bytesRemainingInBlock;
+        drwav_uint16 predictor[2];
+        drwav_int32  delta[2];
+        drwav_int32  cachedSamples[4];  // Samples are stored in this cache during decoding.
+        drwav_uint32 cachedSampleCount;
+        drwav_int32  prevSamples[2][2]; // The previous 2 samples for each channel (2 channels at most).
+    } msadpcm;
+
+    // IMA ADPCM specific data.
+    struct
+    {
+        drwav_uint32 bytesRemainingInBlock;
+        drwav_int32  predictor[2];
+        drwav_int32  stepIndex[2];
+        drwav_int32  cachedSamples[16]; // Samples are stored in this cache during decoding.
+        drwav_uint32 cachedSampleCount;
+    } ima;
+} drwav;
+
+
+// Initializes a pre-allocated drwav object.
+//
+// onRead    [in]           The function to call when data needs to be read from the client.
+// onSeek    [in]           The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the loader with drwav_uninit().
+//
+// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+// a drwav object on the heap and return a pointer to it.
+//
+// See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+
+// Initializes a pre-allocated drwav object for writing.
+//
+// onWrite   [in]           The function to call when data needs to be written.
+// onSeek    [in]           The function to call when the write position needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the writer with drwav_uninit().
+//
+// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+// a drwav object on the heap and return a pointer to it.
+//
+// See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+
+// Uninitializes the given drwav object.
+//
+// Use this only for objects initialized with drwav_init().
+void drwav_uninit(drwav* pWav);
+
+
+// Opens a wav file using the given callbacks.
+//
+// onRead    [in]           The function to call when data needs to be read from the client.
+// onSeek    [in]           The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns null on error.
+//
+// Close the loader with drwav_close().
+//
+// This is the lowest level function for opening a WAV file. You can also use drwav_open_file() and drwav_open_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+// initializing it.
+//
+// See also: drwav_open_file(), drwav_open_memory(), drwav_close()
+drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+
+// Opens a wav file for writing using the given callbacks.
+//
+// onWrite   [in]           The function to call when data needs to be written.
+// onSeek    [in]           The function to call when the write position needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+//
+// Returns null on error.
+//
+// Close the loader with drwav_close().
+//
+// This is the lowest level function for opening a WAV file. You can also use drwav_open_file_write() and drwav_open_memory_write()
+// to open the stream from a file or from a block of memory respectively.
+//
+// This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+// initializing it.
+//
+// See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close()
+drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+
+// Uninitializes and deletes the the given drwav object.
+//
+// Use this only for objects created with drwav_open().
+void drwav_close(drwav* pWav);
+
+
+// Reads raw audio data.
+//
+// This is the lowest level function for reading audio data. It simply reads the given number of
+// bytes of the raw internal sample data.
+//
+// Consider using drwav_read_s16(), drwav_read_s32() or drwav_read_f32() for reading sample data in
+// a consistent format.
+//
+// Returns the number of bytes actually read.
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
+
+// Reads a chunk of audio data in the native internal format.
+//
+// This is typically the most efficient way to retrieve audio data, but it does not do any format
+// conversions which means you'll need to convert the data manually if required.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached or
+// you have requested more samples than can possibly fit in the output buffer.
+//
+// This function will only work when sample data is of a fixed size and uncompressed. If you are
+// using a compressed format consider using drwav_read_raw() or drwav_read_s16/s32/f32/etc().
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut);
+
+// Seeks to the given sample.
+//
+// Returns true if successful; false otherwise.
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample);
+
+
+// Writes raw audio data.
+//
+// Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
+
+// Writes audio data based on sample counts.
+//
+// Returns the number of samples written.
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData);
+
+
+
+//// Convertion Utilities ////
+#ifndef DR_WAV_NO_CONVERSION_API
+
+// Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached.
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples.
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples.
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples.
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples.
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples.
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to signed 16-bit PCM samples.
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to signed 16-bit PCM samples.
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+// Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached.
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples.
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to IEEE 32-bit floating point samples.
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to IEEE 32-bit floating point samples.
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+// Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached.
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples.
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples.
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples.
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples.
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples.
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to signed 32-bit PCM samples.
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to signed 32-bit PCM samples.
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+#endif  //DR_WAV_NO_CONVERSION_API
+
+
+//// High-Level Convenience Helpers ////
+
+#ifndef DR_WAV_NO_STDIO
+
+// Helper for initializing a wave file using stdio.
+//
+// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename);
+
+// Helper for initializing a wave file for writing using stdio.
+//
+// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat);
+
+// Helper for opening a wave file using stdio.
+//
+// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav* drwav_open_file(const char* filename);
+
+// Helper for opening a wave file for writing using stdio.
+//
+// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat);
+
+#endif  //DR_WAV_NO_STDIO
+
+// Helper for initializing a loader from a pre-allocated memory buffer.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drwav object.
+//
+// The buffer should contain the contents of the entire wave file, not just the sample data.
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize);
+
+// Helper for initializing a writer which outputs data to a memory buffer.
+//
+// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+//
+// The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
+// considered valid until after drwav_uninit() has been called anyway.
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
+
+// Helper for opening a loader from a pre-allocated memory buffer.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drwav object.
+//
+// The buffer should contain the contents of the entire wave file, not just the sample data.
+drwav* drwav_open_memory(const void* data, size_t dataSize);
+
+// Helper for opening a writer which outputs data to a memory buffer.
+//
+// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+//
+// The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be
+// considered valid until after drwav_close() has been called anyway.
+drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+// Opens and reads a wav file in a single operation.
+drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+#ifndef DR_WAV_NO_STDIO
+// Opens an decodes a wav file in a single operation.
+drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+#endif
+
+// Opens an decodes a wav file from a block of memory in a single operation.
+drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+#endif
+
+// Frees data that was allocated internally by dr_wav.
+void drwav_free(void* pDataReturnedByOpenAndRead);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  // dr_wav_h
+
+
+/////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+/////////////////////////////////////////////////////
+
+#ifdef DR_WAV_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h> // For memcpy(), memset()
+#include <limits.h> // For INT_MAX
+
+#ifndef DR_WAV_NO_STDIO
+#include <stdio.h>
+#endif
+
+// Standard library stuff.
+#ifndef DRWAV_ASSERT
+#include <assert.h>
+#define DRWAV_ASSERT(expression)           assert(expression)
+#endif
+#ifndef DRWAV_MALLOC
+#define DRWAV_MALLOC(sz)                   malloc((sz))
+#endif
+#ifndef DRWAV_REALLOC
+#define DRWAV_REALLOC(p, sz)               realloc((p), (sz))
+#endif
+#ifndef DRWAV_FREE
+#define DRWAV_FREE(p)                      free((p))
+#endif
+#ifndef DRWAV_COPY_MEMORY
+#define DRWAV_COPY_MEMORY(dst, src, sz)    memcpy((dst), (src), (sz))
+#endif
+#ifndef DRWAV_ZERO_MEMORY
+#define DRWAV_ZERO_MEMORY(p, sz)           memset((p), 0, (sz))
+#endif
+
+#define drwav_countof(x)                   (sizeof(x) / sizeof(x[0]))
+#define drwav_align(x, a)                  ((((x) + (a) - 1) / (a)) * (a))
+#define drwav_min(a, b)                    (((a) < (b)) ? (a) : (b))
+#define drwav_max(a, b)                    (((a) > (b)) ? (a) : (b))
+#define drwav_clamp(x, lo, hi)             (drwav_max((lo), drwav_min((hi), (x))))
+
+#define drwav_assert                       DRWAV_ASSERT
+#define drwav_copy_memory                  DRWAV_COPY_MEMORY
+#define drwav_zero_memory                  DRWAV_ZERO_MEMORY
+
+
+#define DRWAV_MAX_SIMD_VECTOR_SIZE         64  // 64 for AVX-512 in the future.
+
+#ifdef _MSC_VER
+#define DRWAV_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRWAV_INLINE inline __attribute__((always_inline))
+#else
+#define DRWAV_INLINE inline
+#endif
+#endif
+
+// I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know.
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+    #if defined(_WIN64)
+        #define SIZE_MAX    ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
+    #else
+        #define SIZE_MAX    0xFFFFFFFF
+    #endif
+#endif
+
+static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};    // 66666972-912E-11CF-A5D6-28DB04C10000
+static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    // 65766177-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    // 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    // 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    // 74636166-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};    // 61746164-ACF3-11D3-8CD1-00C04F8EDB8A
+
+static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
+{
+    const drwav_uint32* a32 = (const drwav_uint32*)a;
+    const drwav_uint32* b32 = (const drwav_uint32*)b;
+
+    return
+        a32[0] == b32[0] &&
+        a32[1] == b32[1] &&
+        a32[2] == b32[2] &&
+        a32[3] == b32[3];
+}
+
+static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b)
+{
+    return
+        a[0] == b[0] &&
+        a[1] == b[1] &&
+        a[2] == b[2] &&
+        a[3] == b[3];
+}
+
+
+
+static DRWAV_INLINE int drwav__is_little_endian()
+{
+    int n = 1;
+    return (*(char*)&n) == 1;
+}
+
+static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data)
+{
+    if (drwav__is_little_endian()) {
+        return (data[0] << 0) | (data[1] << 8);
+    } else {
+        return (data[1] << 0) | (data[0] << 8);
+    }
+}
+
+static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data)
+{
+    return (short)drwav__bytes_to_u16(data);
+}
+
+static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data)
+{
+    if (drwav__is_little_endian()) {
+        return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+    } else {
+        return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24);
+    }
+}
+
+static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data)
+{
+    if (drwav__is_little_endian()) {
+        return
+            ((drwav_uint64)data[0] <<  0) | ((drwav_uint64)data[1] <<  8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
+            ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
+    } else {
+        return
+            ((drwav_uint64)data[7] <<  0) | ((drwav_uint64)data[6] <<  8) | ((drwav_uint64)data[5] << 16) | ((drwav_uint64)data[4] << 24) |
+            ((drwav_uint64)data[3] << 32) | ((drwav_uint64)data[2] << 40) | ((drwav_uint64)data[1] << 48) | ((drwav_uint64)data[0] << 56);
+    }
+}
+
+static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid)
+{
+    int i;
+    for (i = 0; i < 16; ++i) {
+        guid[i] = data[i];
+    }
+}
+
+
+static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
+{
+    return
+        formatTag == DR_WAVE_FORMAT_ADPCM ||
+        formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
+}
+
+
+typedef struct
+{
+    union
+    {
+        drwav_uint8 fourcc[4];
+        drwav_uint8 guid[16];
+    } id;
+
+    // The size in bytes of the chunk.
+    drwav_uint64 sizeInBytes;
+
+    // RIFF = 2 byte alignment.
+    // W64  = 8 byte alignment.
+    unsigned int paddingSize;
+
+} drwav__chunk_header;
+
+static drwav_bool32 drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav__chunk_header* pHeaderOut)
+{
+    if (container == drwav_container_riff) {
+        if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
+            return DRWAV_FALSE;
+        }
+
+        unsigned char sizeInBytes[4];
+        if (onRead(pUserData, sizeInBytes, 4) != 4) {
+            return DRWAV_FALSE;
+        }
+
+        pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes);
+        pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 2);
+        *pRunningBytesReadOut += 8;
+    } else {
+        if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
+            return DRWAV_FALSE;
+        }
+
+        unsigned char sizeInBytes[8];
+        if (onRead(pUserData, sizeInBytes, 8) != 8) {
+            return DRWAV_FALSE;
+        }
+
+        pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24;    // <-- Subtract 24 because w64 includes the size of the header.
+        pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 8);
+        pRunningBytesReadOut += 24;
+    }
+
+    return DRWAV_TRUE;
+}
+
+static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+    drwav_uint64 bytesRemainingToSeek = offset;
+    while (bytesRemainingToSeek > 0) {
+        if (bytesRemainingToSeek > 0x7FFFFFFF) {
+            if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+                return DRWAV_FALSE;
+            }
+            bytesRemainingToSeek -= 0x7FFFFFFF;
+        } else {
+            if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
+                return DRWAV_FALSE;
+            }
+            bytesRemainingToSeek = 0;
+        }
+    }
+
+    return DRWAV_TRUE;
+}
+
+
+static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
+{
+    drwav__chunk_header header;
+    if (!drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header)) {
+        return DRWAV_FALSE;
+    }
+
+
+    // Skip junk chunks.
+    if ((container == drwav_container_riff && drwav__fourcc_equal(header.id.fourcc, "JUNK")) || (container == drwav_container_w64 && drwav__guid_equal(header.id.guid, drwavGUID_W64_JUNK))) {
+        if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
+            return DRWAV_FALSE;
+        }
+        *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
+
+        return drwav__read_fmt(onRead, onSeek, pUserData, container, pRunningBytesReadOut, fmtOut);
+    }
+
+
+    // Validation.
+    if (container == drwav_container_riff) {
+        if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) {
+            return DRWAV_FALSE;
+        }
+    } else {
+        if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
+            return DRWAV_FALSE;
+        }
+    }
+
+
+    unsigned char fmt[16];
+    if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
+        return DRWAV_FALSE;
+    }
+    *pRunningBytesReadOut += sizeof(fmt);
+
+    fmtOut->formatTag      = drwav__bytes_to_u16(fmt + 0);
+    fmtOut->channels       = drwav__bytes_to_u16(fmt + 2);
+    fmtOut->sampleRate     = drwav__bytes_to_u32(fmt + 4);
+    fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8);
+    fmtOut->blockAlign     = drwav__bytes_to_u16(fmt + 12);
+    fmtOut->bitsPerSample  = drwav__bytes_to_u16(fmt + 14);
+
+    fmtOut->extendedSize       = 0;
+    fmtOut->validBitsPerSample = 0;
+    fmtOut->channelMask        = 0;
+    memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
+
+    if (header.sizeInBytes > 16) {
+        unsigned char fmt_cbSize[2];
+        if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
+            return DRWAV_FALSE;    // Expecting more data.
+        }
+        *pRunningBytesReadOut += sizeof(fmt_cbSize);
+
+        int bytesReadSoFar = 18;
+
+        fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize);
+        if (fmtOut->extendedSize > 0) {
+            // Simple validation.
+            if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+                if (fmtOut->extendedSize != 22) {
+                    return DRWAV_FALSE;
+                }
+            }
+
+            if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+                unsigned char fmtext[22];
+                if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
+                    return DRWAV_FALSE;    // Expecting more data.
+                }
+
+                fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0);
+                fmtOut->channelMask        = drwav__bytes_to_u32(fmtext + 2);
+                drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat);
+            } else {
+                if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
+                    return DRWAV_FALSE;
+                }
+            }
+            *pRunningBytesReadOut += fmtOut->extendedSize;
+
+            bytesReadSoFar += fmtOut->extendedSize;
+        }
+
+        // Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size.
+        if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
+            return DRWAV_FALSE;
+        }
+        *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
+    }
+
+    if (header.paddingSize > 0) {
+        if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
+            return DRWAV_FALSE;
+        }
+        *pRunningBytesReadOut += header.paddingSize;
+    }
+
+    return DRWAV_TRUE;
+}
+
+
+#ifndef DR_WAV_NO_STDIO
+static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+    return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
+{
+    return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
+}
+
+static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
+{
+    return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename)
+{
+    FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+    if (fopen_s(&pFile, filename, "rb") != 0) {
+        return DRWAV_FALSE;
+    }
+#else
+    pFile = fopen(filename, "rb");
+    if (pFile == NULL) {
+        return DRWAV_FALSE;
+    }
+#endif
+
+    return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
+}
+
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat)
+{
+    FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+    if (fopen_s(&pFile, filename, "wb") != 0) {
+        return DRWAV_FALSE;
+    }
+#else
+    pFile = fopen(filename, "wb");
+    if (pFile == NULL) {
+        return DRWAV_FALSE;
+    }
+#endif
+
+    return drwav_init_write(pWav, pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+}
+
+drwav* drwav_open_file(const char* filename)
+{
+    FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+    if (fopen_s(&pFile, filename, "rb") != 0) {
+        return NULL;
+    }
+#else
+    pFile = fopen(filename, "rb");
+    if (pFile == NULL) {
+        return NULL;
+    }
+#endif
+
+    drwav* pWav = drwav_open(drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
+    if (pWav == NULL) {
+        fclose(pFile);
+        return NULL;
+    }
+
+    return pWav;
+}
+
+drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat)
+{
+    FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+    if (fopen_s(&pFile, filename, "wb") != 0) {
+        return NULL;
+    }
+#else
+    pFile = fopen(filename, "wb");
+    if (pFile == NULL) {
+        return NULL;
+    }
+#endif
+
+    drwav* pWav = drwav_open_write(pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+    if (pWav == NULL) {
+        fclose(pFile);
+        return NULL;
+    }
+
+    return pWav;
+}
+#endif  //DR_WAV_NO_STDIO
+
+
+static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+    drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+    drwav_assert(memory != NULL);
+    drwav_assert(memory->dataSize >= memory->currentReadPos);
+
+    size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
+    if (bytesToRead > bytesRemaining) {
+        bytesToRead = bytesRemaining;
+    }
+
+    if (bytesToRead > 0) {
+        DRWAV_COPY_MEMORY(pBufferOut, memory->data + memory->currentReadPos, bytesToRead);
+        memory->currentReadPos += bytesToRead;
+    }
+
+    return bytesToRead;
+}
+
+static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
+{
+    drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+    drwav_assert(memory != NULL);
+
+    if (origin == drwav_seek_origin_current) {
+        if (offset > 0) {
+            if (memory->currentReadPos + offset > memory->dataSize) {
+                offset = (int)(memory->dataSize - memory->currentReadPos);  // Trying to seek too far forward.
+            }
+        } else {
+            if (memory->currentReadPos < (size_t)-offset) {
+                offset = -(int)memory->currentReadPos;  // Trying to seek too far backwards.
+            }
+        }
+
+        // This will never underflow thanks to the clamps above.
+        memory->currentReadPos += offset;
+    } else {
+        if ((drwav_uint32)offset <= memory->dataSize) {
+            memory->currentReadPos = offset;
+        } else {
+            memory->currentReadPos = memory->dataSize;  // Trying to seek too far forward.
+        }
+    }
+
+    return DRWAV_TRUE;
+}
+
+static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
+{
+    drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+    drwav_assert(memory != NULL);
+    drwav_assert(memory->dataCapacity >= memory->currentWritePos);
+
+    size_t bytesRemaining = memory->dataCapacity - memory->currentWritePos;
+    if (bytesRemaining < bytesToWrite) {
+        // Need to reallocate.
+        size_t newDataCapacity = (memory->dataCapacity == 0) ? 256 : memory->dataCapacity * 2;
+
+        // If doubling wasn't enough, just make it the minimum required size to write the data.
+        if ((newDataCapacity - memory->currentWritePos) < bytesToWrite) {
+            newDataCapacity = memory->currentWritePos + bytesToWrite;
+        }
+
+        void* pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity);
+        if (pNewData == NULL) {
+            return 0;
+        }
+
+        *memory->ppData = pNewData;
+        memory->dataCapacity = newDataCapacity;
+    }
+
+    drwav_uint8* pDataOut = (drwav_uint8*)(*memory->ppData);
+    DRWAV_COPY_MEMORY(pDataOut + memory->currentWritePos, pDataIn, bytesToWrite);
+
+    memory->currentWritePos += bytesToWrite;
+    if (memory->dataSize < memory->currentWritePos) {
+        memory->dataSize = memory->currentWritePos;
+    }
+
+    *memory->pDataSize = memory->dataSize;
+
+    return bytesToWrite;
+}
+
+static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
+{
+    drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+    drwav_assert(memory != NULL);
+
+    if (origin == drwav_seek_origin_current) {
+        if (offset > 0) {
+            if (memory->currentWritePos + offset > memory->dataSize) {
+                offset = (int)(memory->dataSize - memory->currentWritePos);  // Trying to seek too far forward.
+            }
+        } else {
+            if (memory->currentWritePos < (size_t)-offset) {
+                offset = -(int)memory->currentWritePos;  // Trying to seek too far backwards.
+            }
+        }
+
+        // This will never underflow thanks to the clamps above.
+        memory->currentWritePos += offset;
+    } else {
+        if ((drwav_uint32)offset <= memory->dataSize) {
+            memory->currentWritePos = offset;
+        } else {
+            memory->currentWritePos = memory->dataSize;  // Trying to seek too far forward.
+        }
+    }
+
+    return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
+{
+    if (data == NULL || dataSize == 0) {
+        return DRWAV_FALSE;
+    }
+
+    drwav__memory_stream memoryStream;
+    drwav_zero_memory(&memoryStream, sizeof(memoryStream));
+    memoryStream.data = (const unsigned char*)data;
+    memoryStream.dataSize = dataSize;
+    memoryStream.currentReadPos = 0;
+
+    if (!drwav_init(pWav, drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream)) {
+        return DRWAV_FALSE;
+    }
+
+    pWav->memoryStream = memoryStream;
+    pWav->pUserData = &pWav->memoryStream;
+    return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
+{
+    if (ppData == NULL) {
+        return DRWAV_FALSE;
+    }
+
+    *ppData = NULL; // Important because we're using realloc()!
+    *pDataSize = 0;
+
+    drwav__memory_stream_write memoryStreamWrite;
+    drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
+    memoryStreamWrite.ppData = ppData;
+    memoryStreamWrite.pDataSize = pDataSize;
+    memoryStreamWrite.dataSize = 0;
+    memoryStreamWrite.dataCapacity = 0;
+    memoryStreamWrite.currentWritePos = 0;
+
+    if (!drwav_init_write(pWav, pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) {
+        return DRWAV_FALSE;
+    }
+
+    pWav->memoryStreamWrite = memoryStreamWrite;
+    pWav->pUserData = &pWav->memoryStreamWrite;
+    return DRWAV_TRUE;
+}
+
+drwav* drwav_open_memory(const void* data, size_t dataSize)
+{
+    if (data == NULL || dataSize == 0) {
+        return NULL;
+    }
+
+    drwav__memory_stream memoryStream;
+    drwav_zero_memory(&memoryStream, sizeof(memoryStream));
+    memoryStream.data = (const unsigned char*)data;
+    memoryStream.dataSize = dataSize;
+    memoryStream.currentReadPos = 0;
+
+    drwav* pWav = drwav_open(drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream);
+    if (pWav == NULL) {
+        return NULL;
+    }
+
+    pWav->memoryStream = memoryStream;
+    pWav->pUserData = &pWav->memoryStream;
+    return pWav;
+}
+
+drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
+{
+    if (ppData == NULL) {
+        return NULL;
+    }
+
+    *ppData = NULL; // Important because we're using realloc()!
+    *pDataSize = 0;
+
+    drwav__memory_stream_write memoryStreamWrite;
+    drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
+    memoryStreamWrite.ppData = ppData;
+    memoryStreamWrite.pDataSize = pDataSize;
+    memoryStreamWrite.dataSize = 0;
+    memoryStreamWrite.dataCapacity = 0;
+    memoryStreamWrite.currentWritePos = 0;
+
+    drwav* pWav = drwav_open_write(pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite);
+    if (pWav == NULL) {
+        return NULL;
+    }
+
+    pWav->memoryStreamWrite = memoryStreamWrite;
+    pWav->pUserData = &pWav->memoryStreamWrite;
+    return pWav;
+}
+
+
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
+{
+    int i;
+    if (onRead == NULL || onSeek == NULL) {
+        return DRWAV_FALSE;
+    }
+
+    drwav_zero_memory(pWav, sizeof(*pWav));
+
+
+    // The first 4 bytes should be the RIFF identifier.
+    unsigned char riff[4];
+    if (onRead(pUserData, riff, sizeof(riff)) != sizeof(riff)) {
+        return DRWAV_FALSE;    // Failed to read data.
+    }
+
+    // The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
+    // w64 it will start with "riff".
+    if (drwav__fourcc_equal(riff, "RIFF")) {
+        pWav->container = drwav_container_riff;
+    } else if (drwav__fourcc_equal(riff, "riff")) {
+        pWav->container = drwav_container_w64;
+
+        // Check the rest of the GUID for validity.
+        drwav_uint8 riff2[12];
+        if (onRead(pUserData, riff2, sizeof(riff2)) != sizeof(riff2)) {
+            return DRWAV_FALSE;
+        }
+
+        for (i = 0; i < 12; ++i) {
+            if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
+                return DRWAV_FALSE;
+            }
+        }
+    } else {
+        return DRWAV_FALSE;   // Unknown or unsupported container.
+    }
+
+
+    if (pWav->container == drwav_container_riff) {
+        // RIFF/WAVE
+        unsigned char chunkSizeBytes[4];
+        if (onRead(pUserData, chunkSizeBytes, sizeof(chunkSizeBytes)) != sizeof(chunkSizeBytes)) {
+            return DRWAV_FALSE;
+        }
+
+        unsigned int chunkSize = drwav__bytes_to_u32(chunkSizeBytes);
+        if (chunkSize < 36) {
+            return DRWAV_FALSE;    // Chunk size should always be at least 36 bytes.
+        }
+
+        unsigned char wave[4];
+        if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) {
+            return DRWAV_FALSE;
+        }
+
+        if (!drwav__fourcc_equal(wave, "WAVE")) {
+            return DRWAV_FALSE;    // Expecting "WAVE".
+        }
+
+        pWav->dataChunkDataPos = 4 + sizeof(chunkSizeBytes) + sizeof(wave);
+    } else {
+        // W64
+        unsigned char chunkSize[8];
+        if (onRead(pUserData, chunkSize, sizeof(chunkSize)) != sizeof(chunkSize)) {
+            return DRWAV_FALSE;
+        }
+
+        if (drwav__bytes_to_u64(chunkSize) < 80) {
+            return DRWAV_FALSE;
+        }
+
+        drwav_uint8 wave[16];
+        if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) {
+            return DRWAV_FALSE;
+        }
+
+        if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) {
+            return DRWAV_FALSE;
+        }
+
+        pWav->dataChunkDataPos = 16 + sizeof(chunkSize) + sizeof(wave);
+    }
+
+
+    // The next 24 bytes should be the "fmt " chunk.
+    drwav_fmt fmt;
+    if (!drwav__read_fmt(onRead, onSeek, pUserData, pWav->container, &pWav->dataChunkDataPos, &fmt)) {
+        return DRWAV_FALSE;    // Failed to read the "fmt " chunk.
+    }
+
+
+    // Translate the internal format.
+    unsigned short translatedFormatTag = fmt.formatTag;
+    if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+        translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0);
+    }
+
+
+    drwav_uint64 sampleCountFromFactChunk = 0;
+
+    // The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop.
+    drwav_uint64 dataSize;
+    for (;;)
+    {
+        drwav__chunk_header header;
+        if (!drwav__read_chunk_header(onRead, pUserData, pWav->container, &pWav->dataChunkDataPos, &header)) {
+            return DRWAV_FALSE;
+        }
+
+        dataSize = header.sizeInBytes;
+        if (pWav->container == drwav_container_riff) {
+            if (drwav__fourcc_equal(header.id.fourcc, "data")) {
+                break;
+            }
+        } else {
+            if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
+                break;
+            }
+        }
+
+        // Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats.
+        if (pWav->container == drwav_container_riff) {
+            if (drwav__fourcc_equal(header.id.fourcc, "fact")) {
+                drwav_uint32 sampleCount;
+                if (onRead(pUserData, &sampleCount, 4) != 4) {
+                    return DRWAV_FALSE;
+                }
+                pWav->dataChunkDataPos += 4;
+                dataSize -= 4;
+
+                // The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
+                // for Microsoft ADPCM formats.
+                if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+                    sampleCountFromFactChunk = sampleCount;
+                } else {
+                    sampleCountFromFactChunk = 0;
+                }
+            }
+        } else {
+            if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
+                if (onRead(pUserData, &sampleCountFromFactChunk, 8) != 8) {
+                    return DRWAV_FALSE;
+                }
+                pWav->dataChunkDataPos += 4;
+                dataSize -= 8;
+            }
+        }
+
+        // If we get here it means we didn't find the "data" chunk. Seek past it.
+
+        // Make sure we seek past the padding.
+        dataSize += header.paddingSize;
+        drwav__seek_forward(onSeek, dataSize, pUserData);
+        pWav->dataChunkDataPos += dataSize;
+    }
+
+    // At this point we should be sitting on the first byte of the raw audio data.
+
+    pWav->onRead              = onRead;
+    pWav->onSeek              = onSeek;
+    pWav->pUserData           = pUserData;
+    pWav->fmt                 = fmt;
+    pWav->sampleRate          = fmt.sampleRate;
+    pWav->channels            = fmt.channels;
+    pWav->bitsPerSample       = fmt.bitsPerSample;
+    pWav->bytesPerSample      = (unsigned int)(fmt.blockAlign / fmt.channels);
+    pWav->bytesRemaining      = dataSize;
+    pWav->translatedFormatTag = translatedFormatTag;
+    pWav->dataChunkDataSize   = dataSize;
+
+    if (sampleCountFromFactChunk != 0) {
+        pWav->totalSampleCount = sampleCountFromFactChunk * fmt.channels;
+    } else {
+        pWav->totalSampleCount = dataSize / pWav->bytesPerSample;
+
+        if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+            drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+            pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2;  // x2 because two samples per byte.
+        }
+        if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+            drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+            pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+        }
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+        pWav->bytesPerSample = 0;
+    }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+    // I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
+    // it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
+    // from the number of blocks, however this results in the inclusion of the extra silent samples at the end of the last block. The correct
+    // way to know the total sample count is to inspect the "fact" chunk which should always be present for compressed formats, and should
+    // always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
+    // correctness tests against libsndfile and is disabled by default.
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+        drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+        pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2;  // x2 because two samples per byte.
+    }
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+        drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+        pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+    }
+#endif
+
+    return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+    if (onWrite == NULL || onSeek == NULL) {
+        return DRWAV_FALSE;
+    }
+
+    // Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this.
+    if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
+        return DRWAV_FALSE;
+    }
+    if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
+        return DRWAV_FALSE;
+    }
+
+
+    drwav_zero_memory(pWav, sizeof(*pWav));
+    pWav->onWrite = onWrite;
+    pWav->onSeek = onSeek;
+    pWav->pUserData = pUserData;
+    pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
+    pWav->fmt.channels = (drwav_uint16)pFormat->channels;
+    pWav->fmt.sampleRate = pFormat->sampleRate;
+    pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) >> 3);
+    pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) >> 3);
+    pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+    pWav->fmt.extendedSize = 0;
+
+    size_t runningPos = 0;
+
+    // "RIFF" chunk.
+    drwav_uint64 chunkSizeRIFF = 0;
+    if (pFormat->container == drwav_container_riff) {
+        runningPos += pWav->onWrite(pUserData, "RIFF", 4);
+        runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4);
+        runningPos += pWav->onWrite(pUserData, "WAVE", 4);
+    } else {
+        runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16);
+        runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8);
+        runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16);
+    }
+
+    // "fmt " chunk.
+    drwav_uint64 chunkSizeFMT;
+    if (pFormat->container == drwav_container_riff) {
+        chunkSizeFMT = 16;
+        runningPos += pWav->onWrite(pUserData, "fmt ", 4);
+        runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 4);
+    } else {
+        chunkSizeFMT = 40;
+        runningPos += pWav->onWrite(pUserData, drwavGUID_W64_FMT, 16);
+        runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 8);
+    }
+
+    runningPos += pWav->onWrite(pUserData, &pWav->fmt.formatTag,      2);
+    runningPos += pWav->onWrite(pUserData, &pWav->fmt.channels,       2);
+    runningPos += pWav->onWrite(pUserData, &pWav->fmt.sampleRate,     4);
+    runningPos += pWav->onWrite(pUserData, &pWav->fmt.avgBytesPerSec, 4);
+    runningPos += pWav->onWrite(pUserData, &pWav->fmt.blockAlign,     2);
+    runningPos += pWav->onWrite(pUserData, &pWav->fmt.bitsPerSample,  2);
+
+    pWav->dataChunkDataPos = runningPos;
+    pWav->dataChunkDataSize = 0;
+
+    // "data" chunk.
+    drwav_uint64 chunkSizeDATA = 0;
+    if (pFormat->container == drwav_container_riff) {
+        runningPos += pWav->onWrite(pUserData, "data", 4);
+        runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4);
+    } else {
+        runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16);
+        runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8);
+    }
+
+
+    // Simple validation.
+    if (pFormat->container == drwav_container_riff) {
+        if (runningPos != 20 + chunkSizeFMT + 8) {
+            return DRWAV_FALSE;
+        }
+    } else {
+        if (runningPos != 40 + chunkSizeFMT + 24) {
+            return DRWAV_FALSE;
+        }
+    }
+
+
+
+    // Set some properties for the client's convenience.
+    pWav->container = pFormat->container;
+    pWav->channels = (drwav_uint16)pFormat->channels;
+    pWav->sampleRate = pFormat->sampleRate;
+    pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+    pWav->bytesPerSample = (drwav_uint16)(pFormat->bitsPerSample >> 3);
+    pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
+
+    return DRWAV_TRUE;
+}
+
+void drwav_uninit(drwav* pWav)
+{
+    if (pWav == NULL) {
+        return;
+    }
+
+    // If the drwav object was opened in write mode we'll need to finialize a few things:
+    //   - Make sure the "data" chunk is aligned to 16-bits
+    //   - Set the size of the "data" chunk.
+    if (pWav->onWrite != NULL) {
+        // Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding.
+        drwav_uint32 paddingSize = 0;
+        if (pWav->container == drwav_container_riff) {
+            paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 2);
+        } else {
+            paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 8);
+        }
+
+        if (paddingSize > 0) {
+            drwav_uint64 paddingData = 0;
+            pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
+        }
+
+
+        // Chunk sizes.
+        if (pWav->onSeek) {
+            if (pWav->container == drwav_container_riff) {
+                // The "RIFF" chunk size.
+                if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
+                    drwav_uint32 riffChunkSize = 36;
+                    if (pWav->dataChunkDataSize <= (0xFFFFFFFF - 36)) {
+                        riffChunkSize = 36 + (drwav_uint32)pWav->dataChunkDataSize;
+                    } else {
+                        riffChunkSize = 0xFFFFFFFF;
+                    }
+
+                    pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
+                }
+
+                // the "data" chunk size.
+                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
+                    drwav_uint32 dataChunkSize = 0;
+                    if (pWav->dataChunkDataSize <= 0xFFFFFFFF) {
+                        dataChunkSize = (drwav_uint32)pWav->dataChunkDataSize;
+                    } else {
+                        dataChunkSize = 0xFFFFFFFF;
+                    }
+
+                    pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
+                }
+            } else {
+                // The "RIFF" chunk size.
+                if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
+                    drwav_uint64 riffChunkSize = 80 + 24 + pWav->dataChunkDataSize;
+                    pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
+                }
+
+                // The "data" chunk size.
+                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
+                    drwav_uint64 dataChunkSize = 24 + pWav->dataChunkDataSize;  // +24 because W64 includes the size of the GUID and size fields.
+                    pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
+                }
+            }
+        }
+    }
+
+#ifndef DR_WAV_NO_STDIO
+    // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
+    // was used by looking at the onRead and onSeek callbacks.
+    if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
+        fclose((FILE*)pWav->pUserData);
+    }
+#endif
+}
+
+
+drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
+{
+    drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
+    if (pWav == NULL) {
+        return NULL;
+    }
+
+    if (!drwav_init(pWav, onRead, onSeek, pUserData)) {
+        DRWAV_FREE(pWav);
+        return NULL;
+    }
+
+    return pWav;
+}
+
+drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+    drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
+    if (pWav == NULL) {
+        return NULL;
+    }
+
+    if (!drwav_init_write(pWav, pFormat, onWrite, onSeek, pUserData)) {
+        DRWAV_FREE(pWav);
+        return NULL;
+    }
+
+    return pWav;
+}
+
+void drwav_close(drwav* pWav)
+{
+    drwav_uninit(pWav);
+    DRWAV_FREE(pWav);
+}
+
+
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
+{
+    if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) {
+        return 0;
+    }
+
+    if (bytesToRead > pWav->bytesRemaining) {
+        bytesToRead = (size_t)pWav->bytesRemaining;
+    }
+
+    size_t bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
+
+    pWav->bytesRemaining -= bytesRead;
+    return bytesRead;
+}
+
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut)
+{
+    if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+        return 0;
+    }
+
+    // Cannot use this function for compressed formats.
+    if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+        return 0;
+    }
+
+    // Don't try to read more samples than can potentially fit in the output buffer.
+    if (samplesToRead * pWav->bytesPerSample > SIZE_MAX) {
+        samplesToRead = SIZE_MAX / pWav->bytesPerSample;
+    }
+
+    size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * pWav->bytesPerSample), pBufferOut);
+    return bytesRead / pWav->bytesPerSample;
+}
+
+drwav_bool32 drwav_seek_to_first_sample(drwav* pWav)
+{
+    if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
+        return DRWAV_FALSE;
+    }
+
+    if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+        pWav->compressed.iCurrentSample = 0;
+    }
+
+    pWav->bytesRemaining = pWav->dataChunkDataSize;
+    return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
+{
+    // Seeking should be compatible with wave files > 2GB.
+
+    if (pWav == NULL || pWav->onSeek == NULL) {
+        return DRWAV_FALSE;
+    }
+
+    // If there are no samples, just return DRWAV_TRUE without doing anything.
+    if (pWav->totalSampleCount == 0) {
+        return DRWAV_TRUE;
+    }
+
+    // Make sure the sample is clamped.
+    if (sample >= pWav->totalSampleCount) {
+        sample  = pWav->totalSampleCount - 1;
+    }
+
+
+    // For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
+    // to seek back to the start.
+    if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+        // TODO: This can be optimized.
+        if (sample > pWav->compressed.iCurrentSample) {
+            // Seeking forward - just move from the current position.
+            drwav_uint64 offset = sample - pWav->compressed.iCurrentSample;
+
+            drwav_int16 devnull[2048];
+            while (offset > 0) {
+                drwav_uint64 samplesToRead = sample;
+                if (samplesToRead > 2048) {
+                    samplesToRead = 2048;
+                }
+
+                drwav_uint64 samplesRead = drwav_read_s16(pWav, samplesToRead, devnull);
+                if (samplesRead != samplesToRead) {
+                    return DRWAV_FALSE;
+                }
+
+                offset -= samplesRead;
+            }
+        } else {
+            // Seeking backwards. Just use the fallback.
+            goto fallback;
+        }
+    } else {
+        drwav_uint64 totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample;
+        drwav_assert(totalSizeInBytes >= pWav->bytesRemaining);
+
+        drwav_uint64 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
+        drwav_uint64 targetBytePos  = sample * pWav->bytesPerSample;
+
+        drwav_uint64 offset;
+        if (currentBytePos < targetBytePos) {
+            // Offset forwards.
+            offset = (targetBytePos - currentBytePos);
+        } else {
+            // Offset backwards.
+            if (!drwav_seek_to_first_sample(pWav)) {
+                return DRWAV_FALSE;
+            }
+            offset = targetBytePos;
+        }
+
+        while (offset > 0) {
+            int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
+            if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
+                return DRWAV_FALSE;
+            }
+
+            pWav->bytesRemaining -= offset32;
+            offset -= offset32;
+        }
+    }
+
+    return DRWAV_TRUE;
+
+fallback:
+    // This is a generic seek implementation that just continuously reads samples into a temporary buffer. This should work for all supported
+    // formats, but it is not efficient. This should be used as a fall back.
+    if (!drwav_seek_to_first_sample(pWav)) {
+        return DRWAV_FALSE;
+    }
+
+    drwav_int16 devnull[2048];
+    while (sample > 0) {
+        drwav_uint64 samplesToRead = sample;
+        if (samplesToRead > 2048) {
+            samplesToRead = 2048;
+        }
+
+        drwav_uint64 samplesRead = drwav_read_s16(pWav, samplesToRead, devnull);
+        if (samplesRead != samplesToRead) {
+            return DRWAV_FALSE;
+        }
+
+        sample -= samplesRead;
+    }
+
+    return DRWAV_TRUE;
+}
+
+
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
+{
+    if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
+        return 0;
+    }
+
+    size_t bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
+    pWav->dataChunkDataSize += bytesWritten;
+
+    return bytesWritten;
+}
+
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData)
+{
+    if (pWav == NULL || samplesToWrite == 0 || pData == NULL) {
+        return 0;
+    }
+
+    drwav_uint64 bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8);
+    if (bytesToWrite > SIZE_MAX) {
+        return 0;
+    }
+
+    size_t bytesWritten = drwav_write_raw(pWav, (size_t)bytesToWrite, pData);
+    return ((drwav_uint64)bytesWritten * 8) / pWav->bitsPerSample;
+}
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+static unsigned short g_drwavAlawTable[256] = {
+    0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
+    0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
+    0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
+    0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
+    0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
+    0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
+    0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
+    0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
+    0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
+    0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
+    0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
+    0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
+    0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
+    0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
+    0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
+    0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
+};
+
+static unsigned short g_drwavMulawTable[256] = {
+    0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
+    0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
+    0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
+    0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
+    0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
+    0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
+    0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
+    0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
+    0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
+    0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
+    0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
+    0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
+    0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
+    0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
+    0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
+    0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
+{
+    return (short)g_drwavAlawTable[sampleIn];
+}
+
+static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
+{
+    return (short)g_drwavMulawTable[sampleIn];
+}
+
+
+
+static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+    unsigned int i;
+    unsigned short j;
+    // Special case for 8-bit sample data because it's treated as unsigned.
+    if (bytesPerSample == 1) {
+        drwav_u8_to_s16(pOut, pIn, totalSampleCount);
+        return;
+    }
+
+
+    // Slightly more optimal implementation for common formats.
+    if (bytesPerSample == 2) {
+        for (i = 0; i < totalSampleCount; ++i) {
+           *pOut++ = ((drwav_int16*)pIn)[i];
+        }
+        return;
+    }
+    if (bytesPerSample == 3) {
+        drwav_s24_to_s16(pOut, pIn, totalSampleCount);
+        return;
+    }
+    if (bytesPerSample == 4) {
+        drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
+        return;
+    }
+
+
+    // Generic, slow converter.
+    for (i = 0; i < totalSampleCount; ++i) {
+        unsigned short sample = 0;
+        unsigned short shift  = (8 - bytesPerSample) * 8;
+        for (j = 0; j < bytesPerSample && j < 2; ++j) {
+            sample |= (unsigned short)(pIn[j]) << shift;
+            shift  += 8;
+        }
+
+        pIn += bytesPerSample;
+        *pOut++ = sample;
+    }
+}
+
+static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+    if (bytesPerSample == 4) {
+        drwav_f32_to_s16(pOut, (float*)pIn, totalSampleCount);
+        return;
+    } else {
+        drwav_f64_to_s16(pOut, (double*)pIn, totalSampleCount);
+        return;
+    }
+}
+
+drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    // Fast path.
+    if (pWav->bytesPerSample == 2) {
+        return drwav_read(pWav, samplesToRead, pBufferOut);
+    }
+
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    drwav_assert(pWav != NULL);
+    drwav_assert(samplesToRead > 0);
+    drwav_assert(pBufferOut != NULL);
+
+    // TODO: Lots of room for optimization here.
+
+    drwav_uint64 totalSamplesRead = 0;
+
+    while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+        // If there are no cached samples we need to load a new block.
+        if (pWav->msadpcm.cachedSampleCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
+            if (pWav->channels == 1) {
+                // Mono.
+                drwav_uint8 header[7];
+                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+                    return totalSamplesRead;
+                }
+                pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+                pWav->msadpcm.predictor[0] = header[0];
+                pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1);
+                pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3);
+                pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5);
+                pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][0];
+                pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[0][1];
+                pWav->msadpcm.cachedSampleCount = 2;
+            } else {
+                // Stereo.
+                drwav_uint8 header[14];
+                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+                    return totalSamplesRead;
+                }
+                pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+                pWav->msadpcm.predictor[0] = header[0];
+                pWav->msadpcm.predictor[1] = header[1];
+                pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2);
+                pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4);
+                pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6);
+                pWav->msadpcm.prevSamples[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8);
+                pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10);
+                pWav->msadpcm.prevSamples[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12);
+
+                pWav->msadpcm.cachedSamples[0] = pWav->msadpcm.prevSamples[0][0];
+                pWav->msadpcm.cachedSamples[1] = pWav->msadpcm.prevSamples[1][0];
+                pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][1];
+                pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[1][1];
+                pWav->msadpcm.cachedSampleCount = 4;
+            }
+        }
+
+        // Output anything that's cached.
+        while (samplesToRead > 0 && pWav->msadpcm.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+            pBufferOut[0] = (drwav_int16)pWav->msadpcm.cachedSamples[drwav_countof(pWav->msadpcm.cachedSamples) - pWav->msadpcm.cachedSampleCount];
+            pWav->msadpcm.cachedSampleCount -= 1;
+
+            pBufferOut += 1;
+            samplesToRead -= 1;
+            totalSamplesRead += 1;
+            pWav->compressed.iCurrentSample += 1;
+        }
+
+        if (samplesToRead == 0) {
+            return totalSamplesRead;
+        }
+
+
+        // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+        // loop iteration which will trigger the loading of a new block.
+        if (pWav->msadpcm.cachedSampleCount == 0) {
+            if (pWav->msadpcm.bytesRemainingInBlock == 0) {
+                continue;
+            } else {
+                drwav_uint8 nibbles;
+                if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
+                    return totalSamplesRead;
+                }
+                pWav->msadpcm.bytesRemainingInBlock -= 1;
+
+                // TODO: Optimize away these if statements.
+                drwav_int32 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
+                drwav_int32 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
+
+                static drwav_int32 adaptationTable[] = {
+                    230, 230, 230, 230, 307, 409, 512, 614,
+                    768, 614, 512, 409, 307, 230, 230, 230
+                };
+                static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };
+                static drwav_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };
+
+                if (pWav->channels == 1) {
+                    // Mono.
+                    drwav_int32 newSample0;
+                    newSample0  = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+                    newSample0 += nibble0 * pWav->msadpcm.delta[0];
+                    newSample0  = drwav_clamp(newSample0, -32768, 32767);
+
+                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+                    if (pWav->msadpcm.delta[0] < 16) {
+                        pWav->msadpcm.delta[0] = 16;
+                    }
+
+                    pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+                    pWav->msadpcm.prevSamples[0][1] = newSample0;
+
+
+                    drwav_int32 newSample1;
+                    newSample1  = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+                    newSample1 += nibble1 * pWav->msadpcm.delta[0];
+                    newSample1  = drwav_clamp(newSample1, -32768, 32767);
+
+                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
+                    if (pWav->msadpcm.delta[0] < 16) {
+                        pWav->msadpcm.delta[0] = 16;
+                    }
+
+                    pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+                    pWav->msadpcm.prevSamples[0][1] = newSample1;
+
+
+                    pWav->msadpcm.cachedSamples[2] = newSample0;
+                    pWav->msadpcm.cachedSamples[3] = newSample1;
+                    pWav->msadpcm.cachedSampleCount = 2;
+                } else {
+                    // Stereo.
+
+                    // Left.
+                    drwav_int32 newSample0;
+                    newSample0  = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+                    newSample0 += nibble0 * pWav->msadpcm.delta[0];
+                    newSample0  = drwav_clamp(newSample0, -32768, 32767);
+
+                    pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+                    if (pWav->msadpcm.delta[0] < 16) {
+                        pWav->msadpcm.delta[0] = 16;
+                    }
+
+                    pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+                    pWav->msadpcm.prevSamples[0][1] = newSample0;
+
+
+                    // Right.
+                    drwav_int32 newSample1;
+                    newSample1  = ((pWav->msadpcm.prevSamples[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevSamples[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
+                    newSample1 += nibble1 * pWav->msadpcm.delta[1];
+                    newSample1  = drwav_clamp(newSample1, -32768, 32767);
+
+                    pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
+                    if (pWav->msadpcm.delta[1] < 16) {
+                        pWav->msadpcm.delta[1] = 16;
+                    }
+
+                    pWav->msadpcm.prevSamples[1][0] = pWav->msadpcm.prevSamples[1][1];
+                    pWav->msadpcm.prevSamples[1][1] = newSample1;
+
+                    pWav->msadpcm.cachedSamples[2] = newSample0;
+                    pWav->msadpcm.cachedSamples[3] = newSample1;
+                    pWav->msadpcm.cachedSampleCount = 2;
+                }
+            }
+        }
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    drwav_uint32 iChannel;
+    drwav_uint32 iByte;
+    drwav_assert(pWav != NULL);
+    drwav_assert(samplesToRead > 0);
+    drwav_assert(pBufferOut != NULL);
+
+    // TODO: Lots of room for optimization here.
+
+    drwav_uint64 totalSamplesRead = 0;
+
+    while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+        // If there are no cached samples we need to load a new block.
+        if (pWav->ima.cachedSampleCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
+            if (pWav->channels == 1) {
+                // Mono.
+                drwav_uint8 header[4];
+                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+                    return totalSamplesRead;
+                }
+                pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+                pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+                pWav->ima.stepIndex[0] = header[2];
+                pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[0];
+                pWav->ima.cachedSampleCount = 1;
+            } else {
+                // Stereo.
+                drwav_uint8 header[8];
+                if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+                    return totalSamplesRead;
+                }
+                pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+                pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+                pWav->ima.stepIndex[0] = header[2];
+                pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4);
+                pWav->ima.stepIndex[1] = header[6];
+
+                pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 2] = pWav->ima.predictor[0];
+                pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[1];
+                pWav->ima.cachedSampleCount = 2;
+            }
+        }
+
+        // Output anything that's cached.
+        while (samplesToRead > 0 && pWav->ima.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+            pBufferOut[0] = (drwav_int16)pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount];
+            pWav->ima.cachedSampleCount -= 1;
+
+            pBufferOut += 1;
+            samplesToRead -= 1;
+            totalSamplesRead += 1;
+            pWav->compressed.iCurrentSample += 1;
+        }
+
+        if (samplesToRead == 0) {
+            return totalSamplesRead;
+        }
+
+        // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+        // loop iteration which will trigger the loading of a new block.
+        if (pWav->ima.cachedSampleCount == 0) {
+            if (pWav->ima.bytesRemainingInBlock == 0) {
+                continue;
+            } else {
+                static drwav_int32 indexTable[16] = {
+                    -1, -1, -1, -1, 2, 4, 6, 8,
+                    -1, -1, -1, -1, 2, 4, 6, 8
+                };
+
+                static drwav_int32 stepTable[89] = {
+                    7,     8,     9,     10,    11,    12,    13,    14,    16,    17,
+                    19,    21,    23,    25,    28,    31,    34,    37,    41,    45,
+                    50,    55,    60,    66,    73,    80,    88,    97,    107,   118,
+                    130,   143,   157,   173,   190,   209,   230,   253,   279,   307,
+                    337,   371,   408,   449,   494,   544,   598,   658,   724,   796,
+                    876,   963,   1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066,
+                    2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358,
+                    5894,  6484,  7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899,
+                    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+                };
+
+                // From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
+                // left channel, 4 bytes for the right channel.
+                pWav->ima.cachedSampleCount = 8 * pWav->channels;
+                for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
+                    drwav_uint8 nibbles[4];
+                    if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
+                        return totalSamplesRead;
+                    }
+                    pWav->ima.bytesRemainingInBlock -= 4;
+
+                    for (iByte = 0; iByte < 4; ++iByte) {
+                        drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
+                        drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
+
+                        drwav_int32 step      = stepTable[pWav->ima.stepIndex[iChannel]];
+                        drwav_int32 predictor = pWav->ima.predictor[iChannel];
+
+                        drwav_int32      diff  = step >> 3;
+                        if (nibble0 & 1) diff += step >> 2;
+                        if (nibble0 & 2) diff += step >> 1;
+                        if (nibble0 & 4) diff += step;
+                        if (nibble0 & 8) diff  = -diff;
+
+                        predictor = drwav_clamp(predictor + diff, -32768, 32767);
+                        pWav->ima.predictor[iChannel] = predictor;
+                        pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
+                        pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
+
+
+                        step      = stepTable[pWav->ima.stepIndex[iChannel]];
+                        predictor = pWav->ima.predictor[iChannel];
+
+                                         diff  = step >> 3;
+                        if (nibble1 & 1) diff += step >> 2;
+                        if (nibble1 & 2) diff += step >> 1;
+                        if (nibble1 & 4) diff += step;
+                        if (nibble1 & 8) diff  = -diff;
+
+                        predictor = drwav_clamp(predictor + diff, -32768, 32767);
+                        pWav->ima.predictor[iChannel] = predictor;
+                        pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
+                        pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
+                    }
+                }
+            }
+        }
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+    if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+        return 0;
+    }
+
+    // Don't try to read more samples than can potentially fit in the output buffer.
+    if (samplesToRead * sizeof(drwav_int16) > SIZE_MAX) {
+        samplesToRead = SIZE_MAX / sizeof(drwav_int16);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+        return drwav_read_s16__pcm(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+        return drwav_read_s16__msadpcm(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+        return drwav_read_s16__ieee(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+        return drwav_read_s16__alaw(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+        return drwav_read_s16__mulaw(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+        return drwav_read_s16__ima(pWav, samplesToRead, pBufferOut);
+    }
+
+    return 0;
+}
+
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    int r;
+    size_t i;
+    for (i = 0; i < sampleCount; ++i) {
+        int x = pIn[i];
+        r = x - 128;
+        r = r << 8;
+        pOut[i] = (short)r;
+    }
+}
+
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    int r;
+    size_t i;
+    for (i = 0; i < sampleCount; ++i) {
+        int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8;
+        r = x >> 8;
+        pOut[i] = (short)r;
+    }
+}
+
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+    int r;
+    size_t i;
+    for (i = 0; i < sampleCount; ++i) {
+        int x = pIn[i];
+        r = x >> 16;
+        pOut[i] = (short)r;
+    }
+}
+
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
+{
+/*
+    int r;
+    for (size_t i = 0; i < sampleCount; ++i) {
+        float x = pIn[i];
+        float c;
+        int s;
+        c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+        s = ((*((int*)&x)) & 0x80000000) >> 31;
+        s = s + 32767;
+        r = (int)(c * s);
+        pOut[i] = (short)r;
+    }
+*/
+    fprintf(stderr, "Warning: drwav_f32_to_s16 has been deactivated!\n");
+}
+
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
+{
+/*
+    int r;
+    for (size_t i = 0; i < sampleCount; ++i) {
+        double x = pIn[i];
+        double c;
+        int s;
+        c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+        s = (int)(((*((drwav_uint64*)&x)) & (drwav_uint64)0x8000000000000000) >> 63);
+        s = s + 32767;
+        r = (int)(c * s);
+        pOut[i] = (short)r;
+    }
+*/
+    fprintf(stderr, "Warning: drwav_f64_to_s16 has been deactivated!\n");
+}
+
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    for (i = 0; i < sampleCount; ++i) {
+        pOut[i] = drwav__alaw_to_s16(pIn[i]);
+    }
+}
+
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    for (i = 0; i < sampleCount; ++i) {
+        pOut[i] = drwav__mulaw_to_s16(pIn[i]);
+    }
+}
+
+
+
+static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+{
+    unsigned int i;
+    unsigned short j;
+    // Special case for 8-bit sample data because it's treated as unsigned.
+    if (bytesPerSample == 1) {
+        drwav_u8_to_f32(pOut, pIn, sampleCount);
+        return;
+    }
+
+    // Slightly more optimal implementation for common formats.
+    if (bytesPerSample == 2) {
+        drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
+        return;
+    }
+    if (bytesPerSample == 3) {
+        drwav_s24_to_f32(pOut, pIn, sampleCount);
+        return;
+    }
+    if (bytesPerSample == 4) {
+        drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
+        return;
+    }
+
+    // Generic, slow converter.
+    for (i = 0; i < sampleCount; ++i) {
+        unsigned int sample = 0;
+        unsigned int shift  = (8 - bytesPerSample) * 8;
+        for (j = 0; j < bytesPerSample && j < 4; ++j) {
+            sample |= (unsigned int)(pIn[j]) << shift;
+            shift  += 8;
+        }
+
+        pIn += bytesPerSample;
+        *pOut++ = (float)((int)sample / 2147483648.0);
+    }
+}
+
+static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+{
+    unsigned int i;
+    if (bytesPerSample == 4) {
+        for (i = 0; i < sampleCount; ++i) {
+            *pOut++ = ((float*)pIn)[i];
+        }
+        return;
+    } else {
+        drwav_f64_to_f32(pOut, (double*)pIn, sampleCount);
+        return;
+    }
+}
+
+
+drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+        pBufferOut += samplesRead;
+
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+    // want to duplicate that code.
+    drwav_uint64 totalSamplesRead = 0;
+    drwav_int16 samples16[2048];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead);   // <-- Safe cast because we're clamping to 2048.
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+    // want to duplicate that code.
+    drwav_uint64 totalSamplesRead = 0;
+    drwav_int16 samples16[2048];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead);   // <-- Safe cast because we're clamping to 2048.
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    // Fast path.
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bytesPerSample == 4) {
+        return drwav_read(pWav, samplesToRead, pBufferOut);
+    }
+
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+    if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+        return 0;
+    }
+
+    // Don't try to read more samples than can potentially fit in the output buffer.
+    if (samplesToRead * sizeof(float) > SIZE_MAX) {
+        samplesToRead = SIZE_MAX / sizeof(float);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+        return drwav_read_f32__pcm(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+        return drwav_read_f32__msadpcm(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+        return drwav_read_f32__ieee(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+        return drwav_read_f32__alaw(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+        return drwav_read_f32__mulaw(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+        return drwav_read_f32__ima(pWav, samplesToRead, pBufferOut);
+    }
+
+    return 0;
+}
+
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+    // It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
+    // libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
+    // the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
+    // correctness testing. This is disabled by default.
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
+    }
+#else
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = (pIn[i] / 255.0f) * 2 - 1;
+    }
+#endif
+}
+
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = pIn[i] / 32768.0f;
+    }
+}
+
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        unsigned int s0 = pIn[i*3 + 0];
+        unsigned int s1 = pIn[i*3 + 1];
+        unsigned int s2 = pIn[i*3 + 2];
+
+        int sample32 = (int)((s0 << 8) | (s1 << 16) | (s2 << 24));
+        *pOut++ = (float)(sample32 / 2147483648.0);
+    }
+}
+
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = (float)(pIn[i] / 2147483648.0);
+    }
+}
+
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = (float)pIn[i];
+    }
+}
+
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
+    }
+}
+
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
+    }
+}
+
+
+
+static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+    unsigned int i;
+    unsigned short j;
+    // Special case for 8-bit sample data because it's treated as unsigned.
+    if (bytesPerSample == 1) {
+        drwav_u8_to_s32(pOut, pIn, totalSampleCount);
+        return;
+    }
+
+    // Slightly more optimal implementation for common formats.
+    if (bytesPerSample == 2) {
+        drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
+        return;
+    }
+    if (bytesPerSample == 3) {
+        drwav_s24_to_s32(pOut, pIn, totalSampleCount);
+        return;
+    }
+    if (bytesPerSample == 4) {
+        for (i = 0; i < totalSampleCount; ++i) {
+           *pOut++ = ((drwav_int32*)pIn)[i];
+        }
+        return;
+    }
+
+    // Generic, slow converter.
+    for (i = 0; i < totalSampleCount; ++i) {
+        unsigned int sample = 0;
+        unsigned int shift  = (8 - bytesPerSample) * 8;
+        for (j = 0; j < bytesPerSample && j < 4; ++j) {
+            sample |= (unsigned int)(pIn[j]) << shift;
+            shift  += 8;
+        }
+
+        pIn += bytesPerSample;
+        *pOut++ = sample;
+    }
+}
+
+static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+    if (bytesPerSample == 4) {
+        drwav_f32_to_s32(pOut, (float*)pIn, totalSampleCount);
+        return;
+    } else {
+        drwav_f64_to_s32(pOut, (double*)pIn, totalSampleCount);
+        return;
+    }
+}
+
+
+drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    // Fast path.
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bytesPerSample == 4) {
+        return drwav_read(pWav, samplesToRead, pBufferOut);
+    }
+
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+    // want to duplicate that code.
+    drwav_uint64 totalSamplesRead = 0;
+    drwav_int16 samples16[2048];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead);   // <-- Safe cast because we're clamping to 2048.
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+    // want to duplicate that code.
+    drwav_uint64 totalSamplesRead = 0;
+    drwav_int16 samples16[2048];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead);   // <-- Safe cast because we're clamping to 2048.
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    drwav_uint64 totalSamplesRead = 0;
+    unsigned char sampleData[4096];
+    while (samplesToRead > 0) {
+        drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+        if (samplesRead == 0) {
+            break;
+        }
+
+        drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
+
+        pBufferOut       += samplesRead;
+        samplesToRead    -= samplesRead;
+        totalSamplesRead += samplesRead;
+    }
+
+    return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+    if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+        return 0;
+    }
+
+    // Don't try to read more samples than can potentially fit in the output buffer.
+    if (samplesToRead * sizeof(drwav_int32) > SIZE_MAX) {
+        samplesToRead = SIZE_MAX / sizeof(drwav_int32);
+    }
+
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+        return drwav_read_s32__pcm(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+        return drwav_read_s32__msadpcm(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+        return drwav_read_s32__ieee(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+        return drwav_read_s32__alaw(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+        return drwav_read_s32__mulaw(pWav, samplesToRead, pBufferOut);
+    }
+
+    if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+        return drwav_read_s32__ima(pWav, samplesToRead, pBufferOut);
+    }
+
+    return 0;
+}
+
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = ((int)pIn[i] - 128) << 24;
+    }
+}
+
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = pIn[i] << 16;
+    }
+}
+
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        unsigned int s0 = pIn[i*3 + 0];
+        unsigned int s1 = pIn[i*3 + 1];
+        unsigned int s2 = pIn[i*3 + 2];
+
+        drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
+        *pOut++ = sample32;
+    }
+}
+
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+    }
+}
+
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+    }
+}
+
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i = 0; i < sampleCount; ++i) {
+        *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
+    }
+}
+
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+    size_t i;
+    if (pOut == NULL || pIn == NULL) {
+        return;
+    }
+
+    for (i= 0; i < sampleCount; ++i) {
+        *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
+    }
+}
+
+
+
+drwav_int16* drwav__read_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    drwav_assert(pWav != NULL);
+
+    drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int16);
+    if (sampleDataSize > SIZE_MAX) {
+        drwav_uninit(pWav);
+        return NULL;    // File's too big.
+    }
+
+    drwav_int16* pSampleData = (drwav_int16*)DRWAV_MALLOC((size_t)sampleDataSize);    // <-- Safe cast due to the check above.
+    if (pSampleData == NULL) {
+        drwav_uninit(pWav);
+        return NULL;    // Failed to allocate memory.
+    }
+
+    drwav_uint64 samplesRead = drwav_read_s16(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+    if (samplesRead != pWav->totalSampleCount) {
+        DRWAV_FREE(pSampleData);
+        drwav_uninit(pWav);
+        return NULL;    // There was an error reading the samples.
+    }
+
+    drwav_uninit(pWav);
+
+    if (sampleRate) *sampleRate = pWav->sampleRate;
+    if (channels) *channels = pWav->channels;
+    if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+    return pSampleData;
+}
+
+float* drwav__read_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    drwav_assert(pWav != NULL);
+
+    drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(float);
+    if (sampleDataSize > SIZE_MAX) {
+        drwav_uninit(pWav);
+        return NULL;    // File's too big.
+    }
+
+    float* pSampleData = (float*)DRWAV_MALLOC((size_t)sampleDataSize);    // <-- Safe cast due to the check above.
+    if (pSampleData == NULL) {
+        drwav_uninit(pWav);
+        return NULL;    // Failed to allocate memory.
+    }
+
+    drwav_uint64 samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+    if (samplesRead != pWav->totalSampleCount) {
+        DRWAV_FREE(pSampleData);
+        drwav_uninit(pWav);
+        return NULL;    // There was an error reading the samples.
+    }
+
+    drwav_uninit(pWav);
+
+    if (sampleRate) *sampleRate = pWav->sampleRate;
+    if (channels) *channels = pWav->channels;
+    if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+    return pSampleData;
+}
+
+drwav_int32* drwav__read_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    drwav_assert(pWav != NULL);
+
+    drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int32);
+    if (sampleDataSize > SIZE_MAX) {
+        drwav_uninit(pWav);
+        return NULL;    // File's too big.
+    }
+
+    drwav_int32* pSampleData = (drwav_int32*)DRWAV_MALLOC((size_t)sampleDataSize);    // <-- Safe cast due to the check above.
+    if (pSampleData == NULL) {
+        drwav_uninit(pWav);
+        return NULL;    // Failed to allocate memory.
+    }
+
+    drwav_uint64 samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+    if (samplesRead != pWav->totalSampleCount) {
+        DRWAV_FREE(pSampleData);
+        drwav_uninit(pWav);
+        return NULL;    // There was an error reading the samples.
+    }
+
+    drwav_uninit(pWav);
+
+    if (sampleRate) *sampleRate = pWav->sampleRate;
+    if (channels) *channels = pWav->channels;
+    if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+    return pSampleData;
+}
+
+
+drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+#ifndef DR_WAV_NO_STDIO
+drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init_file(&wav, filename)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init_file(&wav, filename)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init_file(&wav, filename)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+#endif
+
+drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init_memory(&wav, data, dataSize)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init_memory(&wav, data, dataSize)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+    if (sampleRate) *sampleRate = 0;
+    if (channels) *channels = 0;
+    if (totalSampleCount) *totalSampleCount = 0;
+
+    drwav wav;
+    if (!drwav_init_memory(&wav, data, dataSize)) {
+        return NULL;
+    }
+
+    return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+#endif  //DR_WAV_NO_CONVERSION_API
+
+
+void drwav_free(void* pDataReturnedByOpenAndRead)
+{
+    DRWAV_FREE(pDataReturnedByOpenAndRead);
+}
+
+#endif  //DR_WAV_IMPLEMENTATION
+
+
+// REVISION HISTORY
+//
+// v0.7 - 2017-11-04
+//   - Add writing APIs.
+//
+// v0.6 - 2017-08-16
+//   - API CHANGE: Rename dr_* types to drwav_*.
+//   - Add support for custom implementations of malloc(), realloc(), etc.
+//   - Add support for Microsoft ADPCM.
+//   - Add support for IMA ADPCM (DVI, format code 0x11).
+//   - Optimizations to drwav_read_s16().
+//   - Bug fixes.
+//
+// v0.5g - 2017-07-16
+//   - Change underlying type for booleans to unsigned.
+//
+// v0.5f - 2017-04-04
+//   - Fix a minor bug with drwav_open_and_read_s16() and family.
+//
+// v0.5e - 2016-12-29
+//   - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
+//   - Minor fixes to documentation.
+//
+// v0.5d - 2016-12-28
+//   - Use drwav_int*/drwav_uint* sized types to improve compiler support.
+//
+// v0.5c - 2016-11-11
+//   - Properly handle JUNK chunks that come before the FMT chunk.
+//
+// v0.5b - 2016-10-23
+//   - A minor change to drwav_bool8 and drwav_bool32 types.
+//
+// v0.5a - 2016-10-11
+//   - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
+//   - Improve A-law and mu-law efficiency.
+//
+// v0.5 - 2016-09-29
+//   - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
+//     keep it consistent with dr_audio and drwav_flac.
+//
+// v0.4b - 2016-09-18
+//   - Fixed a typo in documentation.
+//
+// v0.4a - 2016-09-18
+//   - Fixed a typo.
+//   - Change date format to ISO 8601 (YYYY-MM-DD)
+//
+// v0.4 - 2016-07-13
+//   - API CHANGE. Make onSeek consistent with drwav_flac.
+//   - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with drwav_flac.
+//   - Added support for Sony Wave64.
+//
+// v0.3a - 2016-05-28
+//   - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
+//   - Fixed a memory leak.
+//
+// v0.3 - 2016-05-22
+//   - Lots of API changes for consistency.
+//
+// v0.2a - 2016-05-16
+//   - Fixed Linux/GCC build.
+//
+// v0.2 - 2016-05-11
+//   - Added support for reading data as signed 32-bit PCM for consistency with drwav_flac.
+//
+// v0.1a - 2016-05-07
+//   - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
+//
+// v0.1 - 2016-05-04
+//   - Initial versioned release.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
--- /dev/null
+++ b/lib/dr_wav/sp_dr_wav.h
@@ -1,0 +1,17 @@
+#ifndef SP_DRWAV_H
+#define SP_DRWAV_H
+#ifndef dr_wav_h
+typedef struct drwav drwav;
+#endif
+size_t sp_drwav_size(void);
+int sp_drwav_init_file(drwav* pWav, const char* filename);
+size_t sp_drwav_read_f32(drwav* pWav,
+                         size_t samplesToRead,
+                         float* pBufferOut);
+size_t sp_drwav_sampcount(drwav *wav);
+void sp_drwav_uninit(drwav *wav);
+int sp_drwav_seek_to_sample(drwav* pWav, size_t sample);
+drwav * sp_drwav_open_mono_write(const char *filename, int sr);
+size_t sp_drwav_write(drwav* pWav, size_t samplesToWrite, const void* pData);
+void sp_drwav_close(drwav* pWav);
+#endif
--- /dev/null
+++ b/lib/faust/CUI.h
@@ -1,0 +1,16 @@
+#include <stdlib.h>
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT SPFLOAT
+#endif
+
+typedef void (* addHorizontalSliderFun) (void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step);
+typedef void (* addVerticalSliderFun) (void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step);
+typedef void (* addCheckButtonFun) (void* ui_interface, const char* label, FAUSTFLOAT* zone);
+
+typedef struct {
+    void* uiInterface;
+    addHorizontalSliderFun addHorizontalSlider;
+    addVerticalSliderFun addVerticalSlider;
+    addCheckButtonFun addCheckButton;
+} UIGlue;
--- /dev/null
+++ b/lib/fft/Makefile
@@ -1,0 +1,9 @@
+LPATHS += lib/fft/fft.o
+UTIL += lib/fft/fft.c
+
+lib/fft/fft.c: lib/fft/fftlib.c lib/fft/sp_fft.c
+	echo "/* THIS IS A GENERATED FILE. DO NOT EDIT BY HAND */" > $@
+	cat $^ >> $@
+
+lib/fft/fft.o: lib/fft/fft.c
+	$(C89) $< -c $(CFLAGS) -o $@
--- /dev/null
+++ b/lib/fft/fftlib.c
@@ -1,0 +1,3080 @@
+/* Public domain FFT implementation in C by John Green */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+#include "base.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define POW2(m) ((uint32_t) 1 << (m))       /* integer power of 2 for m<32 */
+
+/* fft's with M bigger than this bust primary cache */
+#define MCACHE  (11 - (sizeof(SPFLOAT) / 8))
+
+/* some math constants to 40 decimal places */
+#define MYPI      3.141592653589793238462643383279502884197   /* pi         */
+#define MYROOT2   1.414213562373095048801688724209698078569   /* sqrt(2)    */
+#define MYCOSPID8 0.9238795325112867561281831893967882868224  /* cos(pi/8)  */
+#define MYSINPID8 0.3826834323650897717284599840303988667613  /* sin(pi/8)  */
+
+/*****************************************************
+* routines to initialize tables used by fft routines *
+*****************************************************/
+
+static void fftCosInit(int M, SPFLOAT *Utbl)
+{
+    /* Compute Utbl, the cosine table for ffts  */
+    /* of size (pow(2,M)/4 +1)                  */
+    /* INPUTS                                   */
+    /*   M = log2 of fft size                   */
+    /* OUTPUTS                                  */
+    /*   *Utbl = cosine table                   */
+    unsigned int fftN = POW2(M);
+    unsigned int i1;
+
+    Utbl[0] = 1.0;
+    for (i1 = 1; i1 < fftN/4; i1++)
+      Utbl[i1] = cos((2.0 * M_PI * (SPFLOAT)i1) / (SPFLOAT)fftN);
+    Utbl[fftN/4] = 0.0;
+}
+
+void fftBRInit(int M, int16_t *BRLow)
+{
+    /* Compute BRLow, the bit reversed table for ffts */
+    /* of size pow(2,M/2 -1)                          */
+    /* INPUTS                                         */
+    /*   M = log2 of fft size                         */
+    /* OUTPUTS                                        */
+    /*   *BRLow = bit reversed counter table          */
+    int Mroot_1 = M / 2 - 1;
+    int Nroot_1 = POW2(Mroot_1);
+    int i1;
+    int bitsum;
+    int bitmask;
+    int bit;
+
+    for (i1 = 0; i1 < Nroot_1; i1++) {
+      bitsum = 0;
+      bitmask = 1;
+      for (bit = 1; bit <= Mroot_1; bitmask <<= 1, bit++)
+        if (i1 & bitmask)
+          bitsum = bitsum + (Nroot_1 >> bit);
+      BRLow[i1] = bitsum;
+    }
+}
+
+/*****************
+* parts of ffts1 *
+*****************/
+
+static void bitrevR2(SPFLOAT *ioptr, int M, int16_t *BRLow)
+{
+    /*** bit reverse and first radix 2 stage of forward or inverse fft ***/
+    SPFLOAT f0r;
+    SPFLOAT f0i;
+    SPFLOAT f1r;
+    SPFLOAT f1i;
+    SPFLOAT f2r;
+    SPFLOAT f2i;
+    SPFLOAT f3r;
+    SPFLOAT f3i;
+    SPFLOAT f4r;
+    SPFLOAT f4i;
+    SPFLOAT f5r;
+    SPFLOAT f5i;
+    SPFLOAT f6r;
+    SPFLOAT f6i;
+    SPFLOAT f7r;
+    SPFLOAT f7i;
+    SPFLOAT t0r;
+    SPFLOAT t0i;
+    SPFLOAT t1r;
+    SPFLOAT t1i;
+    SPFLOAT *p0r;
+    SPFLOAT *p1r;
+    SPFLOAT *IOP;
+    SPFLOAT *iolimit;
+    int Colstart;
+    int iCol;
+    unsigned int posA;
+    unsigned int posAi;
+    unsigned int posB;
+    unsigned int posBi;
+
+    const unsigned int Nrems2 = POW2((M + 3) / 2);
+    const unsigned int Nroot_1_ColInc = POW2(M) - Nrems2;
+    const unsigned int Nroot_1 = POW2(M / 2 - 1) - 1;
+    const unsigned int ColstartShift = (M + 1) / 2 + 1;
+
+    posA = POW2(M);               /* 1/2 of POW2(M) complexes */
+    posAi = posA + 1;
+    posB = posA + 2;
+    posBi = posB + 1;
+
+    iolimit = ioptr + Nrems2;
+    for (; ioptr < iolimit; ioptr += POW2(M / 2 + 1)) {
+      for (Colstart = Nroot_1; Colstart >= 0; Colstart--) {
+        iCol = Nroot_1;
+        p0r = ioptr + Nroot_1_ColInc + BRLow[Colstart] * 4;
+        IOP = ioptr + (Colstart << ColstartShift);
+        p1r = IOP + BRLow[iCol] * 4;
+        f0r = *(p0r);
+        f0i = *(p0r + 1);
+        f1r = *(p0r + posA);
+        f1i = *(p0r + posAi);
+        for (; iCol > Colstart;) {
+          f2r = *(p0r + 2);
+          f2i = *(p0r + (2 + 1));
+          f3r = *(p0r + posB);
+          f3i = *(p0r + posBi);
+          f4r = *(p1r);
+          f4i = *(p1r + 1);
+          f5r = *(p1r + posA);
+          f5i = *(p1r + posAi);
+          f6r = *(p1r + 2);
+          f6i = *(p1r + (2 + 1));
+          f7r = *(p1r + posB);
+          f7i = *(p1r + posBi);
+
+          t0r = f0r + f1r;
+          t0i = f0i + f1i;
+          f1r = f0r - f1r;
+          f1i = f0i - f1i;
+          t1r = f2r + f3r;
+          t1i = f2i + f3i;
+          f3r = f2r - f3r;
+          f3i = f2i - f3i;
+          f0r = f4r + f5r;
+          f0i = f4i + f5i;
+          f5r = f4r - f5r;
+          f5i = f4i - f5i;
+          f2r = f6r + f7r;
+          f2i = f6i + f7i;
+          f7r = f6r - f7r;
+          f7i = f6i - f7i;
+
+          *(p1r) = t0r;
+          *(p1r + 1) = t0i;
+          *(p1r + 2) = f1r;
+          *(p1r + (2 + 1)) = f1i;
+          *(p1r + posA) = t1r;
+          *(p1r + posAi) = t1i;
+          *(p1r + posB) = f3r;
+          *(p1r + posBi) = f3i;
+          *(p0r) = f0r;
+          *(p0r + 1) = f0i;
+          *(p0r + 2) = f5r;
+          *(p0r + (2 + 1)) = f5i;
+          *(p0r + posA) = f2r;
+          *(p0r + posAi) = f2i;
+          *(p0r + posB) = f7r;
+          *(p0r + posBi) = f7i;
+
+          p0r -= Nrems2;
+          f0r = *(p0r);
+          f0i = *(p0r + 1);
+          f1r = *(p0r + posA);
+          f1i = *(p0r + posAi);
+          iCol -= 1;
+          p1r = IOP + BRLow[iCol] * 4;
+        }
+        f2r = *(p0r + 2);
+        f2i = *(p0r + (2 + 1));
+        f3r = *(p0r + posB);
+        f3i = *(p0r + posBi);
+
+        t0r = f0r + f1r;
+        t0i = f0i + f1i;
+        f1r = f0r - f1r;
+        f1i = f0i - f1i;
+        t1r = f2r + f3r;
+        t1i = f2i + f3i;
+        f3r = f2r - f3r;
+        f3i = f2i - f3i;
+
+        *(p0r) = t0r;
+        *(p0r + 1) = t0i;
+        *(p0r + 2) = f1r;
+        *(p0r + (2 + 1)) = f1i;
+        *(p0r + posA) = t1r;
+        *(p0r + posAi) = t1i;
+        *(p0r + posB) = f3r;
+        *(p0r + posBi) = f3i;
+      }
+    }
+}
+
+static void fft2pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 2 fft      ***/
+    SPFLOAT f0r, f0i, f1r, f1i;
+    SPFLOAT t0r, t0i;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+    f1r = ioptr[2];
+    f1i = ioptr[3];
+
+    /* Butterflys           */
+    /*
+       f0   -       -       t0
+       f1   -  1 -  f1
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i + f1i;
+    f1r = f0r - f1r;
+    f1i = f0i - f1i;
+
+    /* store result */
+    ioptr[0] = t0r;
+    ioptr[1] = t0i;
+    ioptr[2] = f1r;
+    ioptr[3] = f1i;
+}
+
+static void fft4pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 4 fft      ***/
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+    f1r = ioptr[4];
+    f1i = ioptr[5];
+    f2r = ioptr[2];
+    f2i = ioptr[3];
+    f3r = ioptr[6];
+    f3i = ioptr[7];
+
+    /* Butterflys           */
+    /*
+       f0   -       -       t0      -       -       f0
+       f1   -  1 -  f1      -       -       f1
+       f2   -       -       f2      -  1 -  f2
+       f3   -  1 -  t1      - -i -  f3
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i + f1i;
+    f1r = f0r - f1r;
+    f1i = f0i - f1i;
+
+    t1r = f2r - f3r;
+    t1i = f2i - f3i;
+    f2r = f2r + f3r;
+    f2i = f2i + f3i;
+
+    f0r = t0r + f2r;
+    f0i = t0i + f2i;
+    f2r = t0r - f2r;
+    f2i = t0i - f2i;
+
+    f3r = f1r - t1i;
+    f3i = f1i + t1r;
+    f1r = f1r + t1i;
+    f1i = f1i - t1r;
+
+    /* store result */
+    ioptr[0] = f0r;
+    ioptr[1] = f0i;
+    ioptr[2] = f1r;
+    ioptr[3] = f1i;
+    ioptr[4] = f2r;
+    ioptr[5] = f2i;
+    ioptr[6] = f3r;
+    ioptr[7] = f3i;
+}
+
+static void fft8pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 8 fft      ***/
+    SPFLOAT w0r = (SPFLOAT)(1.0 / MYROOT2);    /* cos(pi/4)   */
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+    f1r = ioptr[8];
+    f1i = ioptr[9];
+    f2r = ioptr[4];
+    f2i = ioptr[5];
+    f3r = ioptr[12];
+    f3i = ioptr[13];
+    f4r = ioptr[2];
+    f4i = ioptr[3];
+    f5r = ioptr[10];
+    f5i = ioptr[11];
+    f6r = ioptr[6];
+    f6i = ioptr[7];
+    f7r = ioptr[14];
+    f7i = ioptr[15];
+    /* Butterflys           */
+    /*
+       f0   -       -       t0      -       -       f0      -       -       f0
+       f1   -  1 -  f1      -       -       f1      -       -       f1
+       f2   -       -       f2      -  1 -  f2      -       -       f2
+       f3   -  1 -  t1      - -i -  f3      -       -       f3
+       f4   -       -       t0      -       -       f4      -  1 -  t0
+       f5   -  1 -  f5      -       -       f5      - w3 -  f4
+       f6   -       -       f6      -  1 -  f6      - -i -  t1
+       f7   -  1 -  t1      - -i -  f7      - iw3-  f6
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i + f1i;
+    f1r = f0r - f1r;
+    f1i = f0i - f1i;
+
+    t1r = f2r - f3r;
+    t1i = f2i - f3i;
+    f2r = f2r + f3r;
+    f2i = f2i + f3i;
+
+    f0r = t0r + f2r;
+    f0i = t0i + f2i;
+    f2r = t0r - f2r;
+    f2i = t0i - f2i;
+
+    f3r = f1r - t1i;
+    f3i = f1i + t1r;
+    f1r = f1r + t1i;
+    f1i = f1i - t1r;
+
+    t0r = f4r + f5r;
+    t0i = f4i + f5i;
+    f5r = f4r - f5r;
+    f5i = f4i - f5i;
+
+    t1r = f6r - f7r;
+    t1i = f6i - f7i;
+    f6r = f6r + f7r;
+    f6i = f6i + f7i;
+
+    f4r = t0r + f6r;
+    f4i = t0i + f6i;
+    f6r = t0r - f6r;
+    f6i = t0i - f6i;
+
+    f7r = f5r - t1i;
+    f7i = f5i + t1r;
+    f5r = f5r + t1i;
+    f5i = f5i - t1r;
+
+    t0r = f0r - f4r;
+    t0i = f0i - f4i;
+    f0r = f0r + f4r;
+    f0i = f0i + f4i;
+
+    t1r = f2r - f6i;
+    t1i = f2i + f6r;
+    f2r = f2r + f6i;
+    f2i = f2i - f6r;
+
+    f4r = f1r - f5r * w0r - f5i * w0r;
+    f4i = f1i + f5r * w0r - f5i * w0r;
+    f1r = f1r * Two - f4r;
+    f1i = f1i * Two - f4i;
+
+    f6r = f3r + f7r * w0r - f7i * w0r;
+    f6i = f3i + f7r * w0r + f7i * w0r;
+    f3r = f3r * Two - f6r;
+    f3i = f3i * Two - f6i;
+
+    /* store result */
+    ioptr[0] = f0r;
+    ioptr[1] = f0i;
+    ioptr[2] = f1r;
+    ioptr[3] = f1i;
+    ioptr[4] = f2r;
+    ioptr[5] = f2i;
+    ioptr[6] = f3r;
+    ioptr[7] = f3i;
+    ioptr[8] = t0r;
+    ioptr[9] = t0i;
+    ioptr[10] = f4r;
+    ioptr[11] = f4i;
+    ioptr[12] = t1r;
+    ioptr[13] = t1i;
+    ioptr[14] = f6r;
+    ioptr[15] = f6i;
+}
+
+static void bfR2(SPFLOAT *ioptr, int M, int NDiffU)
+{
+    /*** 2nd radix 2 stage ***/
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int pinc;
+    unsigned int pnext;
+    unsigned int NSameU;
+    unsigned int SameUCnt;
+
+    SPFLOAT *pstrt;
+    SPFLOAT *p0r, *p1r, *p2r, *p3r;
+
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+
+    pinc = NDiffU * 2;            /* 2 floats per complex */
+    pnext = pinc * 4;
+    pos = 2;
+    posi = pos + 1;
+    NSameU = POW2(M) / 4 / NDiffU;        /* 4 Us at a time */
+    pstrt = ioptr;
+    p0r = pstrt;
+    p1r = pstrt + pinc;
+    p2r = p1r + pinc;
+    p3r = p2r + pinc;
+
+    /* Butterflys           */
+    /*
+       f0   -       -       f4
+       f1   -  1 -  f5
+       f2   -       -       f6
+       f3   -  1 -  f7
+     */
+    /* Butterflys           */
+    /*
+       f0   -       -       f4
+       f1   -  1 -  f5
+       f2   -       -       f6
+       f3   -  1 -  f7
+     */
+
+    for (SameUCnt = NSameU; SameUCnt > 0; SameUCnt--) {
+
+      f0r = *p0r;
+      f1r = *p1r;
+      f0i = *(p0r + 1);
+      f1i = *(p1r + 1);
+      f2r = *p2r;
+      f3r = *p3r;
+      f2i = *(p2r + 1);
+      f3i = *(p3r + 1);
+
+      f4r = f0r + f1r;
+      f4i = f0i + f1i;
+      f5r = f0r - f1r;
+      f5i = f0i - f1i;
+
+      f6r = f2r + f3r;
+      f6i = f2i + f3i;
+      f7r = f2r - f3r;
+      f7i = f2i - f3i;
+
+      *p0r = f4r;
+      *(p0r + 1) = f4i;
+      *p1r = f5r;
+      *(p1r + 1) = f5i;
+      *p2r = f6r;
+      *(p2r + 1) = f6i;
+      *p3r = f7r;
+      *(p3r + 1) = f7i;
+
+      f0r = *(p0r + pos);
+      f1i = *(p1r + posi);
+      f0i = *(p0r + posi);
+      f1r = *(p1r + pos);
+      f2r = *(p2r + pos);
+      f3i = *(p3r + posi);
+      f2i = *(p2r + posi);
+      f3r = *(p3r + pos);
+
+      f4r = f0r + f1i;
+      f4i = f0i - f1r;
+      f5r = f0r - f1i;
+      f5i = f0i + f1r;
+
+      f6r = f2r + f3i;
+      f6i = f2i - f3r;
+      f7r = f2r - f3i;
+      f7i = f2i + f3r;
+
+      *(p0r + pos) = f4r;
+      *(p0r + posi) = f4i;
+      *(p1r + pos) = f5r;
+      *(p1r + posi) = f5i;
+      *(p2r + pos) = f6r;
+      *(p2r + posi) = f6i;
+      *(p3r + pos) = f7r;
+      *(p3r + posi) = f7i;
+
+      p0r += pnext;
+      p1r += pnext;
+      p2r += pnext;
+      p3r += pnext;
+    }
+}
+
+static void bfR4(SPFLOAT *ioptr, int M, int NDiffU)
+{
+    /*** 1 radix 4 stage ***/
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int pinc;
+    unsigned int pnext;
+    unsigned int pnexti;
+    unsigned int NSameU;
+    unsigned int SameUCnt;
+
+    SPFLOAT *pstrt;
+    SPFLOAT *p0r, *p1r, *p2r, *p3r;
+
+    SPFLOAT w1r = 1.0 / MYROOT2;    /* cos(pi/4)   */
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    pinc = NDiffU * 2;            /* 2 floats per complex */
+    pnext = pinc * 4;
+    pnexti = pnext + 1;
+    pos = 2;
+    posi = pos + 1;
+    NSameU = POW2(M) / 4 / NDiffU;        /* 4 pts per butterfly */
+    pstrt = ioptr;
+    p0r = pstrt;
+    p1r = pstrt + pinc;
+    p2r = p1r + pinc;
+    p3r = p2r + pinc;
+
+    /* Butterflys           */
+    /*
+       f0   -       -       f0      -       -       f4
+       f1   -  1 -  f5      -       -       f5
+       f2   -       -       f6      -  1 -  f6
+       f3   -  1 -  f3      - -i -  f7
+     */
+    /* Butterflys           */
+    /*
+       f0   -       -       f4      -       -       f4
+       f1   - -i -  t1      -       -       f5
+       f2   -       -       f2      - w1 -  f6
+       f3   - -i -  f7      - iw1-  f7
+     */
+
+    f0r = *p0r;
+    f1r = *p1r;
+    f2r = *p2r;
+    f3r = *p3r;
+    f0i = *(p0r + 1);
+    f1i = *(p1r + 1);
+    f2i = *(p2r + 1);
+    f3i = *(p3r + 1);
+
+    f5r = f0r - f1r;
+    f5i = f0i - f1i;
+    f0r = f0r + f1r;
+    f0i = f0i + f1i;
+
+    f6r = f2r + f3r;
+    f6i = f2i + f3i;
+    f3r = f2r - f3r;
+    f3i = f2i - f3i;
+
+    for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) {
+
+      f7r = f5r - f3i;
+      f7i = f5i + f3r;
+      f5r = f5r + f3i;
+      f5i = f5i - f3r;
+
+      f4r = f0r + f6r;
+      f4i = f0i + f6i;
+      f6r = f0r - f6r;
+      f6i = f0i - f6i;
+
+      f2r = *(p2r + pos);
+      f2i = *(p2r + posi);
+      f1r = *(p1r + pos);
+      f1i = *(p1r + posi);
+      f3i = *(p3r + posi);
+      f0r = *(p0r + pos);
+      f3r = *(p3r + pos);
+      f0i = *(p0r + posi);
+
+      *p3r = f7r;
+      *p0r = f4r;
+      *(p3r + 1) = f7i;
+      *(p0r + 1) = f4i;
+      *p1r = f5r;
+      *p2r = f6r;
+      *(p1r + 1) = f5i;
+      *(p2r + 1) = f6i;
+
+      f7r = f2r - f3i;
+      f7i = f2i + f3r;
+      f2r = f2r + f3i;
+      f2i = f2i - f3r;
+
+      f4r = f0r + f1i;
+      f4i = f0i - f1r;
+      t1r = f0r - f1i;
+      t1i = f0i + f1r;
+
+      f5r = t1r - f7r * w1r + f7i * w1r;
+      f5i = t1i - f7r * w1r - f7i * w1r;
+      f7r = t1r * Two - f5r;
+      f7i = t1i * Two - f5i;
+
+      f6r = f4r - f2r * w1r - f2i * w1r;
+      f6i = f4i + f2r * w1r - f2i * w1r;
+      f4r = f4r * Two - f6r;
+      f4i = f4i * Two - f6i;
+
+      f3r = *(p3r + pnext);
+      f0r = *(p0r + pnext);
+      f3i = *(p3r + pnexti);
+      f0i = *(p0r + pnexti);
+      f2r = *(p2r + pnext);
+      f2i = *(p2r + pnexti);
+      f1r = *(p1r + pnext);
+      f1i = *(p1r + pnexti);
+
+      *(p2r + pos) = f6r;
+      *(p1r + pos) = f5r;
+      *(p2r + posi) = f6i;
+      *(p1r + posi) = f5i;
+      *(p3r + pos) = f7r;
+      *(p0r + pos) = f4r;
+      *(p3r + posi) = f7i;
+      *(p0r + posi) = f4i;
+
+      f6r = f2r + f3r;
+      f6i = f2i + f3i;
+      f3r = f2r - f3r;
+      f3i = f2i - f3i;
+
+      f5r = f0r - f1r;
+      f5i = f0i - f1i;
+      f0r = f0r + f1r;
+      f0i = f0i + f1i;
+
+      p3r += pnext;
+      p0r += pnext;
+      p1r += pnext;
+      p2r += pnext;
+    }
+    f7r = f5r - f3i;
+    f7i = f5i + f3r;
+    f5r = f5r + f3i;
+    f5i = f5i - f3r;
+
+    f4r = f0r + f6r;
+    f4i = f0i + f6i;
+    f6r = f0r - f6r;
+    f6i = f0i - f6i;
+
+    f2r = *(p2r + pos);
+    f2i = *(p2r + posi);
+    f1r = *(p1r + pos);
+    f1i = *(p1r + posi);
+    f3i = *(p3r + posi);
+    f0r = *(p0r + pos);
+    f3r = *(p3r + pos);
+    f0i = *(p0r + posi);
+
+    *p3r = f7r;
+    *p0r = f4r;
+    *(p3r + 1) = f7i;
+    *(p0r + 1) = f4i;
+    *p1r = f5r;
+    *p2r = f6r;
+    *(p1r + 1) = f5i;
+    *(p2r + 1) = f6i;
+
+    f7r = f2r - f3i;
+    f7i = f2i + f3r;
+    f2r = f2r + f3i;
+    f2i = f2i - f3r;
+
+    f4r = f0r + f1i;
+    f4i = f0i - f1r;
+    t1r = f0r - f1i;
+    t1i = f0i + f1r;
+
+    f5r = t1r - f7r * w1r + f7i * w1r;
+    f5i = t1i - f7r * w1r - f7i * w1r;
+    f7r = t1r * Two - f5r;
+    f7i = t1i * Two - f5i;
+
+    f6r = f4r - f2r * w1r - f2i * w1r;
+    f6i = f4i + f2r * w1r - f2i * w1r;
+    f4r = f4r * Two - f6r;
+    f4i = f4i * Two - f6i;
+
+    *(p2r + pos) = f6r;
+    *(p1r + pos) = f5r;
+    *(p2r + posi) = f6i;
+    *(p1r + posi) = f5i;
+    *(p3r + pos) = f7r;
+    *(p0r + pos) = f4r;
+    *(p3r + posi) = f7i;
+    *(p0r + posi) = f4i;
+}
+
+static void bfstages(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int Ustride,
+                     int NDiffU, int StageCnt)
+{
+    /***   RADIX 8 Stages   ***/
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int pinc;
+    unsigned int pnext;
+    unsigned int NSameU;
+    int          Uinc;
+    int          Uinc2;
+    int          Uinc4;
+    unsigned int DiffUCnt;
+    unsigned int SameUCnt;
+    unsigned int U2toU3;
+
+    SPFLOAT *pstrt;
+    SPFLOAT *p0r, *p1r, *p2r, *p3r;
+    SPFLOAT *u0r, *u0i, *u1r, *u1i, *u2r, *u2i;
+
+    SPFLOAT w0r, w0i, w1r, w1i, w2r, w2i, w3r, w3i;
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    pinc = NDiffU * 2;            /* 2 floats per complex */
+    pnext = pinc * 8;
+    pos = pinc * 4;
+    posi = pos + 1;
+    NSameU = POW2(M) / 8 / NDiffU;        /* 8 pts per butterfly */
+    Uinc = (int) NSameU * Ustride;
+    Uinc2 = Uinc * 2;
+    Uinc4 = Uinc * 4;
+    U2toU3 = (POW2(M) / 8) * Ustride;
+    for (; StageCnt > 0; StageCnt--) {
+
+      u0r = &Utbl[0];
+      u0i = &Utbl[POW2(M - 2) * Ustride];
+      u1r = u0r;
+      u1i = u0i;
+      u2r = u0r;
+      u2i = u0i;
+
+      w0r = *u0r;
+      w0i = *u0i;
+      w1r = *u1r;
+      w1i = *u1i;
+      w2r = *u2r;
+      w2i = *u2i;
+      w3r = *(u2r + U2toU3);
+      w3i = *(u2i - U2toU3);
+
+      pstrt = ioptr;
+
+      p0r = pstrt;
+      p1r = pstrt + pinc;
+      p2r = p1r + pinc;
+      p3r = p2r + pinc;
+
+      /* Butterflys           */
+      /*
+         f0   -       -       t0      -       -       f0      -       -       f0
+         f1   - w0-   f1      -       -       f1      -       -       f1
+         f2   -       -       f2      - w1-   f2      -       -       f4
+         f3   - w0-   t1      - iw1-  f3      -       -       f5
+
+         f4   -       -       t0      -       -       f4      - w2-   t0
+         f5   - w0-   f5      -       -       f5      - w3-   t1
+         f6   -       -       f6      - w1-   f6      - iw2-  f6
+         f7   - w0-   t1      - iw1-  f7      - iw3-  f7
+       */
+
+      for (DiffUCnt = NDiffU; DiffUCnt > 0; DiffUCnt--) {
+        f0r = *p0r;
+        f0i = *(p0r + 1);
+        f1r = *p1r;
+        f1i = *(p1r + 1);
+        for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) {
+          f2r = *p2r;
+          f2i = *(p2r + 1);
+          f3r = *p3r;
+          f3i = *(p3r + 1);
+
+          t0r = f0r + f1r * w0r + f1i * w0i;
+          t0i = f0i - f1r * w0i + f1i * w0r;
+          f1r = f0r * Two - t0r;
+          f1i = f0i * Two - t0i;
+
+          f4r = *(p0r + pos);
+          f4i = *(p0r + posi);
+          f5r = *(p1r + pos);
+          f5i = *(p1r + posi);
+
+          f6r = *(p2r + pos);
+          f6i = *(p2r + posi);
+          f7r = *(p3r + pos);
+          f7i = *(p3r + posi);
+
+          t1r = f2r - f3r * w0r - f3i * w0i;
+          t1i = f2i + f3r * w0i - f3i * w0r;
+          f2r = f2r * Two - t1r;
+          f2i = f2i * Two - t1i;
+
+          f0r = t0r + f2r * w1r + f2i * w1i;
+          f0i = t0i - f2r * w1i + f2i * w1r;
+          f2r = t0r * Two - f0r;
+          f2i = t0i * Two - f0i;
+
+          f3r = f1r + t1r * w1i - t1i * w1r;
+          f3i = f1i + t1r * w1r + t1i * w1i;
+          f1r = f1r * Two - f3r;
+          f1i = f1i * Two - f3i;
+
+          t0r = f4r + f5r * w0r + f5i * w0i;
+          t0i = f4i - f5r * w0i + f5i * w0r;
+          f5r = f4r * Two - t0r;
+          f5i = f4i * Two - t0i;
+
+          t1r = f6r - f7r * w0r - f7i * w0i;
+          t1i = f6i + f7r * w0i - f7i * w0r;
+          f6r = f6r * Two - t1r;
+          f6i = f6i * Two - t1i;
+
+          f4r = t0r + f6r * w1r + f6i * w1i;
+          f4i = t0i - f6r * w1i + f6i * w1r;
+          f6r = t0r * Two - f4r;
+          f6i = t0i * Two - f4i;
+
+          f7r = f5r + t1r * w1i - t1i * w1r;
+          f7i = f5i + t1r * w1r + t1i * w1i;
+          f5r = f5r * Two - f7r;
+          f5i = f5i * Two - f7i;
+
+          t0r = f0r - f4r * w2r - f4i * w2i;
+          t0i = f0i + f4r * w2i - f4i * w2r;
+          f0r = f0r * Two - t0r;
+          f0i = f0i * Two - t0i;
+
+          t1r = f1r - f5r * w3r - f5i * w3i;
+          t1i = f1i + f5r * w3i - f5i * w3r;
+          f1r = f1r * Two - t1r;
+          f1i = f1i * Two - t1i;
+
+          *(p0r + pos) = t0r;
+          *(p1r + pos) = t1r;
+          *(p0r + posi) = t0i;
+          *(p1r + posi) = t1i;
+          *p0r = f0r;
+          *p1r = f1r;
+          *(p0r + 1) = f0i;
+          *(p1r + 1) = f1i;
+
+          p0r += pnext;
+          f0r = *p0r;
+          f0i = *(p0r + 1);
+
+          p1r += pnext;
+
+          f1r = *p1r;
+          f1i = *(p1r + 1);
+
+          f4r = f2r - f6r * w2i + f6i * w2r;
+          f4i = f2i - f6r * w2r - f6i * w2i;
+          f6r = f2r * Two - f4r;
+          f6i = f2i * Two - f4i;
+
+          f5r = f3r - f7r * w3i + f7i * w3r;
+          f5i = f3i - f7r * w3r - f7i * w3i;
+          f7r = f3r * Two - f5r;
+          f7i = f3i * Two - f5i;
+
+          *p2r = f4r;
+          *p3r = f5r;
+          *(p2r + 1) = f4i;
+          *(p3r + 1) = f5i;
+          *(p2r + pos) = f6r;
+          *(p3r + pos) = f7r;
+          *(p2r + posi) = f6i;
+          *(p3r + posi) = f7i;
+
+          p2r += pnext;
+          p3r += pnext;
+        }
+
+        f2r = *p2r;
+        f2i = *(p2r + 1);
+        f3r = *p3r;
+        f3i = *(p3r + 1);
+
+        t0r = f0r + f1r * w0r + f1i * w0i;
+        t0i = f0i - f1r * w0i + f1i * w0r;
+        f1r = f0r * Two - t0r;
+        f1i = f0i * Two - t0i;
+
+        f4r = *(p0r + pos);
+        f4i = *(p0r + posi);
+        f5r = *(p1r + pos);
+        f5i = *(p1r + posi);
+
+        f6r = *(p2r + pos);
+        f6i = *(p2r + posi);
+        f7r = *(p3r + pos);
+        f7i = *(p3r + posi);
+
+        t1r = f2r - f3r * w0r - f3i * w0i;
+        t1i = f2i + f3r * w0i - f3i * w0r;
+        f2r = f2r * Two - t1r;
+        f2i = f2i * Two - t1i;
+
+        f0r = t0r + f2r * w1r + f2i * w1i;
+        f0i = t0i - f2r * w1i + f2i * w1r;
+        f2r = t0r * Two - f0r;
+        f2i = t0i * Two - f0i;
+
+        f3r = f1r + t1r * w1i - t1i * w1r;
+        f3i = f1i + t1r * w1r + t1i * w1i;
+        f1r = f1r * Two - f3r;
+        f1i = f1i * Two - f3i;
+
+        if ((int) DiffUCnt == NDiffU / 2)
+          Uinc4 = -Uinc4;
+
+        u0r += Uinc4;
+        u0i -= Uinc4;
+        u1r += Uinc2;
+        u1i -= Uinc2;
+        u2r += Uinc;
+        u2i -= Uinc;
+
+        pstrt += 2;
+
+        t0r = f4r + f5r * w0r + f5i * w0i;
+        t0i = f4i - f5r * w0i + f5i * w0r;
+        f5r = f4r * Two - t0r;
+        f5i = f4i * Two - t0i;
+
+        t1r = f6r - f7r * w0r - f7i * w0i;
+        t1i = f6i + f7r * w0i - f7i * w0r;
+        f6r = f6r * Two - t1r;
+        f6i = f6i * Two - t1i;
+
+        f4r = t0r + f6r * w1r + f6i * w1i;
+        f4i = t0i - f6r * w1i + f6i * w1r;
+        f6r = t0r * Two - f4r;
+        f6i = t0i * Two - f4i;
+
+        f7r = f5r + t1r * w1i - t1i * w1r;
+        f7i = f5i + t1r * w1r + t1i * w1i;
+        f5r = f5r * Two - f7r;
+        f5i = f5i * Two - f7i;
+
+        w0r = *u0r;
+        w0i = *u0i;
+        w1r = *u1r;
+        w1i = *u1i;
+
+        if ((int) DiffUCnt <= NDiffU / 2)
+          w0r = -w0r;
+
+        t0r = f0r - f4r * w2r - f4i * w2i;
+        t0i = f0i + f4r * w2i - f4i * w2r;
+        f0r = f0r * Two - t0r;
+        f0i = f0i * Two - t0i;
+
+        f4r = f2r - f6r * w2i + f6i * w2r;
+        f4i = f2i - f6r * w2r - f6i * w2i;
+        f6r = f2r * Two - f4r;
+        f6i = f2i * Two - f4i;
+
+        *(p0r + pos) = t0r;
+        *p2r = f4r;
+        *(p0r + posi) = t0i;
+        *(p2r + 1) = f4i;
+        w2r = *u2r;
+        w2i = *u2i;
+        *p0r = f0r;
+        *(p2r + pos) = f6r;
+        *(p0r + 1) = f0i;
+        *(p2r + posi) = f6i;
+
+        p0r = pstrt;
+        p2r = pstrt + pinc + pinc;
+
+        t1r = f1r - f5r * w3r - f5i * w3i;
+        t1i = f1i + f5r * w3i - f5i * w3r;
+        f1r = f1r * Two - t1r;
+        f1i = f1i * Two - t1i;
+
+        f5r = f3r - f7r * w3i + f7i * w3r;
+        f5i = f3i - f7r * w3r - f7i * w3i;
+        f7r = f3r * Two - f5r;
+        f7i = f3i * Two - f5i;
+
+        *(p1r + pos) = t1r;
+        *p3r = f5r;
+        *(p1r + posi) = t1i;
+        *(p3r + 1) = f5i;
+        w3r = *(u2r + U2toU3);
+        w3i = *(u2i - U2toU3);
+        *p1r = f1r;
+        *(p3r + pos) = f7r;
+        *(p1r + 1) = f1i;
+        *(p3r + posi) = f7i;
+
+        p1r = pstrt + pinc;
+        p3r = p2r + pinc;
+      }
+      NSameU /= 8;
+      Uinc /= 8;
+      Uinc2 /= 8;
+      Uinc4 = Uinc * 4;
+      NDiffU *= 8;
+      pinc *= 8;
+      pnext *= 8;
+      pos *= 8;
+      posi = pos + 1;
+    }
+}
+
+static void fftrecurs(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int Ustride, int NDiffU,
+                      int StageCnt)
+{
+    /* recursive bfstages calls to maximize on chip cache efficiency */
+    int i1;
+
+    if (M <= (int) MCACHE)              /* fits on chip ? */
+      bfstages(ioptr, M, Utbl, Ustride, NDiffU, StageCnt); /* RADIX 8 Stages */
+    else {
+      for (i1 = 0; i1 < 8; i1++) {
+        fftrecurs(&ioptr[i1 * POW2(M - 3) * 2], M - 3, Utbl, 8 * Ustride,
+                  NDiffU, StageCnt - 1);  /*  RADIX 8 Stages      */
+      }
+      bfstages(ioptr, M, Utbl, Ustride, POW2(M - 3), 1);  /*  RADIX 8 Stage */
+    }
+}
+
+static void ffts1(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int16_t *BRLow)
+{
+    /* Compute in-place complex fft on the rows of the input array  */
+    /* INPUTS                                                       */
+    /*   *ioptr = input data array                                  */
+    /*   M = log2 of fft size (ex M=10 for 1024 point fft)          */
+    /*   *Utbl = cosine table                                       */
+    /*   *BRLow = bit reversed counter table                        */
+    /* OUTPUTS                                                      */
+    /*   *ioptr = output data array                                 */
+
+    int StageCnt;
+    int NDiffU;
+
+    switch (M) {
+    case 0:
+      break;
+    case 1:
+      fft2pt(ioptr);            /* a 2 pt fft */
+      break;
+    case 2:
+      fft4pt(ioptr);            /* a 4 pt fft */
+      break;
+    case 3:
+      fft8pt(ioptr);            /* an 8 pt fft */
+      break;
+    default:
+      bitrevR2(ioptr, M, BRLow);  /* bit reverse and first radix 2 stage */
+      StageCnt = (M - 1) / 3;     /* number of radix 8 stages           */
+      NDiffU = 2;                 /* one radix 2 stage already complete */
+      if ((M - 1 - (StageCnt * 3)) == 1) {
+        bfR2(ioptr, M, NDiffU); /* 1 radix 2 stage */
+        NDiffU *= 2;
+      }
+      if ((M - 1 - (StageCnt * 3)) == 2) {
+        bfR4(ioptr, M, NDiffU); /* 1 radix 4 stage */
+        NDiffU *= 4;
+      }
+      if (M <= (int) MCACHE)
+        bfstages(ioptr, M, Utbl, 1, NDiffU, StageCnt);  /* RADIX 8 Stages */
+      else
+        fftrecurs(ioptr, M, Utbl, 1, NDiffU, StageCnt); /* RADIX 8 Stages */
+    }
+}
+
+/******************
+* parts of iffts1 *
+******************/
+
+static void scbitrevR2(SPFLOAT *ioptr, int M, int16_t *BRLow, SPFLOAT scale)
+{
+    /*** scaled bit reverse and first radix 2 stage forward or inverse fft ***/
+    SPFLOAT f0r;
+    SPFLOAT f0i;
+    SPFLOAT f1r;
+    SPFLOAT f1i;
+    SPFLOAT f2r;
+    SPFLOAT f2i;
+    SPFLOAT f3r;
+    SPFLOAT f3i;
+    SPFLOAT f4r;
+    SPFLOAT f4i;
+    SPFLOAT f5r;
+    SPFLOAT f5i;
+    SPFLOAT f6r;
+    SPFLOAT f6i;
+    SPFLOAT f7r;
+    SPFLOAT f7i;
+    SPFLOAT t0r;
+    SPFLOAT t0i;
+    SPFLOAT t1r;
+    SPFLOAT t1i;
+    SPFLOAT *p0r;
+    SPFLOAT *p1r;
+    SPFLOAT *IOP;
+    SPFLOAT *iolimit;
+    int Colstart;
+    int iCol;
+    unsigned int posA;
+    unsigned int posAi;
+    unsigned int posB;
+    unsigned int posBi;
+
+    const unsigned int Nrems2 = POW2((M + 3) / 2);
+    const unsigned int Nroot_1_ColInc = POW2(M) - Nrems2;
+    const unsigned int Nroot_1 = POW2(M / 2 - 1) - 1;
+    const unsigned int ColstartShift = (M + 1) / 2 + 1;
+
+    posA = POW2(M);               /* 1/2 of POW2(M) complexes */
+    posAi = posA + 1;
+    posB = posA + 2;
+    posBi = posB + 1;
+
+    iolimit = ioptr + Nrems2;
+    for (; ioptr < iolimit; ioptr += POW2(M / 2 + 1)) {
+      for (Colstart = Nroot_1; Colstart >= 0; Colstart--) {
+        iCol = Nroot_1;
+        p0r = ioptr + Nroot_1_ColInc + BRLow[Colstart] * 4;
+        IOP = ioptr + (Colstart << ColstartShift);
+        p1r = IOP + BRLow[iCol] * 4;
+        f0r = *(p0r);
+        f0i = *(p0r + 1);
+        f1r = *(p0r + posA);
+        f1i = *(p0r + posAi);
+        for (; iCol > Colstart;) {
+          f2r = *(p0r + 2);
+          f2i = *(p0r + (2 + 1));
+          f3r = *(p0r + posB);
+          f3i = *(p0r + posBi);
+          f4r = *(p1r);
+          f4i = *(p1r + 1);
+          f5r = *(p1r + posA);
+          f5i = *(p1r + posAi);
+          f6r = *(p1r + 2);
+          f6i = *(p1r + (2 + 1));
+          f7r = *(p1r + posB);
+          f7i = *(p1r + posBi);
+
+          t0r = f0r + f1r;
+          t0i = f0i + f1i;
+          f1r = f0r - f1r;
+          f1i = f0i - f1i;
+          t1r = f2r + f3r;
+          t1i = f2i + f3i;
+          f3r = f2r - f3r;
+          f3i = f2i - f3i;
+          f0r = f4r + f5r;
+          f0i = f4i + f5i;
+          f5r = f4r - f5r;
+          f5i = f4i - f5i;
+          f2r = f6r + f7r;
+          f2i = f6i + f7i;
+          f7r = f6r - f7r;
+          f7i = f6i - f7i;
+
+          *(p1r) = scale * t0r;
+          *(p1r + 1) = scale * t0i;
+          *(p1r + 2) = scale * f1r;
+          *(p1r + (2 + 1)) = scale * f1i;
+          *(p1r + posA) = scale * t1r;
+          *(p1r + posAi) = scale * t1i;
+          *(p1r + posB) = scale * f3r;
+          *(p1r + posBi) = scale * f3i;
+          *(p0r) = scale * f0r;
+          *(p0r + 1) = scale * f0i;
+          *(p0r + 2) = scale * f5r;
+          *(p0r + (2 + 1)) = scale * f5i;
+          *(p0r + posA) = scale * f2r;
+          *(p0r + posAi) = scale * f2i;
+          *(p0r + posB) = scale * f7r;
+          *(p0r + posBi) = scale * f7i;
+
+          p0r -= Nrems2;
+          f0r = *(p0r);
+          f0i = *(p0r + 1);
+          f1r = *(p0r + posA);
+          f1i = *(p0r + posAi);
+          iCol -= 1;
+          p1r = IOP + BRLow[iCol] * 4;
+        }
+        f2r = *(p0r + 2);
+        f2i = *(p0r + (2 + 1));
+        f3r = *(p0r + posB);
+        f3i = *(p0r + posBi);
+
+        t0r = f0r + f1r;
+        t0i = f0i + f1i;
+        f1r = f0r - f1r;
+        f1i = f0i - f1i;
+        t1r = f2r + f3r;
+        t1i = f2i + f3i;
+        f3r = f2r - f3r;
+        f3i = f2i - f3i;
+
+        *(p0r) = scale * t0r;
+        *(p0r + 1) = scale * t0i;
+        *(p0r + 2) = scale * f1r;
+        *(p0r + (2 + 1)) = scale * f1i;
+        *(p0r + posA) = scale * t1r;
+        *(p0r + posAi) = scale * t1i;
+        *(p0r + posB) = scale * f3r;
+        *(p0r + posBi) = scale * f3i;
+      }
+    }
+}
+
+/* static void ifft2pt(SPFLOAT *ioptr, SPFLOAT scale) */
+/* { */
+/*     /\***   RADIX 2 ifft     ***\/ */
+/*     SPFLOAT f0r, f0i, f1r, f1i; */
+/*     SPFLOAT t0r, t0i; */
+
+/*     /\* bit reversed load *\/ */
+/*     f0r = ioptr[0]; */
+/*     f0i = ioptr[1]; */
+/*     f1r = ioptr[2]; */
+/*     f1i = ioptr[3]; */
+
+/*     /\* Butterflys           *\/ */
+/*     /\* */
+/*        f0   -       -       t0 */
+/*        f1   -  1 -  f1 */
+/*      *\/ */
+
+/*     t0r = f0r + f1r; */
+/*     t0i = f0i + f1i; */
+/*     f1r = f0r - f1r; */
+/*     f1i = f0i - f1i; */
+
+/*     /\* store result *\/ */
+/*     ioptr[0] = scale * t0r; */
+/*     ioptr[1] = scale * t0i; */
+/*     ioptr[2] = scale * f1r; */
+/*     ioptr[3] = scale * f1i; */
+/* } */
+
+/* static void ifft4pt(SPFLOAT *ioptr, SPFLOAT scale) */
+/* { */
+/*     /\***   RADIX 4 ifft     ***\/ */
+/*     SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; */
+/*     SPFLOAT t0r, t0i, t1r, t1i; */
+
+/*     /\* bit reversed load *\/ */
+/*     f0r = ioptr[0]; */
+/*     f0i = ioptr[1]; */
+/*     f1r = ioptr[4]; */
+/*     f1i = ioptr[5]; */
+/*     f2r = ioptr[2]; */
+/*     f2i = ioptr[3]; */
+/*     f3r = ioptr[6]; */
+/*     f3i = ioptr[7]; */
+
+/*     /\* Butterflys           *\/ */
+/*     /\* */
+/*        f0   -       -       t0      -       -       f0 */
+/*        f1   -  1 -  f1      -       -       f1 */
+/*        f2   -       -       f2      -  1 -  f2 */
+/*        f3   -  1 -  t1      -  i -  f3 */
+/*      *\/ */
+
+/*     t0r = f0r + f1r; */
+/*     t0i = f0i + f1i; */
+/*     f1r = f0r - f1r; */
+/*     f1i = f0i - f1i; */
+
+/*     t1r = f2r - f3r; */
+/*     t1i = f2i - f3i; */
+/*     f2r = f2r + f3r; */
+/*     f2i = f2i + f3i; */
+
+/*     f0r = t0r + f2r; */
+/*     f0i = t0i + f2i; */
+/*     f2r = t0r - f2r; */
+/*     f2i = t0i - f2i; */
+
+/*     f3r = f1r + t1i; */
+/*     f3i = f1i - t1r; */
+/*     f1r = f1r - t1i; */
+/*     f1i = f1i + t1r; */
+
+/*     /\* store result *\/ */
+/*     ioptr[0] = scale * f0r; */
+/*     ioptr[1] = scale * f0i; */
+/*     ioptr[2] = scale * f1r; */
+/*     ioptr[3] = scale * f1i; */
+/*     ioptr[4] = scale * f2r; */
+/*     ioptr[5] = scale * f2i; */
+/*     ioptr[6] = scale * f3r; */
+/*     ioptr[7] = scale * f3i; */
+/* } */
+
+/* static void ifft8pt(SPFLOAT *ioptr, SPFLOAT scale) */
+/* { */
+/*     /\***   RADIX 8 ifft     ***\/ */
+/*     SPFLOAT w0r = 1.0 / MYROOT2;    /\* cos(pi/4)   *\/ */
+/*     SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i; */
+/*     SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i; */
+/*     SPFLOAT t0r, t0i, t1r, t1i; */
+/*     const SPFLOAT Two = 2.0; */
+
+/*     /\* bit reversed load *\/ */
+/*     f0r = ioptr[0]; */
+/*     f0i = ioptr[1]; */
+/*     f1r = ioptr[8]; */
+/*     f1i = ioptr[9]; */
+/*     f2r = ioptr[4]; */
+/*     f2i = ioptr[5]; */
+/*     f3r = ioptr[12]; */
+/*     f3i = ioptr[13]; */
+/*     f4r = ioptr[2]; */
+/*     f4i = ioptr[3]; */
+/*     f5r = ioptr[10]; */
+/*     f5i = ioptr[11]; */
+/*     f6r = ioptr[6]; */
+/*     f6i = ioptr[7]; */
+/*     f7r = ioptr[14]; */
+/*     f7i = ioptr[15]; */
+
+/*     /\* Butterflys           *\/ */
+/*     /\* */
+/*        f0   -       -       t0      -       -       f0      -       -       f0 */
+/*        f1   -  1 -  f1      -       -       f1      -       -       f1 */
+/*        f2   -       -       f2      -  1 -  f2      -       -       f2 */
+/*        f3   -  1 -  t1      -  i -  f3      -       -       f3 */
+/*        f4   -       -       t0      -       -       f4      -  1 -  t0 */
+/*        f5   -  1 -  f5      -       -       f5      - w3 -  f4 */
+/*        f6   -       -       f6      -  1 -  f6      -  i -  t1 */
+/*        f7   -  1 -  t1      -  i -  f7      - iw3-  f6 */
+/*      *\/ */
+
+/*     t0r = f0r + f1r; */
+/*     t0i = f0i + f1i; */
+/*     f1r = f0r - f1r; */
+/*     f1i = f0i - f1i; */
+
+/*     t1r = f2r - f3r; */
+/*     t1i = f2i - f3i; */
+/*     f2r = f2r + f3r; */
+/*     f2i = f2i + f3i; */
+
+/*     f0r = t0r + f2r; */
+/*     f0i = t0i + f2i; */
+/*     f2r = t0r - f2r; */
+/*     f2i = t0i - f2i; */
+
+/*     f3r = f1r + t1i; */
+/*     f3i = f1i - t1r; */
+/*     f1r = f1r - t1i; */
+/*     f1i = f1i + t1r; */
+
+/*     t0r = f4r + f5r; */
+/*     t0i = f4i + f5i; */
+/*     f5r = f4r - f5r; */
+/*     f5i = f4i - f5i; */
+
+/*     t1r = f6r - f7r; */
+/*     t1i = f6i - f7i; */
+/*     f6r = f6r + f7r; */
+/*     f6i = f6i + f7i; */
+
+/*     f4r = t0r + f6r; */
+/*     f4i = t0i + f6i; */
+/*     f6r = t0r - f6r; */
+/*     f6i = t0i - f6i; */
+
+/*     f7r = f5r + t1i; */
+/*     f7i = f5i - t1r; */
+/*     f5r = f5r - t1i; */
+/*     f5i = f5i + t1r; */
+
+/*     t0r = f0r - f4r; */
+/*     t0i = f0i - f4i; */
+/*     f0r = f0r + f4r; */
+/*     f0i = f0i + f4i; */
+
+/*     t1r = f2r + f6i; */
+/*     t1i = f2i - f6r; */
+/*     f2r = f2r - f6i; */
+/*     f2i = f2i + f6r; */
+
+/*     f4r = f1r - f5r * w0r + f5i * w0r; */
+/*     f4i = f1i - f5r * w0r - f5i * w0r; */
+/*     f1r = f1r * Two - f4r; */
+/*     f1i = f1i * Two - f4i; */
+
+/*     f6r = f3r + f7r * w0r + f7i * w0r; */
+/*     f6i = f3i - f7r * w0r + f7i * w0r; */
+/*     f3r = f3r * Two - f6r; */
+/*     f3i = f3i * Two - f6i; */
+
+/*     /\* store result *\/ */
+/*     ioptr[0] = scale * f0r; */
+/*     ioptr[1] = scale * f0i; */
+/*     ioptr[2] = scale * f1r; */
+/*     ioptr[3] = scale * f1i; */
+/*     ioptr[4] = scale * f2r; */
+/*     ioptr[5] = scale * f2i; */
+/*     ioptr[6] = scale * f3r; */
+/*     ioptr[7] = scale * f3i; */
+/*     ioptr[8] = scale * t0r; */
+/*     ioptr[9] = scale * t0i; */
+/*     ioptr[10] = scale * f4r; */
+/*     ioptr[11] = scale * f4i; */
+/*     ioptr[12] = scale * t1r; */
+/*     ioptr[13] = scale * t1i; */
+/*     ioptr[14] = scale * f6r; */
+/*     ioptr[15] = scale * f6i; */
+/* } */
+
+static void ibfR2(SPFLOAT *ioptr, int M, int NDiffU)
+{
+    /*** 2nd radix 2 stage ***/
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int pinc;
+    unsigned int pnext;
+    unsigned int NSameU;
+    unsigned int SameUCnt;
+
+    SPFLOAT *pstrt;
+    SPFLOAT *p0r, *p1r, *p2r, *p3r;
+
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+
+    pinc = NDiffU * 2;            /* 2 floats per complex */
+    pnext = pinc * 4;
+    pos = 2;
+    posi = pos + 1;
+    NSameU = POW2(M) / 4 / NDiffU;        /* 4 Us at a time */
+    pstrt = ioptr;
+    p0r = pstrt;
+    p1r = pstrt + pinc;
+    p2r = p1r + pinc;
+    p3r = p2r + pinc;
+
+    /* Butterflys           */
+    /*
+       f0   -       -       f4
+       f1   -  1 -  f5
+       f2   -       -       f6
+       f3   -  1 -  f7
+     */
+    /* Butterflys           */
+    /*
+       f0   -       -       f4
+       f1   -  1 -  f5
+       f2   -       -       f6
+       f3   -  1 -  f7
+     */
+
+    for (SameUCnt = NSameU; SameUCnt > 0; SameUCnt--) {
+
+      f0r = *p0r;
+      f1r = *p1r;
+      f0i = *(p0r + 1);
+      f1i = *(p1r + 1);
+      f2r = *p2r;
+      f3r = *p3r;
+      f2i = *(p2r + 1);
+      f3i = *(p3r + 1);
+
+      f4r = f0r + f1r;
+      f4i = f0i + f1i;
+      f5r = f0r - f1r;
+      f5i = f0i - f1i;
+
+      f6r = f2r + f3r;
+      f6i = f2i + f3i;
+      f7r = f2r - f3r;
+      f7i = f2i - f3i;
+
+      *p0r = f4r;
+      *(p0r + 1) = f4i;
+      *p1r = f5r;
+      *(p1r + 1) = f5i;
+      *p2r = f6r;
+      *(p2r + 1) = f6i;
+      *p3r = f7r;
+      *(p3r + 1) = f7i;
+
+      f0r = *(p0r + pos);
+      f1i = *(p1r + posi);
+      f0i = *(p0r + posi);
+      f1r = *(p1r + pos);
+      f2r = *(p2r + pos);
+      f3i = *(p3r + posi);
+      f2i = *(p2r + posi);
+      f3r = *(p3r + pos);
+
+      f4r = f0r - f1i;
+      f4i = f0i + f1r;
+      f5r = f0r + f1i;
+      f5i = f0i - f1r;
+
+      f6r = f2r - f3i;
+      f6i = f2i + f3r;
+      f7r = f2r + f3i;
+      f7i = f2i - f3r;
+
+      *(p0r + pos) = f4r;
+      *(p0r + posi) = f4i;
+      *(p1r + pos) = f5r;
+      *(p1r + posi) = f5i;
+      *(p2r + pos) = f6r;
+      *(p2r + posi) = f6i;
+      *(p3r + pos) = f7r;
+      *(p3r + posi) = f7i;
+
+      p0r += pnext;
+      p1r += pnext;
+      p2r += pnext;
+      p3r += pnext;
+    }
+}
+
+static void ibfR4(SPFLOAT *ioptr, int M, int NDiffU)
+{
+    /*** 1 radix 4 stage ***/
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int pinc;
+    unsigned int pnext;
+    unsigned int pnexti;
+    unsigned int NSameU;
+    unsigned int SameUCnt;
+
+    SPFLOAT *pstrt;
+    SPFLOAT *p0r, *p1r, *p2r, *p3r;
+
+    SPFLOAT w1r = 1.0 / MYROOT2;    /* cos(pi/4)   */
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    pinc = NDiffU * 2;            /* 2 floats per complex */
+    pnext = pinc * 4;
+    pnexti = pnext + 1;
+    pos = 2;
+    posi = pos + 1;
+    NSameU = POW2(M) / 4 / NDiffU;        /* 4 pts per butterfly */
+    pstrt = ioptr;
+    p0r = pstrt;
+    p1r = pstrt + pinc;
+    p2r = p1r + pinc;
+    p3r = p2r + pinc;
+
+    /* Butterflys           */
+    /*
+       f0   -       -       f0      -       -       f4
+       f1   -  1 -  f5      -       -       f5
+       f2   -       -       f6      -  1 -  f6
+       f3   -  1 -  f3      - -i -  f7
+     */
+    /* Butterflys           */
+    /*
+       f0   -       -       f4      -       -       f4
+       f1   - -i -  t1      -       -       f5
+       f2   -       -       f2      - w1 -  f6
+       f3   - -i -  f7      - iw1-  f7
+     */
+
+    f0r = *p0r;
+    f1r = *p1r;
+    f2r = *p2r;
+    f3r = *p3r;
+    f0i = *(p0r + 1);
+    f1i = *(p1r + 1);
+    f2i = *(p2r + 1);
+    f3i = *(p3r + 1);
+
+    f5r = f0r - f1r;
+    f5i = f0i - f1i;
+    f0r = f0r + f1r;
+    f0i = f0i + f1i;
+
+    f6r = f2r + f3r;
+    f6i = f2i + f3i;
+    f3r = f2r - f3r;
+    f3i = f2i - f3i;
+
+    for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) {
+
+      f7r = f5r + f3i;
+      f7i = f5i - f3r;
+      f5r = f5r - f3i;
+      f5i = f5i + f3r;
+
+      f4r = f0r + f6r;
+      f4i = f0i + f6i;
+      f6r = f0r - f6r;
+      f6i = f0i - f6i;
+
+      f2r = *(p2r + pos);
+      f2i = *(p2r + posi);
+      f1r = *(p1r + pos);
+      f1i = *(p1r + posi);
+      f3i = *(p3r + posi);
+      f0r = *(p0r + pos);
+      f3r = *(p3r + pos);
+      f0i = *(p0r + posi);
+
+      *p3r = f7r;
+      *p0r = f4r;
+      *(p3r + 1) = f7i;
+      *(p0r + 1) = f4i;
+      *p1r = f5r;
+      *p2r = f6r;
+      *(p1r + 1) = f5i;
+      *(p2r + 1) = f6i;
+
+      f7r = f2r + f3i;
+      f7i = f2i - f3r;
+      f2r = f2r - f3i;
+      f2i = f2i + f3r;
+
+      f4r = f0r - f1i;
+      f4i = f0i + f1r;
+      t1r = f0r + f1i;
+      t1i = f0i - f1r;
+
+      f5r = t1r - f7r * w1r - f7i * w1r;
+      f5i = t1i + f7r * w1r - f7i * w1r;
+      f7r = t1r * Two - f5r;
+      f7i = t1i * Two - f5i;
+
+      f6r = f4r - f2r * w1r + f2i * w1r;
+      f6i = f4i - f2r * w1r - f2i * w1r;
+      f4r = f4r * Two - f6r;
+      f4i = f4i * Two - f6i;
+
+      f3r = *(p3r + pnext);
+      f0r = *(p0r + pnext);
+      f3i = *(p3r + pnexti);
+      f0i = *(p0r + pnexti);
+      f2r = *(p2r + pnext);
+      f2i = *(p2r + pnexti);
+      f1r = *(p1r + pnext);
+      f1i = *(p1r + pnexti);
+
+      *(p2r + pos) = f6r;
+      *(p1r + pos) = f5r;
+      *(p2r + posi) = f6i;
+      *(p1r + posi) = f5i;
+      *(p3r + pos) = f7r;
+      *(p0r + pos) = f4r;
+      *(p3r + posi) = f7i;
+      *(p0r + posi) = f4i;
+
+      f6r = f2r + f3r;
+      f6i = f2i + f3i;
+      f3r = f2r - f3r;
+      f3i = f2i - f3i;
+
+      f5r = f0r - f1r;
+      f5i = f0i - f1i;
+      f0r = f0r + f1r;
+      f0i = f0i + f1i;
+
+      p3r += pnext;
+      p0r += pnext;
+      p1r += pnext;
+      p2r += pnext;
+    }
+    f7r = f5r + f3i;
+    f7i = f5i - f3r;
+    f5r = f5r - f3i;
+    f5i = f5i + f3r;
+
+    f4r = f0r + f6r;
+    f4i = f0i + f6i;
+    f6r = f0r - f6r;
+    f6i = f0i - f6i;
+
+    f2r = *(p2r + pos);
+    f2i = *(p2r + posi);
+    f1r = *(p1r + pos);
+    f1i = *(p1r + posi);
+    f3i = *(p3r + posi);
+    f0r = *(p0r + pos);
+    f3r = *(p3r + pos);
+    f0i = *(p0r + posi);
+
+    *p3r = f7r;
+    *p0r = f4r;
+    *(p3r + 1) = f7i;
+    *(p0r + 1) = f4i;
+    *p1r = f5r;
+    *p2r = f6r;
+    *(p1r + 1) = f5i;
+    *(p2r + 1) = f6i;
+
+    f7r = f2r + f3i;
+    f7i = f2i - f3r;
+    f2r = f2r - f3i;
+    f2i = f2i + f3r;
+
+    f4r = f0r - f1i;
+    f4i = f0i + f1r;
+    t1r = f0r + f1i;
+    t1i = f0i - f1r;
+
+    f5r = t1r - f7r * w1r - f7i * w1r;
+    f5i = t1i + f7r * w1r - f7i * w1r;
+    f7r = t1r * Two - f5r;
+    f7i = t1i * Two - f5i;
+
+    f6r = f4r - f2r * w1r + f2i * w1r;
+    f6i = f4i - f2r * w1r - f2i * w1r;
+    f4r = f4r * Two - f6r;
+    f4i = f4i * Two - f6i;
+
+    *(p2r + pos) = f6r;
+    *(p1r + pos) = f5r;
+    *(p2r + posi) = f6i;
+    *(p1r + posi) = f5i;
+    *(p3r + pos) = f7r;
+    *(p0r + pos) = f4r;
+    *(p3r + posi) = f7i;
+    *(p0r + posi) = f4i;
+}
+
+static void ibfstages(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int Ustride,
+                      int NDiffU, int StageCnt)
+{
+    /***   RADIX 8 Stages   ***/
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int pinc;
+    unsigned int pnext;
+    unsigned int NSameU;
+    int          Uinc;
+    int          Uinc2;
+    int          Uinc4;
+    unsigned int DiffUCnt;
+    unsigned int SameUCnt;
+    unsigned int U2toU3;
+
+    SPFLOAT *pstrt;
+    SPFLOAT *p0r, *p1r, *p2r, *p3r;
+    SPFLOAT *u0r, *u0i, *u1r, *u1i, *u2r, *u2i;
+
+    SPFLOAT w0r, w0i, w1r, w1i, w2r, w2i, w3r, w3i;
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    pinc = NDiffU * 2;            /* 2 floats per complex */
+    pnext = pinc * 8;
+    pos = pinc * 4;
+    posi = pos + 1;
+    NSameU = POW2(M) / 8 / NDiffU;        /* 8 pts per butterfly */
+    Uinc = (int) NSameU * Ustride;
+    Uinc2 = Uinc * 2;
+    Uinc4 = Uinc * 4;
+    U2toU3 = (POW2(M) / 8) * Ustride;
+    for (; StageCnt > 0; StageCnt--) {
+
+      u0r = &Utbl[0];
+      u0i = &Utbl[POW2(M - 2) * Ustride];
+      u1r = u0r;
+      u1i = u0i;
+      u2r = u0r;
+      u2i = u0i;
+
+      w0r = *u0r;
+      w0i = *u0i;
+      w1r = *u1r;
+      w1i = *u1i;
+      w2r = *u2r;
+      w2i = *u2i;
+      w3r = *(u2r + U2toU3);
+      w3i = *(u2i - U2toU3);
+
+      pstrt = ioptr;
+
+      p0r = pstrt;
+      p1r = pstrt + pinc;
+      p2r = p1r + pinc;
+      p3r = p2r + pinc;
+
+      /* Butterflys           */
+      /*
+         f0   -       -       t0      -       -       f0      -       -       f0
+         f1   - w0-   f1      -       -       f1      -       -       f1
+         f2   -       -       f2      - w1-   f2      -       -       f4
+         f3   - w0-   t1      - iw1-  f3      -       -       f5
+
+         f4   -       -       t0      -       -       f4      - w2-   t0
+         f5   - w0-   f5      -       -       f5      - w3-   t1
+         f6   -       -       f6      - w1-   f6      - iw2-  f6
+         f7   - w0-   t1      - iw1-  f7      - iw3-  f7
+       */
+
+      for (DiffUCnt = NDiffU; DiffUCnt > 0; DiffUCnt--) {
+        f0r = *p0r;
+        f0i = *(p0r + 1);
+        f1r = *p1r;
+        f1i = *(p1r + 1);
+        for (SameUCnt = NSameU - 1; SameUCnt > 0; SameUCnt--) {
+          f2r = *p2r;
+          f2i = *(p2r + 1);
+          f3r = *p3r;
+          f3i = *(p3r + 1);
+
+          t0r = f0r + f1r * w0r - f1i * w0i;
+          t0i = f0i + f1r * w0i + f1i * w0r;
+          f1r = f0r * Two - t0r;
+          f1i = f0i * Two - t0i;
+
+          f4r = *(p0r + pos);
+          f4i = *(p0r + posi);
+          f5r = *(p1r + pos);
+          f5i = *(p1r + posi);
+
+          f6r = *(p2r + pos);
+          f6i = *(p2r + posi);
+          f7r = *(p3r + pos);
+          f7i = *(p3r + posi);
+
+          t1r = f2r - f3r * w0r + f3i * w0i;
+          t1i = f2i - f3r * w0i - f3i * w0r;
+          f2r = f2r * Two - t1r;
+          f2i = f2i * Two - t1i;
+
+          f0r = t0r + f2r * w1r - f2i * w1i;
+          f0i = t0i + f2r * w1i + f2i * w1r;
+          f2r = t0r * Two - f0r;
+          f2i = t0i * Two - f0i;
+
+          f3r = f1r + t1r * w1i + t1i * w1r;
+          f3i = f1i - t1r * w1r + t1i * w1i;
+          f1r = f1r * Two - f3r;
+          f1i = f1i * Two - f3i;
+
+          t0r = f4r + f5r * w0r - f5i * w0i;
+          t0i = f4i + f5r * w0i + f5i * w0r;
+          f5r = f4r * Two - t0r;
+          f5i = f4i * Two - t0i;
+
+          t1r = f6r - f7r * w0r + f7i * w0i;
+          t1i = f6i - f7r * w0i - f7i * w0r;
+          f6r = f6r * Two - t1r;
+          f6i = f6i * Two - t1i;
+
+          f4r = t0r + f6r * w1r - f6i * w1i;
+          f4i = t0i + f6r * w1i + f6i * w1r;
+          f6r = t0r * Two - f4r;
+          f6i = t0i * Two - f4i;
+
+          f7r = f5r + t1r * w1i + t1i * w1r;
+          f7i = f5i - t1r * w1r + t1i * w1i;
+          f5r = f5r * Two - f7r;
+          f5i = f5i * Two - f7i;
+
+          t0r = f0r - f4r * w2r + f4i * w2i;
+          t0i = f0i - f4r * w2i - f4i * w2r;
+          f0r = f0r * Two - t0r;
+          f0i = f0i * Two - t0i;
+
+          t1r = f1r - f5r * w3r + f5i * w3i;
+          t1i = f1i - f5r * w3i - f5i * w3r;
+          f1r = f1r * Two - t1r;
+          f1i = f1i * Two - t1i;
+
+          *(p0r + pos) = t0r;
+          *(p0r + posi) = t0i;
+          *p0r = f0r;
+          *(p0r + 1) = f0i;
+
+          p0r += pnext;
+          f0r = *p0r;
+          f0i = *(p0r + 1);
+
+          *(p1r + pos) = t1r;
+          *(p1r + posi) = t1i;
+          *p1r = f1r;
+          *(p1r + 1) = f1i;
+
+          p1r += pnext;
+
+          f1r = *p1r;
+          f1i = *(p1r + 1);
+
+          f4r = f2r - f6r * w2i - f6i * w2r;
+          f4i = f2i + f6r * w2r - f6i * w2i;
+          f6r = f2r * Two - f4r;
+          f6i = f2i * Two - f4i;
+
+          f5r = f3r - f7r * w3i - f7i * w3r;
+          f5i = f3i + f7r * w3r - f7i * w3i;
+          f7r = f3r * Two - f5r;
+          f7i = f3i * Two - f5i;
+
+          *p2r = f4r;
+          *(p2r + 1) = f4i;
+          *(p2r + pos) = f6r;
+          *(p2r + posi) = f6i;
+
+          p2r += pnext;
+
+          *p3r = f5r;
+          *(p3r + 1) = f5i;
+          *(p3r + pos) = f7r;
+          *(p3r + posi) = f7i;
+
+          p3r += pnext;
+        }
+
+        f2r = *p2r;
+        f2i = *(p2r + 1);
+        f3r = *p3r;
+        f3i = *(p3r + 1);
+
+        t0r = f0r + f1r * w0r - f1i * w0i;
+        t0i = f0i + f1r * w0i + f1i * w0r;
+        f1r = f0r * Two - t0r;
+        f1i = f0i * Two - t0i;
+
+        f4r = *(p0r + pos);
+        f4i = *(p0r + posi);
+        f5r = *(p1r + pos);
+        f5i = *(p1r + posi);
+
+        f6r = *(p2r + pos);
+        f6i = *(p2r + posi);
+        f7r = *(p3r + pos);
+        f7i = *(p3r + posi);
+
+        t1r = f2r - f3r * w0r + f3i * w0i;
+        t1i = f2i - f3r * w0i - f3i * w0r;
+        f2r = f2r * Two - t1r;
+        f2i = f2i * Two - t1i;
+
+        f0r = t0r + f2r * w1r - f2i * w1i;
+        f0i = t0i + f2r * w1i + f2i * w1r;
+        f2r = t0r * Two - f0r;
+        f2i = t0i * Two - f0i;
+
+        f3r = f1r + t1r * w1i + t1i * w1r;
+        f3i = f1i - t1r * w1r + t1i * w1i;
+        f1r = f1r * Two - f3r;
+        f1i = f1i * Two - f3i;
+
+        if ((int) DiffUCnt == NDiffU / 2)
+          Uinc4 = -Uinc4;
+
+        u0r += Uinc4;
+        u0i -= Uinc4;
+        u1r += Uinc2;
+        u1i -= Uinc2;
+        u2r += Uinc;
+        u2i -= Uinc;
+
+        pstrt += 2;
+
+        t0r = f4r + f5r * w0r - f5i * w0i;
+        t0i = f4i + f5r * w0i + f5i * w0r;
+        f5r = f4r * Two - t0r;
+        f5i = f4i * Two - t0i;
+
+        t1r = f6r - f7r * w0r + f7i * w0i;
+        t1i = f6i - f7r * w0i - f7i * w0r;
+        f6r = f6r * Two - t1r;
+        f6i = f6i * Two - t1i;
+
+        f4r = t0r + f6r * w1r - f6i * w1i;
+        f4i = t0i + f6r * w1i + f6i * w1r;
+        f6r = t0r * Two - f4r;
+        f6i = t0i * Two - f4i;
+
+        f7r = f5r + t1r * w1i + t1i * w1r;
+        f7i = f5i - t1r * w1r + t1i * w1i;
+        f5r = f5r * Two - f7r;
+        f5i = f5i * Two - f7i;
+
+        w0r = *u0r;
+        w0i = *u0i;
+        w1r = *u1r;
+        w1i = *u1i;
+
+        if ((int) DiffUCnt <= NDiffU / 2)
+          w0r = -w0r;
+
+        t0r = f0r - f4r * w2r + f4i * w2i;
+        t0i = f0i - f4r * w2i - f4i * w2r;
+        f0r = f0r * Two - t0r;
+        f0i = f0i * Two - t0i;
+
+        f4r = f2r - f6r * w2i - f6i * w2r;
+        f4i = f2i + f6r * w2r - f6i * w2i;
+        f6r = f2r * Two - f4r;
+        f6i = f2i * Two - f4i;
+
+        *(p0r + pos) = t0r;
+        *p2r = f4r;
+        *(p0r + posi) = t0i;
+        *(p2r + 1) = f4i;
+        w2r = *u2r;
+        w2i = *u2i;
+        *p0r = f0r;
+        *(p2r + pos) = f6r;
+        *(p0r + 1) = f0i;
+        *(p2r + posi) = f6i;
+
+        p0r = pstrt;
+        p2r = pstrt + pinc + pinc;
+
+        t1r = f1r - f5r * w3r + f5i * w3i;
+        t1i = f1i - f5r * w3i - f5i * w3r;
+        f1r = f1r * Two - t1r;
+        f1i = f1i * Two - t1i;
+
+        f5r = f3r - f7r * w3i - f7i * w3r;
+        f5i = f3i + f7r * w3r - f7i * w3i;
+        f7r = f3r * Two - f5r;
+        f7i = f3i * Two - f5i;
+
+        *(p1r + pos) = t1r;
+        *p3r = f5r;
+        *(p1r + posi) = t1i;
+        *(p3r + 1) = f5i;
+        w3r = *(u2r + U2toU3);
+        w3i = *(u2i - U2toU3);
+        *p1r = f1r;
+        *(p3r + pos) = f7r;
+        *(p1r + 1) = f1i;
+        *(p3r + posi) = f7i;
+
+        p1r = pstrt + pinc;
+        p3r = p2r + pinc;
+      }
+      NSameU /= 8;
+      Uinc /= 8;
+      Uinc2 /= 8;
+      Uinc4 = Uinc * 4;
+      NDiffU *= 8;
+      pinc *= 8;
+      pnext *= 8;
+      pos *= 8;
+      posi = pos + 1;
+    }
+}
+
+static void ifftrecurs(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int Ustride,
+                       int NDiffU, int StageCnt)
+{
+    /* recursive bfstages calls to maximize on chip cache efficiency */
+    int i1;
+
+    if (M <= (int) MCACHE)
+      ibfstages(ioptr, M, Utbl, Ustride, NDiffU, StageCnt); /* RADIX 8 Stages */
+    else {
+      for (i1 = 0; i1 < 8; i1++) {
+        ifftrecurs(&ioptr[i1 * POW2(M - 3) * 2], M - 3, Utbl, 8 * Ustride,
+                   NDiffU, StageCnt - 1);           /*  RADIX 8 Stages       */
+      }
+      ibfstages(ioptr, M, Utbl, Ustride, POW2(M - 3), 1);   /* RADIX 8 Stage */
+    }
+}
+
+/* Paul: not currently used, so commented out */
+
+/* static void iffts1(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int16_t *BRLow) */
+/* { */
+/*     /\* Compute in-place inverse complex fft on the rows of the input array  *\/ */
+/*     /\* INPUTS                                                               *\/ */
+/*     /\*   *ioptr = input data array                                          *\/ */
+/*     /\*   M = log2 of fft size                                               *\/ */
+/*     /\*   *Utbl = cosine table                                               *\/ */
+/*     /\*   *BRLow = bit reversed counter table                                *\/ */
+/*     /\* OUTPUTS                                                              *\/ */
+/*     /\*   *ioptr = output data array                                         *\/ */
+
+/*     int StageCnt; */
+/*     int NDiffU; */
+/*     const SPFLOAT scale = 1.0 / POW2(M); */
+
+/*     switch (M) { */
+/*     case 0: */
+/*       break; */
+/*     case 1: */
+/*       ifft2pt(ioptr, scale);    /\* a 2 pt fft *\/ */
+/*       break; */
+/*     case 2: */
+/*       ifft4pt(ioptr, scale);    /\* a 4 pt fft *\/ */
+/*       break; */
+/*     case 3: */
+/*       ifft8pt(ioptr, scale);    /\* an 8 pt fft *\/ */
+/*       break; */
+/*     default: */
+/*       /\* bit reverse and first radix 2 stage *\/ */
+/*       scbitrevR2(ioptr, M, BRLow, scale); */
+/*       StageCnt = (M - 1) / 3;   /\* number of radix 8 stages *\/ */
+/*       NDiffU = 2;               /\* one radix 2 stage already complete *\/ */
+/*       if ((M - 1 - (StageCnt * 3)) == 1) { */
+/*         ibfR2(ioptr, M, NDiffU);        /\* 1 radix 2 stage *\/ */
+/*         NDiffU *= 2; */
+/*       } */
+/*       if ((M - 1 - (StageCnt * 3)) == 2) { */
+/*         ibfR4(ioptr, M, NDiffU);        /\* 1 radix 4 stage *\/ */
+/*         NDiffU *= 4; */
+/*       } */
+/*       if (M <= (int) MCACHE) */
+/*         ibfstages(ioptr, M, Utbl, 1, NDiffU, StageCnt);  /\* RADIX 8 Stages *\/ */
+/*       else */
+/*         ifftrecurs(ioptr, M, Utbl, 1, NDiffU, StageCnt); /\* RADIX 8 Stages *\/ */
+/*     } */
+/* } */
+
+/******************
+* parts of rffts1 *
+******************/
+
+static void rfft1pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 2 rfft     ***/
+    SPFLOAT f0r, f0i;
+    SPFLOAT t0r, t0i;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+
+    /* finish rfft */
+    t0r = f0r + f0i;
+    t0i = f0r - f0i;
+
+    /* store result */
+    ioptr[0] = t0r;
+    ioptr[1] = t0i;
+}
+
+static void rfft2pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 4 rfft     ***/
+    SPFLOAT f0r, f0i, f1r, f1i;
+    SPFLOAT t0r, t0i;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+    f1r = ioptr[2];
+    f1i = ioptr[3];
+
+    /* Butterflys           */
+    /*
+       f0   -       -       t0
+       f1   -  1 -  f1
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i + f1i;
+    f1r = f0r - f1r;
+    f1i = f1i - f0i;
+    /* finish rfft */
+    f0r = t0r + t0i;
+    f0i = t0r - t0i;
+
+    /* store result */
+    ioptr[0] = f0r;
+    ioptr[1] = f0i;
+    ioptr[2] = f1r;
+    ioptr[3] = f1i;
+}
+
+static void rfft4pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 8 rfft     ***/
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    SPFLOAT w0r = 1.0 / MYROOT2;    /* cos(pi/4)   */
+    const SPFLOAT Two = 2.0;
+    const SPFLOAT scale = 0.5;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+    f1r = ioptr[4];
+    f1i = ioptr[5];
+    f2r = ioptr[2];
+    f2i = ioptr[3];
+    f3r = ioptr[6];
+    f3i = ioptr[7];
+
+    /* Butterflys           */
+    /*
+       f0   -       -       t0      -       -       f0
+       f1   -  1 -  f1      -       -       f1
+       f2   -       -       f2      -  1 -  f2
+       f3   -  1 -  t1      - -i -  f3
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i + f1i;
+    f1r = f0r - f1r;
+    f1i = f0i - f1i;
+
+    t1r = f2r - f3r;
+    t1i = f2i - f3i;
+    f2r = f2r + f3r;
+    f2i = f2i + f3i;
+
+    f0r = t0r + f2r;
+    f0i = t0i + f2i;
+    f2r = t0r - f2r;
+    f2i = f2i - t0i;              /* neg for rfft */
+
+    f3r = f1r - t1i;
+    f3i = f1i + t1r;
+    f1r = f1r + t1i;
+    f1i = f1i - t1r;
+
+    /* finish rfft */
+    t0r = f0r + f0i;              /* compute Re(x[0]) */
+    t0i = f0r - f0i;              /* compute Re(x[N/2]) */
+
+    t1r = f1r + f3r;
+    t1i = f1i - f3i;
+    f0r = f1i + f3i;
+    f0i = f3r - f1r;
+
+    f1r = t1r + w0r * f0r + w0r * f0i;
+    f1i = t1i - w0r * f0r + w0r * f0i;
+    f3r = Two * t1r - f1r;
+    f3i = f1i - Two * t1i;
+
+    /* store result */
+    ioptr[4] = f2r;
+    ioptr[5] = f2i;
+    ioptr[0] = t0r;
+    ioptr[1] = t0i;
+
+    ioptr[2] = scale * f1r;
+    ioptr[3] = scale * f1i;
+    ioptr[6] = scale * f3r;
+    ioptr[7] = scale * f3i;
+}
+
+static void rfft8pt(SPFLOAT *ioptr)
+{
+    /***   RADIX 16 rfft    ***/
+    SPFLOAT w0r = 1.0 / MYROOT2;    /* cos(pi/4)   */
+    SPFLOAT w1r = MYCOSPID8;        /* cos(pi/8)     */
+    SPFLOAT w1i = MYSINPID8;        /* sin(pi/8)     */
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+    const SPFLOAT scale = 0.5;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+    f1r = ioptr[8];
+    f1i = ioptr[9];
+    f2r = ioptr[4];
+    f2i = ioptr[5];
+    f3r = ioptr[12];
+    f3i = ioptr[13];
+    f4r = ioptr[2];
+    f4i = ioptr[3];
+    f5r = ioptr[10];
+    f5i = ioptr[11];
+    f6r = ioptr[6];
+    f6i = ioptr[7];
+    f7r = ioptr[14];
+    f7i = ioptr[15];
+    /* Butterflys           */
+    /*
+       f0   -       -       t0      -       -       f0      -       -       f0
+       f1   -  1 -  f1      -       -       f1      -       -       f1
+       f2   -       -       f2      -  1 -  f2      -       -       f2
+       f3   -  1 -  t1      - -i -  f3      -       -       f3
+       f4   -       -       t0      -       -       f4      -  1 -  t0
+       f5   -  1 -  f5      -       -       f5      - w3 -  f4
+       f6   -       -       f6      -  1 -  f6      - -i -  t1
+       f7   -  1 -  t1      - -i -  f7      - iw3-  f6
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i + f1i;
+    f1r = f0r - f1r;
+    f1i = f0i - f1i;
+
+    t1r = f2r - f3r;
+    t1i = f2i - f3i;
+    f2r = f2r + f3r;
+    f2i = f2i + f3i;
+
+    f0r = t0r + f2r;
+    f0i = t0i + f2i;
+    f2r = t0r - f2r;
+    f2i = t0i - f2i;
+
+    f3r = f1r - t1i;
+    f3i = f1i + t1r;
+    f1r = f1r + t1i;
+    f1i = f1i - t1r;
+
+    t0r = f4r + f5r;
+    t0i = f4i + f5i;
+    f5r = f4r - f5r;
+    f5i = f4i - f5i;
+
+    t1r = f6r - f7r;
+    t1i = f6i - f7i;
+    f6r = f6r + f7r;
+    f6i = f6i + f7i;
+
+    f4r = t0r + f6r;
+    f4i = t0i + f6i;
+    f6r = t0r - f6r;
+    f6i = t0i - f6i;
+
+    f7r = f5r - t1i;
+    f7i = f5i + t1r;
+    f5r = f5r + t1i;
+    f5i = f5i - t1r;
+
+    t0r = f0r - f4r;
+    t0i = f4i - f0i;              /* neg for rfft */
+    f0r = f0r + f4r;
+    f0i = f0i + f4i;
+
+    t1r = f2r - f6i;
+    t1i = f2i + f6r;
+    f2r = f2r + f6i;
+    f2i = f2i - f6r;
+
+    f4r = f1r - f5r * w0r - f5i * w0r;
+    f4i = f1i + f5r * w0r - f5i * w0r;
+    f1r = f1r * Two - f4r;
+    f1i = f1i * Two - f4i;
+
+    f6r = f3r + f7r * w0r - f7i * w0r;
+    f6i = f3i + f7r * w0r + f7i * w0r;
+    f3r = f3r * Two - f6r;
+    f3i = f3i * Two - f6i;
+
+    /* finish rfft */
+    f5r = f0r + f0i;              /* compute Re(x[0]) */
+    f5i = f0r - f0i;              /* compute Re(x[N/2]) */
+
+    f0r = f2r + t1r;
+    f0i = f2i - t1i;
+    f7r = f2i + t1i;
+    f7i = t1r - f2r;
+
+    f2r = f0r + w0r * f7r + w0r * f7i;
+    f2i = f0i - w0r * f7r + w0r * f7i;
+    t1r = Two * f0r - f2r;
+    t1i = f2i - Two * f0i;
+
+    f0r = f1r + f6r;
+    f0i = f1i - f6i;
+    f7r = f1i + f6i;
+    f7i = f6r - f1r;
+
+    f1r = f0r + w1r * f7r + w1i * f7i;
+    f1i = f0i - w1i * f7r + w1r * f7i;
+    f6r = Two * f0r - f1r;
+    f6i = f1i - Two * f0i;
+
+    f0r = f3r + f4r;
+    f0i = f3i - f4i;
+    f7r = f3i + f4i;
+    f7i = f4r - f3r;
+
+    f3r = f0r + w1i * f7r + w1r * f7i;
+    f3i = f0i - w1r * f7r + w1i * f7i;
+    f4r = Two * f0r - f3r;
+    f4i = f3i - Two * f0i;
+
+    /* store result */
+    ioptr[8] = t0r;
+    ioptr[9] = t0i;
+    ioptr[0] = f5r;
+    ioptr[1] = f5i;
+
+    ioptr[4] = scale * f2r;
+    ioptr[5] = scale * f2i;
+    ioptr[12] = scale * t1r;
+    ioptr[13] = scale * t1i;
+
+    ioptr[2] = scale * f1r;
+    ioptr[3] = scale * f1i;
+    ioptr[6] = scale * f3r;
+    ioptr[7] = scale * f3i;
+    ioptr[10] = scale * f4r;
+    ioptr[11] = scale * f4i;
+    ioptr[14] = scale * f6r;
+    ioptr[15] = scale * f6i;
+}
+
+static void frstage(SPFLOAT *ioptr, int M, SPFLOAT *Utbl)
+{
+    /*      Finish RFFT             */
+
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int diffUcnt;
+
+    SPFLOAT *p0r, *p1r;
+    SPFLOAT *u0r, *u0i;
+
+    SPFLOAT w0r, w0i;
+    SPFLOAT f0r, f0i, f1r, f1i, f4r, f4i, f5r, f5i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    pos = POW2(M - 1);
+    posi = pos + 1;
+
+    p0r = ioptr;
+    p1r = ioptr + pos / 2;
+
+    u0r = Utbl + POW2(M - 3);
+
+    w0r = *u0r; f0r = *(p0r);
+    f0i = *(p0r + 1);
+    f4r = *(p0r + pos);
+    f4i = *(p0r + posi);
+    f1r = *(p1r);
+    f1i = *(p1r + 1);
+    f5r = *(p1r + pos);
+    f5i = *(p1r + posi);
+
+    t0r = Two * f0r + Two * f0i;  /* compute Re(x[0]) */
+    t0i = Two * f0r - Two * f0i;  /* compute Re(x[N/2]) */
+    t1r = f4r + f4r;
+    t1i = -f4i - f4i;
+
+    f0r = f1r + f5r;
+    f0i = f1i - f5i;
+    f4r = f1i + f5i;
+    f4i = f5r - f1r;
+
+    f1r = f0r + w0r * f4r + w0r * f4i;
+    f1i = f0i - w0r * f4r + w0r * f4i;
+    f5r = Two * f0r - f1r;
+    f5i = f1i - Two * f0i;
+
+    *(p0r) = t0r;
+    *(p0r + 1) = t0i;
+    *(p0r + pos) = t1r;
+    *(p0r + posi) = t1i;
+    *(p1r) = f1r;
+    *(p1r + 1) = f1i;
+    *(p1r + pos) = f5r;
+    *(p1r + posi) = f5i;
+
+    u0r = Utbl + 1;
+    u0i = Utbl + (POW2(M - 2) - 1);
+
+    w0r = *u0r; w0i = *u0i;
+
+    p0r = (ioptr + 2);
+    p1r = (ioptr + (POW2(M - 2) - 1) * 2);
+
+    /* Butterflys */
+    /*
+       f0   -       t0      -       -       f0
+       f5   -       t1      - w0    -       f5
+
+       f1   -       t0      -       -       f1
+       f4   -       t1      -iw0 -  f4
+     */
+
+    for (diffUcnt = POW2(M - 3) - 1; diffUcnt > 0; diffUcnt--) {
+
+      f0r = *(p0r);
+      f0i = *(p0r + 1);
+      f5r = *(p1r + pos);
+      f5i = *(p1r + posi);
+      f1r = *(p1r);
+      f1i = *(p1r + 1);
+      f4r = *(p0r + pos);
+      f4i = *(p0r + posi);
+
+      t0r = f0r + f5r;
+      t0i = f0i - f5i;
+      t1r = f0i + f5i;
+      t1i = f5r - f0r;
+
+      f0r = t0r + w0r * t1r + w0i * t1i;
+      f0i = t0i - w0i * t1r + w0r * t1i;
+      f5r = Two * t0r - f0r;
+      f5i = f0i - Two * t0i;
+
+      t0r = f1r + f4r;
+      t0i = f1i - f4i;
+      t1r = f1i + f4i;
+      t1i = f4r - f1r;
+
+      f1r = t0r + w0i * t1r + w0r * t1i;
+      f1i = t0i - w0r * t1r + w0i * t1i;
+      f4r = Two * t0r - f1r;
+      f4i = f1i - Two * t0i;
+
+      *(p0r) = f0r;
+      *(p0r + 1) = f0i;
+      *(p1r + pos) = f5r;
+      *(p1r + posi) = f5i;
+
+      w0r = *++u0r;
+      w0i = *--u0i;
+
+      *(p1r) = f1r;
+      *(p1r + 1) = f1i;
+      *(p0r + pos) = f4r;
+      *(p0r + posi) = f4i;
+
+      p0r += 2;
+      p1r -= 2;
+    }
+}
+
+static void rffts1(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int16_t *BRLow)
+{
+    /* Compute in-place real fft on the rows of the input array           */
+    /* The result is the complex spectra of the positive frequencies      */
+    /* except the location for the first complex number contains the real */
+    /* values for DC and Nyquest                                          */
+    /* INPUTS                                                             */
+    /*   *ioptr = real input data array                                   */
+    /*   M = log2 of fft size                                             */
+    /*   *Utbl = cosine table                                             */
+    /*   *BRLow = bit reversed counter table                              */
+    /* OUTPUTS                                                            */
+    /*   *ioptr = output data array   in the following order              */
+    /*     Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]),  */
+    /*     ... Re(x[N/2-1]), Im(x[N/2-1]).                                */
+
+    SPFLOAT scale;
+    int StageCnt;
+    int NDiffU;
+
+    M = M - 1;
+    switch (M) {
+    case -1:
+      break;
+    case 0:
+      rfft1pt(ioptr);           /* a 2 pt fft */
+      break;
+    case 1:
+      rfft2pt(ioptr);           /* a 4 pt fft */
+      break;
+    case 2:
+      rfft4pt(ioptr);           /* an 8 pt fft */
+      break;
+    case 3:
+      rfft8pt(ioptr);           /* a 16 pt fft */
+      break;
+    default:
+      scale = 0.5;
+      /* bit reverse and first radix 2 stage */
+      scbitrevR2(ioptr, M, BRLow, scale);
+      StageCnt = (M - 1) / 3;   /* number of radix 8 stages           */
+      NDiffU = 2;               /* one radix 2 stage already complete */
+      if ((M - 1 - (StageCnt * 3)) == 1) {
+        bfR2(ioptr, M, NDiffU); /* 1 radix 2 stage */
+        NDiffU *= 2;
+      }
+      if ((M - 1 - (StageCnt * 3)) == 2) {
+        bfR4(ioptr, M, NDiffU); /* 1 radix 4 stage */
+        NDiffU *= 4;
+      }
+      if (M <= (int) MCACHE)
+        bfstages(ioptr, M, Utbl, 2, NDiffU, StageCnt);  /* RADIX 8 Stages */
+      else
+        fftrecurs(ioptr, M, Utbl, 2, NDiffU, StageCnt); /* RADIX 8 Stages */
+      frstage(ioptr, M + 1, Utbl);
+    }
+}
+
+/*******************
+* parts of riffts1 *
+*******************/
+
+static void rifft1pt(SPFLOAT *ioptr, SPFLOAT scale)
+{
+    /***   RADIX 2 rifft    ***/
+    SPFLOAT f0r, f0i;
+    SPFLOAT t0r, t0i;
+
+    /* bit reversed load */
+    f0r = ioptr[0];
+    f0i = ioptr[1];
+
+    /* finish rfft */
+    t0r = f0r + f0i;
+    t0i = f0r - f0i;
+
+    /* store result */
+    ioptr[0] = scale * t0r;
+    ioptr[1] = scale * t0i;
+}
+
+static void rifft2pt(SPFLOAT *ioptr, SPFLOAT scale)
+{
+    /***   RADIX 4 rifft    ***/
+    SPFLOAT f0r, f0i, f1r, f1i;
+    SPFLOAT t0r, t0i;
+    const SPFLOAT Two = 2.0;
+
+    /* bit reversed load */
+    t0r = ioptr[0];
+    t0i = ioptr[1];
+    f1r = Two * ioptr[2];
+    f1i = Two * ioptr[3];
+
+    /* start rifft */
+    f0r = t0r + t0i;
+    f0i = t0r - t0i;
+    /* Butterflys           */
+    /*
+       f0   -       -       t0
+       f1   -  1 -  f1
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i - f1i;
+    f1r = f0r - f1r;
+    f1i = f0i + f1i;
+
+    /* store result */
+    ioptr[0] = scale * t0r;
+    ioptr[1] = scale * t0i;
+    ioptr[2] = scale * f1r;
+    ioptr[3] = scale * f1i;
+}
+
+static void rifft4pt(SPFLOAT *ioptr, SPFLOAT scale)
+{
+    /***   RADIX 8 rifft    ***/
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    SPFLOAT w0r = 1.0 / MYROOT2;    /* cos(pi/4)   */
+    const SPFLOAT Two = 2.0;
+
+    /* bit reversed load */
+    t0r = ioptr[0];
+    t0i = ioptr[1];
+    f2r = ioptr[2];
+    f2i = ioptr[3];
+    f1r = Two * ioptr[4];
+    f1i = Two * ioptr[5];
+    f3r = ioptr[6];
+    f3i = ioptr[7];
+
+    /* start rfft */
+    f0r = t0r + t0i;              /* compute Re(x[0]) */
+    f0i = t0r - t0i;              /* compute Re(x[N/2]) */
+
+    t1r = f2r + f3r;
+    t1i = f2i - f3i;
+    t0r = f2r - f3r;
+    t0i = f2i + f3i;
+
+    f2r = t1r - w0r * t0r - w0r * t0i;
+    f2i = t1i + w0r * t0r - w0r * t0i;
+    f3r = Two * t1r - f2r;
+    f3i = f2i - Two * t1i;
+
+    /* Butterflys           */
+    /*
+       f0   -       -       t0      -       -       f0
+       f1   -  1 -  f1      -       -       f1
+       f2   -       -       f2      -  1 -  f2
+       f3   -  1 -  t1      -  i -  f3
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i - f1i;
+    f1r = f0r - f1r;
+    f1i = f0i + f1i;
+
+    t1r = f2r - f3r;
+    t1i = f2i - f3i;
+    f2r = f2r + f3r;
+    f2i = f2i + f3i;
+
+    f0r = t0r + f2r;
+    f0i = t0i + f2i;
+    f2r = t0r - f2r;
+    f2i = t0i - f2i;
+
+    f3r = f1r + t1i;
+    f3i = f1i - t1r;
+    f1r = f1r - t1i;
+    f1i = f1i + t1r;
+
+    /* store result */
+    ioptr[0] = scale * f0r;
+    ioptr[1] = scale * f0i;
+    ioptr[2] = scale * f1r;
+    ioptr[3] = scale * f1i;
+    ioptr[4] = scale * f2r;
+    ioptr[5] = scale * f2i;
+    ioptr[6] = scale * f3r;
+    ioptr[7] = scale * f3i;
+}
+
+static void rifft8pt(SPFLOAT *ioptr, SPFLOAT scale)
+{
+    /***   RADIX 16 rifft   ***/
+    SPFLOAT w0r = (SPFLOAT) (1.0 / MYROOT2);    /* cos(pi/4)    */
+    SPFLOAT w1r = MYCOSPID8;                  /* cos(pi/8)    */
+    SPFLOAT w1i = MYSINPID8;                  /* sin(pi/8)    */
+    SPFLOAT f0r, f0i, f1r, f1i, f2r, f2i, f3r, f3i;
+    SPFLOAT f4r, f4i, f5r, f5i, f6r, f6i, f7r, f7i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    /* bit reversed load */
+    t0r = ioptr[0];
+    t0i = ioptr[1];
+    f4r = ioptr[2];
+    f4i = ioptr[3];
+    f2r = ioptr[4];
+    f2i = ioptr[5];
+    f6r = ioptr[6];
+    f6i = ioptr[7];
+    f1r = Two * ioptr[8];
+    f1i = Two * ioptr[9];
+    f5r = ioptr[10];
+    f5i = ioptr[11];
+    f3r = ioptr[12];
+    f3i = ioptr[13];
+    f7r = ioptr[14];
+    f7i = ioptr[15];
+
+    /* start rfft */
+    f0r = t0r + t0i;              /* compute Re(x[0]) */
+    f0i = t0r - t0i;              /* compute Re(x[N/2]) */
+
+    t0r = f2r + f3r;
+    t0i = f2i - f3i;
+    t1r = f2r - f3r;
+    t1i = f2i + f3i;
+
+    f2r = t0r - w0r * t1r - w0r * t1i;
+    f2i = t0i + w0r * t1r - w0r * t1i;
+    f3r = Two * t0r - f2r;
+    f3i = f2i - Two * t0i;
+
+    t0r = f4r + f7r;
+    t0i = f4i - f7i;
+    t1r = f4r - f7r;
+    t1i = f4i + f7i;
+
+    f4r = t0r - w1i * t1r - w1r * t1i;
+    f4i = t0i + w1r * t1r - w1i * t1i;
+    f7r = Two * t0r - f4r;
+    f7i = f4i - Two * t0i;
+
+    t0r = f6r + f5r;
+    t0i = f6i - f5i;
+    t1r = f6r - f5r;
+    t1i = f6i + f5i;
+
+    f6r = t0r - w1r * t1r - w1i * t1i;
+    f6i = t0i + w1i * t1r - w1r * t1i;
+    f5r = Two * t0r - f6r;
+    f5i = f6i - Two * t0i;
+
+    /* Butterflys           */
+    /*
+       f0   -       -       t0      -       -       f0      -       -       f0
+       f1*  -  1 -  f1      -       -       f1      -       -       f1
+       f2   -       -       f2      -  1 -  f2      -       -       f2
+       f3   -  1 -  t1      -  i -  f3      -       -       f3
+       f4   -       -       t0      -       -       f4      -  1 -  t0
+       f5   -  1 -  f5      -       -       f5      - w3 -  f4
+       f6   -       -       f6      -  1 -  f6      -  i -  t1
+       f7   -  1 -  t1      -  i -  f7      - iw3-  f6
+     */
+
+    t0r = f0r + f1r;
+    t0i = f0i - f1i;
+    f1r = f0r - f1r;
+    f1i = f0i + f1i;
+
+    t1r = f2r - f3r;
+    t1i = f2i - f3i;
+    f2r = f2r + f3r;
+    f2i = f2i + f3i;
+
+    f0r = t0r + f2r;
+    f0i = t0i + f2i;
+    f2r = t0r - f2r;
+    f2i = t0i - f2i;
+
+    f3r = f1r + t1i;
+    f3i = f1i - t1r;
+    f1r = f1r - t1i;
+    f1i = f1i + t1r;
+
+    t0r = f4r + f5r;
+    t0i = f4i + f5i;
+    f5r = f4r - f5r;
+    f5i = f4i - f5i;
+
+    t1r = f6r - f7r;
+    t1i = f6i - f7i;
+    f6r = f6r + f7r;
+    f6i = f6i + f7i;
+
+    f4r = t0r + f6r;
+    f4i = t0i + f6i;
+    f6r = t0r - f6r;
+    f6i = t0i - f6i;
+
+    f7r = f5r + t1i;
+    f7i = f5i - t1r;
+    f5r = f5r - t1i;
+    f5i = f5i + t1r;
+
+    t0r = f0r - f4r;
+    t0i = f0i - f4i;
+    f0r = f0r + f4r;
+    f0i = f0i + f4i;
+
+    t1r = f2r + f6i;
+    t1i = f2i - f6r;
+    f2r = f2r - f6i;
+    f2i = f2i + f6r;
+
+    f4r = f1r - f5r * w0r + f5i * w0r;
+    f4i = f1i - f5r * w0r - f5i * w0r;
+    f1r = f1r * Two - f4r;
+    f1i = f1i * Two - f4i;
+
+    f6r = f3r + f7r * w0r + f7i * w0r;
+    f6i = f3i - f7r * w0r + f7i * w0r;
+    f3r = f3r * Two - f6r;
+    f3i = f3i * Two - f6i;
+
+    /* store result */
+    ioptr[0] = scale * f0r;
+    ioptr[1] = scale * f0i;
+    ioptr[2] = scale * f1r;
+    ioptr[3] = scale * f1i;
+    ioptr[4] = scale * f2r;
+    ioptr[5] = scale * f2i;
+    ioptr[6] = scale * f3r;
+    ioptr[7] = scale * f3i;
+    ioptr[8] = scale * t0r;
+    ioptr[9] = scale * t0i;
+    ioptr[10] = scale * f4r;
+    ioptr[11] = scale * f4i;
+    ioptr[12] = scale * t1r;
+    ioptr[13] = scale * t1i;
+    ioptr[14] = scale * f6r;
+    ioptr[15] = scale * f6i;
+}
+
+static void ifrstage(SPFLOAT *ioptr, int M, SPFLOAT *Utbl)
+{
+    /*      Start RIFFT             */
+
+    unsigned int pos;
+    unsigned int posi;
+    unsigned int diffUcnt;
+
+    SPFLOAT *p0r, *p1r;
+    SPFLOAT *u0r, *u0i;
+
+    SPFLOAT w0r, w0i;
+    SPFLOAT f0r, f0i, f1r, f1i, f4r, f4i, f5r, f5i;
+    SPFLOAT t0r, t0i, t1r, t1i;
+    const SPFLOAT Two = 2.0;
+
+    pos = POW2(M - 1);
+    posi = pos + 1;
+
+    p0r = ioptr;
+    p1r = ioptr + pos / 2;
+
+    u0r = Utbl + POW2(M - 3);
+
+    w0r = *u0r; f0r = *(p0r);
+    f0i = *(p0r + 1);
+    f4r = *(p0r + pos);
+    f4i = *(p0r + posi);
+    f1r = *(p1r);
+    f1i = *(p1r + 1);
+    f5r = *(p1r + pos);
+    f5i = *(p1r + posi);
+
+    t0r = f0r + f0i;
+    t0i = f0r - f0i;
+    t1r = f4r + f4r;
+    t1i = -f4i - f4i;
+
+    f0r = f1r + f5r;
+    f0i = f1i - f5i;
+    f4r = f1r - f5r;
+    f4i = f1i + f5i;
+
+    f1r = f0r - w0r * f4r - w0r * f4i;
+    f1i = f0i + w0r * f4r - w0r * f4i;
+    f5r = Two * f0r - f1r;
+    f5i = f1i - Two * f0i;
+
+    *(p0r) = t0r;
+    *(p0r + 1) = t0i;
+    *(p0r + pos) = t1r;
+    *(p0r + posi) = t1i;
+    *(p1r) = f1r;
+    *(p1r + 1) = f1i;
+    *(p1r + pos) = f5r;
+    *(p1r + posi) = f5i;
+
+    u0r = Utbl + 1;
+    u0i = Utbl + (POW2(M - 2) - 1);
+
+    w0r = *u0r; w0i = *u0i;
+
+    p0r = (ioptr + 2);
+    p1r = (ioptr + (POW2(M - 2) - 1) * 2);
+
+    /* Butterflys */
+    /*
+       f0   -        t0             -       f0
+       f1   -     t1     -w0-   f1
+
+       f2   -        t0             -       f2
+       f3   -     t1           -iw0-  f3
+     */
+
+    for (diffUcnt = POW2(M - 3) - 1; diffUcnt > 0; diffUcnt--) {
+
+      f0r = *(p0r);
+      f0i = *(p0r + 1);
+      f5r = *(p1r + pos);
+      f5i = *(p1r + posi);
+      f1r = *(p1r);
+      f1i = *(p1r + 1);
+      f4r = *(p0r + pos);
+      f4i = *(p0r + posi);
+
+      t0r = f0r + f5r;
+      t0i = f0i - f5i;
+      t1r = f0r - f5r;
+      t1i = f0i + f5i;
+
+      f0r = t0r - w0i * t1r - w0r * t1i;
+      f0i = t0i + w0r * t1r - w0i * t1i;
+      f5r = Two * t0r - f0r;
+      f5i = f0i - Two * t0i;
+
+      t0r = f1r + f4r;
+      t0i = f1i - f4i;
+      t1r = f1r - f4r;
+      t1i = f1i + f4i;
+
+      f1r = t0r - w0r * t1r - w0i * t1i;
+      f1i = t0i + w0i * t1r - w0r * t1i;
+      f4r = Two * t0r - f1r;
+      f4i = f1i - Two * t0i;
+
+      *(p0r) = f0r;
+      *(p0r + 1) = f0i;
+      *(p1r + pos) = f5r;
+      *(p1r + posi) = f5i;
+
+      w0r = *++u0r;
+      w0i = *--u0i;
+
+      *(p1r) = f1r;
+      *(p1r + 1) = f1i;
+      *(p0r + pos) = f4r;
+      *(p0r + posi) = f4i;
+
+      p0r += 2;
+      p1r -= 2;
+    }
+}
+
+static void riffts1(SPFLOAT *ioptr, int M, SPFLOAT *Utbl, int16_t *BRLow)
+{
+    /* Compute in-place real ifft on the rows of the input array    */
+    /* data order as from rffts1                                    */
+    /* INPUTS                                                       */
+    /*   *ioptr = input data array in the following order           */
+    /*   M = log2 of fft size                                       */
+    /*   Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]),                  */
+    /*   Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]).        */
+    /*   *Utbl = cosine table                                       */
+    /*   *BRLow = bit reversed counter table                        */
+    /* OUTPUTS                                                      */
+    /*   *ioptr = real output data array                            */
+
+    SPFLOAT scale;
+    int StageCnt;
+    int NDiffU;
+
+    scale = (SPFLOAT)(1.0 / (double)((int)POW2(M)));
+    M = M - 1;
+    switch (M) {
+    case -1:
+      break;
+    case 0:
+      rifft1pt(ioptr, scale);   /* a 2 pt fft */
+      break;
+    case 1:
+      rifft2pt(ioptr, scale);   /* a 4 pt fft */
+      break;
+    case 2:
+      rifft4pt(ioptr, scale);   /* an 8 pt fft */
+      break;
+    case 3:
+      rifft8pt(ioptr, scale);   /* a 16 pt fft */
+      break;
+    default:
+      ifrstage(ioptr, M + 1, Utbl);
+      /* bit reverse and first radix 2 stage */
+      scbitrevR2(ioptr, M, BRLow, scale);
+      StageCnt = (M - 1) / 3;   /* number of radix 8 stages           */
+      NDiffU = 2;               /* one radix 2 stage already complete */
+      if ((M - 1 - (StageCnt * 3)) == 1) {
+        ibfR2(ioptr, M, NDiffU);        /* 1 radix 2 stage */
+        NDiffU *= 2;
+      }
+      if ((M - 1 - (StageCnt * 3)) == 2) {
+        ibfR4(ioptr, M, NDiffU);        /* 1 radix 4 stage */
+        NDiffU *= 4;
+      }
+      if (M <= (int) MCACHE)
+        ibfstages(ioptr, M, Utbl, 2, NDiffU, StageCnt); /*  RADIX 8 Stages */
+      else
+        ifftrecurs(ioptr, M, Utbl, 2, NDiffU, StageCnt); /* RADIX 8 Stages */
+    }
+}
--- /dev/null
+++ b/lib/fft/sp_fft.c
@@ -1,0 +1,52 @@
+/* this file is placed in the public domain */
+
+void sp_fft_init(sp_fft *fft, int M)
+{
+    SPFLOAT *utbl;
+    int16_t *BRLow;
+    int16_t *BRLowCpx;
+
+    /* init cos table */
+    utbl = (SPFLOAT*) malloc((POW2(M) / 4 + 1) * sizeof(SPFLOAT));
+    fftCosInit(M, utbl);
+
+    BRLowCpx =
+      (int16_t*) malloc(POW2(M / 2 - 1) * sizeof(int16_t));
+    fftBRInit(M, BRLowCpx);
+
+    /* init bit reversed table for real FFT */
+     BRLow =
+      (int16_t*) malloc(POW2((M - 1) / 2 - 1) * sizeof(int16_t));
+    fftBRInit(M - 1, BRLow);
+
+    fft->BRLow = BRLow;
+    fft->BRLowCpx = BRLowCpx;
+    fft->utbl = utbl;
+}
+
+void sp_fftr(sp_fft *fft, SPFLOAT *buf, int FFTsize)
+{
+    int M = log2(FFTsize);
+    rffts1(buf, M, fft->utbl, fft->BRLow);
+}
+
+void sp_fft_cpx(sp_fft *fft, SPFLOAT *buf, int FFTsize)
+{
+    int M = log2(FFTsize);
+    ffts1(buf, M, fft->utbl, fft->BRLowCpx);
+}
+
+
+
+void sp_ifftr(sp_fft *fft, SPFLOAT *buf, int FFTsize)
+{
+    int M = log2(FFTsize);
+    riffts1(buf, M, fft->utbl, fft->BRLow);
+}
+
+void sp_fft_destroy(sp_fft *fft)
+{
+    free(fft->utbl);
+    free(fft->BRLow);
+    free(fft->BRLowCpx);
+}
--- /dev/null
+++ b/lib/inih/LICENSE.txt
@@ -1,0 +1,27 @@
+
+The "inih" library is distributed under the New BSD license:
+
+Copyright (c) 2009, Ben Hoyt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the name of Ben Hoyt nor the names of its contributors
+      may be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY BEN HOYT ''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 BEN HOYT 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.
--- /dev/null
+++ b/lib/inih/Makefile
@@ -1,0 +1,5 @@
+LPATHS += lib/inih/ini.o
+CFLAGS += -Ilib/inih/
+
+inih/ini.o: lib/inih/ini.c
+	$(CC) $(CFLAGS) $< -c -o $@
--- /dev/null
+++ b/lib/inih/ini.c
@@ -1,0 +1,185 @@
+/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "ini.h"
+
+#if !INI_USE_STACK
+#include <stdlib.h>
+#endif
+
+#define MAX_SECTION 50
+#define MAX_NAME 50
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+static char* rstrip(char* s)
+{
+    char* p = s + strlen(s);
+    while (p > s && isspace((unsigned char)(*--p)))
+        *p = '\0';
+    return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+static char* lskip(const char* s)
+{
+    while (*s && isspace((unsigned char)(*s)))
+        s++;
+    return (char*)s;
+}
+
+/* Return pointer to first char c or ';' comment in given string, or pointer to
+   null at end of string if neither found. ';' must be prefixed by a whitespace
+   character to register as a comment. */
+static char* find_char_or_comment(const char* s, char c)
+{
+    int was_whitespace = 0;
+    while (*s && *s != c && !(was_whitespace && *s == ';')) {
+        was_whitespace = isspace((unsigned char)(*s));
+        s++;
+    }
+    return (char*)s;
+}
+
+/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
+static char* strncpy0(char* dest, const char* src, size_t size)
+{
+    strncpy(dest, src, size);
+    dest[size - 1] = '\0';
+    return dest;
+}
+
+/* See documentation in header file. */
+int ini_parse_file(FILE* file,
+                   int (*handler)(void*, const char*, const char*,
+                                  const char*),
+                   void* user)
+{
+    /* Uses a fair bit of stack (use heap instead if you need to) */
+#if INI_USE_STACK
+    char line[INI_MAX_LINE];
+#else
+    char* line;
+#endif
+    char section[MAX_SECTION] = "";
+    char prev_name[MAX_NAME] = "";
+
+    char* start;
+    char* end;
+    char* name;
+    char* value;
+    int lineno = 0;
+    int error = 0;
+
+#if !INI_USE_STACK
+    line = (char*)malloc(INI_MAX_LINE);
+    if (!line) {
+        return -2;
+    }
+#endif
+
+    /* Scan through file line by line */
+    while (fgets(line, INI_MAX_LINE, file) != NULL) {
+        lineno++;
+
+        start = line;
+#if INI_ALLOW_BOM
+        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
+                           (unsigned char)start[1] == 0xBB &&
+                           (unsigned char)start[2] == 0xBF) {
+            start += 3;
+        }
+#endif
+        start = lskip(rstrip(start));
+
+        if (*start == ';' || *start == '#') {
+            /* Per Python ConfigParser, allow '#' comments at start of line */
+        }
+#if INI_ALLOW_MULTILINE
+        else if (*prev_name && *start && start > line) {
+            /* Non-black line with leading whitespace, treat as continuation
+               of previous name's value (as per Python ConfigParser). */
+            if (!handler(user, section, prev_name, start) && !error)
+                error = lineno;
+        }
+#endif
+        else if (*start == '[') {
+            /* A "[section]" line */
+            end = find_char_or_comment(start + 1, ']');
+            if (*end == ']') {
+                *end = '\0';
+                strncpy0(section, start + 1, sizeof(section));
+                *prev_name = '\0';
+            }
+            else if (!error) {
+                /* No ']' found on section line */
+                error = lineno;
+            }
+        }
+        else if (*start && *start != ';') {
+            /* Not a comment, must be a name[=:]value pair */
+            end = find_char_or_comment(start, '=');
+            if (*end != '=') {
+                end = find_char_or_comment(start, ':');
+            }
+            if (*end == '=' || *end == ':') {
+                *end = '\0';
+                name = rstrip(start);
+                value = lskip(end + 1);
+                end = find_char_or_comment(value, '\0');
+                if (*end == ';')
+                    *end = '\0';
+                rstrip(value);
+
+                /* Valid name[=:]value pair found, call handler */
+                strncpy0(prev_name, name, sizeof(prev_name));
+                if (!handler(user, section, name, value) && !error)
+                    error = lineno;
+            }
+            else if (!error) {
+                /* No '=' or ':' found on name[=:]value line */
+                error = lineno;
+            }
+        }
+
+#if INI_STOP_ON_FIRST_ERROR
+        if (error)
+            break;
+#endif
+    }
+
+#if !INI_USE_STACK
+    free(line);
+#endif
+
+    return error;
+}
+
+/* See documentation in header file. */
+int ini_parse(const char* filename,
+              int (*handler)(void*, const char*, const char*, const char*),
+              void* user)
+{
+    FILE* file;
+    int error;
+
+    file = fopen(filename, "r");
+    if (!file)
+        return -1;
+    error = ini_parse_file(file, handler, user);
+    fclose(file);
+    return error;
+}
--- /dev/null
+++ b/lib/inih/ini.h
@@ -1,0 +1,77 @@
+/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#ifndef __INI_H__
+#define __INI_H__
+
+/* Make this header file easier to include in C++ code */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/* Parse given INI-style file. May have [section]s, name=value pairs
+   (whitespace stripped), and comments starting with ';' (semicolon). Section
+   is "" if name=value pair parsed before any section heading. name:value
+   pairs are also supported as a concession to Python's ConfigParser.
+
+   For each name=value pair parsed, call handler function with given user
+   pointer as well as section, name, and value (data only valid for duration
+   of handler call). Handler should return nonzero on success, zero on error.
+
+   Returns 0 on success, line number of first error on parse error (doesn't
+   stop on first error), -1 on file open error, or -2 on memory allocation
+   error (only when INI_USE_STACK is zero).
+*/
+int ini_parse(const char* filename,
+              int (*handler)(void* user, const char* section,
+                             const char* name, const char* value),
+              void* user);
+
+/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
+   close the file when it's finished -- the caller must do that. */
+int ini_parse_file(FILE* file,
+                   int (*handler)(void* user, const char* section,
+                                  const char* name, const char* value),
+                   void* user);
+
+/* Nonzero to allow multi-line value parsing, in the style of Python's
+   ConfigParser. If allowed, ini_parse() will call the handler with the same
+   name for each subsequent line parsed. */
+#ifndef INI_ALLOW_MULTILINE
+#define INI_ALLOW_MULTILINE 1
+#endif
+
+/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
+   the file. See http://code.google.com/p/inih/issues/detail?id=21 */
+#ifndef INI_ALLOW_BOM
+#define INI_ALLOW_BOM 1
+#endif
+
+/* Nonzero to use stack, zero to use heap (malloc/free). */
+#ifndef INI_USE_STACK
+#define INI_USE_STACK 1
+#endif
+
+/* Stop parsing on first error (default is to keep parsing). */
+#ifndef INI_STOP_ON_FIRST_ERROR
+#define INI_STOP_ON_FIRST_ERROR 0
+#endif
+
+/* Maximum line length for any line in INI file. */
+#ifndef INI_MAX_LINE
+#define INI_MAX_LINE 200
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INI_H__ */
--- /dev/null
+++ b/lib/kissfft/COPYING
@@ -1,0 +1,11 @@
+Copyright (c) 2003-2010 Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
--- /dev/null
+++ b/lib/kissfft/Makefile
@@ -1,0 +1,8 @@
+LPATHS += lib/kissfft/kiss_fft.o lib/kissfft/kiss_fftr.o
+CFLAGS += -Ilib/kissfft/ -Dkiss_fft_scalar=$(SPFLOAT)
+
+lib/kissfft/kiss_fft.o: lib/kissfft/kiss_fft.c
+	$(C89) $< -c $(CFLAGS) -o $@
+
+lib/kissfft/kiss_fftr.o: lib/kissfft/kiss_fftr.c
+	$(C89) $< -c $(CFLAGS) -o $@
--- /dev/null
+++ b/lib/kissfft/README
@@ -1,0 +1,134 @@
+KISS FFT - A mixed-radix Fast Fourier Transform based up on the principle, 
+"Keep It Simple, Stupid."
+
+    There are many great fft libraries already around.  Kiss FFT is not trying
+to be better than any of them.  It only attempts to be a reasonably efficient, 
+moderately useful FFT that can use fixed or floating data types and can be 
+incorporated into someone's C program in a few minutes with trivial licensing.
+
+USAGE:
+
+    The basic usage for 1-d complex FFT is:
+
+        #include "kiss_fft.h"
+
+        kiss_fft_cfg cfg = kiss_fft_alloc( nfft ,is_inverse_fft ,0,0 );
+
+        while ...
+        
+            ... // put kth sample in cx_in[k].r and cx_in[k].i
+            
+            kiss_fft( cfg , cx_in , cx_out );
+            
+            ... // transformed. DC is in cx_out[0].r and cx_out[0].i 
+            
+        free(cfg);
+
+    Note: frequency-domain data is stored from dc up to 2pi.
+    so cx_out[0] is the dc bin of the FFT
+    and cx_out[nfft/2] is the Nyquist bin (if exists)
+
+    Declarations are in "kiss_fft.h", along with a brief description of the 
+functions you'll need to use. 
+
+Code definitions for 1d complex FFTs are in kiss_fft.c.
+
+You can do other cool stuff with the extras you'll find in tools/
+
+    * multi-dimensional FFTs 
+    * real-optimized FFTs  (returns the positive half-spectrum: (nfft/2+1) complex frequency bins)
+    * fast convolution FIR filtering (not available for fixed point)
+    * spectrum image creation
+
+The core fft and most tools/ code can be compiled to use float, double 
+or Q15 short samples. The default is float.
+
+
+BACKGROUND:
+
+    I started coding this because I couldn't find a fixed point FFT that didn't 
+use assembly code.  I started with floating point numbers so I could get the 
+theory straight before working on fixed point issues.  In the end, I had a 
+little bit of code that could be recompiled easily to do ffts with short, float
+or double (other types should be easy too).  
+
+    Once I got my FFT working, I was curious about the speed compared to
+a well respected and highly optimized fft library.  I don't want to criticize 
+this great library, so let's call it FFT_BRANDX.
+During this process, I learned:
+
+    1. FFT_BRANDX has more than 100K lines of code. The core of kiss_fft is about 500 lines (cpx 1-d).
+    2. It took me an embarrassingly long time to get FFT_BRANDX working.
+    3. A simple program using FFT_BRANDX is 522KB. A similar program using kiss_fft is 18KB (without optimizing for size).
+    4. FFT_BRANDX is roughly twice as fast as KISS FFT in default mode.
+
+    It is wonderful that free, highly optimized libraries like FFT_BRANDX exist.
+But such libraries carry a huge burden of complexity necessary to extract every 
+last bit of performance.
+
+    Sometimes simpler is better, even if it's not better.
+
+FREQUENTLY ASKED QUESTIONS:
+	Q: Can I use kissfft in a project with a ___ license?
+	A: Yes.  See LICENSE below.
+
+	Q: Why don't I get the output I expect?
+	A: The two most common causes of this are 
+		1) scaling : is there a constant multiplier between what you got and what you want?
+		2) mixed build environment -- all code must be compiled with same preprocessor 
+		definitions for FIXED_POINT and kiss_fft_scalar
+
+	Q: Will you write/debug my code for me?
+	A: Probably not unless you pay me.  I am happy to answer pointed and topical questions, but 
+	I may refer you to a book, a forum, or some other resource.
+
+
+PERFORMANCE:
+    (on Athlon XP 2100+, with gcc 2.96, float data type)
+
+    Kiss performed 10000 1024-pt cpx ffts in .63 s of cpu time.
+    For comparison, it took md5sum twice as long to process the same amount of data.
+
+    Transforming 5 minutes of CD quality audio takes less than a second (nfft=1024). 
+
+DO NOT:
+    ... use Kiss if you need the Fastest Fourier Transform in the World
+    ... ask me to add features that will bloat the code
+
+UNDER THE HOOD:
+
+    Kiss FFT uses a time decimation, mixed-radix, out-of-place FFT. If you give it an input buffer  
+    and output buffer that are the same, a temporary buffer will be created to hold the data.
+
+    No static data is used.  The core routines of kiss_fft are thread-safe (but not all of the tools directory).
+
+    No scaling is done for the floating point version (for speed).  
+    Scaling is done both ways for the fixed-point version (for overflow prevention).
+
+    Optimized butterflies are used for factors 2,3,4, and 5. 
+
+    The real (i.e. not complex) optimization code only works for even length ffts.  It does two half-length
+    FFTs in parallel (packed into real&imag), and then combines them via twiddling.  The result is 
+    nfft/2+1 complex frequency bins from DC to Nyquist.  If you don't know what this means, search the web.
+
+    The fast convolution filtering uses the overlap-scrap method, slightly 
+    modified to put the scrap at the tail.
+
+LICENSE:
+    Revised BSD License, see COPYING for verbiage. 
+    Basically, "free to use&change, give credit where due, no guarantees"
+    Note this license is compatible with GPL at one end of the spectrum and closed, commercial software at 
+    the other end.  See http://www.fsf.org/licensing/licenses
+
+    A commercial license is available which removes the requirement for attribution.  Contact me for details.
+
+  
+TODO:
+    *) Add real optimization for odd length FFTs 
+    *) Document/revisit the input/output fft scaling
+    *) Make doc describing the overlap (tail) scrap fast convolution filtering in kiss_fastfir.c
+    *) Test all the ./tools/ code with fixed point (kiss_fastfir.c doesn't work, maybe others)
+
+AUTHOR:
+    Mark Borgerding
+    Mark@Borgerding.net
--- /dev/null
+++ b/lib/kissfft/_kiss_fft_guts.h
@@ -1,0 +1,168 @@
+/*
+Copyright (c) 2003-2010, Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+*/
+
+/* kiss_fft.h
+   defines kiss_fft_scalar as either short or a float type
+   and defines
+   typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
+#include "kiss_fft.h"
+#include <limits.h>
+
+#define MAXFACTORS 32
+/* e.g. an fft of length 128 has 4 factors
+ as far as kissfft is concerned
+ 4*4*4*2
+ */
+
+#ifndef SOUNDPIPE_H
+
+struct kiss_fft_state{
+    int nfft;
+    int inverse;
+    int factors[2*MAXFACTORS];
+    kiss_fft_cpx twiddles[1];
+};
+
+#endif
+
+/*
+  Explanation of macros dealing with complex math:
+
+   C_MUL(m,a,b)         : m = a*b
+   C_FIXDIV( c , div )  : if a fixed point impl., c /= div. noop otherwise
+   C_SUB( res, a,b)     : res = a - b
+   C_SUBFROM( res , a)  : res -= a
+   C_ADDTO( res , a)    : res += a
+ * */
+#ifdef FIXED_POINT
+#if (FIXED_POINT==32)
+# define FRACBITS 31
+# define SAMPPROD int64_t
+#define SAMP_MAX 2147483647
+#else
+# define FRACBITS 15
+# define SAMPPROD int32_t
+#define SAMP_MAX 32767
+#endif
+
+#define SAMP_MIN -SAMP_MAX
+
+#if defined(CHECK_OVERFLOW)
+#  define CHECK_OVERFLOW_OP(a,op,b)  \
+	if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
+		fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) );  }
+#endif
+
+
+#   define smul(a,b) ( (SAMPPROD)(a)*(b) )
+#   define sround( x )  (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
+
+#   define S_MUL(a,b) sround( smul(a,b) )
+
+#   define C_MUL(m,a,b) \
+      do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
+          (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
+
+#   define DIVSCALAR(x,k) \
+	(x) = sround( smul(  x, SAMP_MAX/k ) )
+
+#   define C_FIXDIV(c,div) \
+	do {    DIVSCALAR( (c).r , div);  \
+		DIVSCALAR( (c).i  , div); }while (0)
+
+#   define C_MULBYSCALAR( c, s ) \
+    do{ (c).r =  sround( smul( (c).r , s ) ) ;\
+        (c).i =  sround( smul( (c).i , s ) ) ; }while(0)
+
+#else  /* not FIXED_POINT*/
+
+#   define S_MUL(a,b) ( (a)*(b) )
+#define C_MUL(m,a,b) \
+    do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
+        (m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
+#   define C_FIXDIV(c,div) /* NOOP */
+#   define C_MULBYSCALAR( c, s ) \
+    do{ (c).r *= (s);\
+        (c).i *= (s); }while(0)
+#endif
+
+#ifndef CHECK_OVERFLOW_OP
+#  define CHECK_OVERFLOW_OP(a,op,b) /* noop */
+#endif
+
+#define  C_ADD( res, a,b)\
+    do { \
+	    CHECK_OVERFLOW_OP((a).r,+,(b).r)\
+	    CHECK_OVERFLOW_OP((a).i,+,(b).i)\
+	    (res).r=(a).r+(b).r;  (res).i=(a).i+(b).i; \
+    }while(0)
+#define  C_SUB( res, a,b)\
+    do { \
+	    CHECK_OVERFLOW_OP((a).r,-,(b).r)\
+	    CHECK_OVERFLOW_OP((a).i,-,(b).i)\
+	    (res).r=(a).r-(b).r;  (res).i=(a).i-(b).i; \
+    }while(0)
+#define C_ADDTO( res , a)\
+    do { \
+	    CHECK_OVERFLOW_OP((res).r,+,(a).r)\
+	    CHECK_OVERFLOW_OP((res).i,+,(a).i)\
+	    (res).r += (a).r;  (res).i += (a).i;\
+    }while(0)
+
+#define C_SUBFROM( res , a)\
+    do {\
+	    CHECK_OVERFLOW_OP((res).r,-,(a).r)\
+	    CHECK_OVERFLOW_OP((res).i,-,(a).i)\
+	    (res).r -= (a).r;  (res).i -= (a).i; \
+    }while(0)
+
+
+#ifdef FIXED_POINT
+#  define KISS_FFT_COS(phase)  floor(.5+SAMP_MAX * cos (phase))
+#  define KISS_FFT_SIN(phase)  floor(.5+SAMP_MAX * sin (phase))
+#  define HALF_OF(x) ((x)>>1)
+#elif defined(USE_SIMD)
+#  define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
+#  define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
+#  define HALF_OF(x) ((x)*_mm_set1_ps(.5))
+#else
+#  define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
+#  define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
+#  define HALF_OF(x) ((x)*.5)
+#endif
+
+#define  kf_cexp(x,phase) \
+	do{ \
+		(x)->r = KISS_FFT_COS(phase);\
+		(x)->i = KISS_FFT_SIN(phase);\
+	}while(0)
+
+
+/* a debugging function */
+#define pcpx(c)\
+    fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
+
+
+#ifdef KISS_FFT_USE_ALLOCA
+// define this to allow use of alloca instead of malloc for temporary buffers
+// Temporary buffers are used in two case:
+// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
+// 2. "in-place" FFTs.  Notice the quotes, since kissfft does not really do an in-place transform.
+#include <alloca.h>
+#define  KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
+#define  KISS_FFT_TMP_FREE(ptr)
+#else
+#define  KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
+#define  KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
+#endif
--- /dev/null
+++ b/lib/kissfft/kiss_fft.c
@@ -1,0 +1,409 @@
+/*
+Copyright (c) 2003-2010, Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+*/
+
+
+#include "_kiss_fft_guts.h"
+/* The guts header contains all the multiplication and addition macros that are defined for
+ fixed or floating point complex numbers.  It also delares the kf_ internal functions.
+ */
+
+static void kf_bfly2(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        int m
+        )
+{
+    kiss_fft_cpx * Fout2;
+    kiss_fft_cpx * tw1 = st->twiddles;
+    kiss_fft_cpx t;
+    Fout2 = Fout + m;
+    do{
+        C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
+
+        C_MUL (t,  *Fout2 , *tw1);
+        tw1 += fstride;
+        C_SUB( *Fout2 ,  *Fout , t );
+        C_ADDTO( *Fout ,  t );
+        ++Fout2;
+        ++Fout;
+    }while (--m);
+}
+
+static void kf_bfly4(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        const size_t m
+        )
+{
+    kiss_fft_cpx *tw1,*tw2,*tw3;
+    kiss_fft_cpx scratch[6];
+    size_t k=m;
+    const size_t m2=2*m;
+    const size_t m3=3*m;
+
+
+    tw3 = tw2 = tw1 = st->twiddles;
+
+    do {
+        C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
+
+        C_MUL(scratch[0],Fout[m] , *tw1 );
+        C_MUL(scratch[1],Fout[m2] , *tw2 );
+        C_MUL(scratch[2],Fout[m3] , *tw3 );
+
+        C_SUB( scratch[5] , *Fout, scratch[1] );
+        C_ADDTO(*Fout, scratch[1]);
+        C_ADD( scratch[3] , scratch[0] , scratch[2] );
+        C_SUB( scratch[4] , scratch[0] , scratch[2] );
+        C_SUB( Fout[m2], *Fout, scratch[3] );
+        tw1 += fstride;
+        tw2 += fstride*2;
+        tw3 += fstride*3;
+        C_ADDTO( *Fout , scratch[3] );
+
+        if(st->inverse) {
+            Fout[m].r = scratch[5].r - scratch[4].i;
+            Fout[m].i = scratch[5].i + scratch[4].r;
+            Fout[m3].r = scratch[5].r + scratch[4].i;
+            Fout[m3].i = scratch[5].i - scratch[4].r;
+        }else{
+            Fout[m].r = scratch[5].r + scratch[4].i;
+            Fout[m].i = scratch[5].i - scratch[4].r;
+            Fout[m3].r = scratch[5].r - scratch[4].i;
+            Fout[m3].i = scratch[5].i + scratch[4].r;
+        }
+        ++Fout;
+    }while(--k);
+}
+
+static void kf_bfly3(
+         kiss_fft_cpx * Fout,
+         const size_t fstride,
+         const kiss_fft_cfg st,
+         size_t m
+         )
+{
+     size_t k=m;
+     const size_t m2 = 2*m;
+     kiss_fft_cpx *tw1,*tw2;
+     kiss_fft_cpx scratch[5];
+     kiss_fft_cpx epi3;
+     epi3 = st->twiddles[fstride*m];
+
+     tw1=tw2=st->twiddles;
+
+     do{
+         C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
+
+         C_MUL(scratch[1],Fout[m] , *tw1);
+         C_MUL(scratch[2],Fout[m2] , *tw2);
+
+         C_ADD(scratch[3],scratch[1],scratch[2]);
+         C_SUB(scratch[0],scratch[1],scratch[2]);
+         tw1 += fstride;
+         tw2 += fstride*2;
+
+         Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
+         Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
+
+         C_MULBYSCALAR( scratch[0] , epi3.i );
+
+         C_ADDTO(*Fout,scratch[3]);
+
+         Fout[m2].r = Fout[m].r + scratch[0].i;
+         Fout[m2].i = Fout[m].i - scratch[0].r;
+
+         Fout[m].r -= scratch[0].i;
+         Fout[m].i += scratch[0].r;
+
+         ++Fout;
+     }while(--k);
+}
+
+static void kf_bfly5(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        int m
+        )
+{
+    kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
+    int u;
+    kiss_fft_cpx scratch[13];
+    kiss_fft_cpx * twiddles = st->twiddles;
+    kiss_fft_cpx *tw;
+    kiss_fft_cpx ya,yb;
+    ya = twiddles[fstride*m];
+    yb = twiddles[fstride*2*m];
+
+    Fout0=Fout;
+    Fout1=Fout0+m;
+    Fout2=Fout0+2*m;
+    Fout3=Fout0+3*m;
+    Fout4=Fout0+4*m;
+
+    tw=st->twiddles;
+    for ( u=0; u<m; ++u ) {
+        C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
+        scratch[0] = *Fout0;
+
+        C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
+        C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
+        C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
+        C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
+
+        C_ADD( scratch[7],scratch[1],scratch[4]);
+        C_SUB( scratch[10],scratch[1],scratch[4]);
+        C_ADD( scratch[8],scratch[2],scratch[3]);
+        C_SUB( scratch[9],scratch[2],scratch[3]);
+
+        Fout0->r += scratch[7].r + scratch[8].r;
+        Fout0->i += scratch[7].i + scratch[8].i;
+
+        scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
+        scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
+
+        scratch[6].r =  S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
+        scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
+
+        C_SUB(*Fout1,scratch[5],scratch[6]);
+        C_ADD(*Fout4,scratch[5],scratch[6]);
+
+        scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
+        scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
+        scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
+        scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
+
+        C_ADD(*Fout2,scratch[11],scratch[12]);
+        C_SUB(*Fout3,scratch[11],scratch[12]);
+
+        ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
+    }
+}
+
+/* perform the butterfly for one stage of a mixed radix FFT */
+static void kf_bfly_generic(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        int m,
+        int p
+        )
+{
+    int u,k,q1,q;
+    kiss_fft_cpx * twiddles = st->twiddles;
+    kiss_fft_cpx t;
+    int Norig = st->nfft;
+
+    kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
+
+    for ( u=0; u<m; ++u ) {
+        k=u;
+        for ( q1=0 ; q1<p ; ++q1 ) {
+            scratch[q1] = Fout[ k  ];
+            C_FIXDIV(scratch[q1],p);
+            k += m;
+        }
+
+        k=u;
+        for ( q1=0 ; q1<p ; ++q1 ) {
+            int twidx=0;
+            Fout[ k ] = scratch[0];
+            for (q=1;q<p;++q ) {
+                twidx += fstride * k;
+                if (twidx>=Norig) twidx-=Norig;
+                C_MUL(t,scratch[q] , twiddles[twidx] );
+                C_ADDTO( Fout[ k ] ,t);
+            }
+            k += m;
+        }
+    }
+    KISS_FFT_TMP_FREE(scratch);
+}
+
+static
+void kf_work(
+        kiss_fft_cpx * Fout,
+        const kiss_fft_cpx * f,
+        const size_t fstride,
+        int in_stride,
+        int * factors,
+        const kiss_fft_cfg st
+        )
+{
+    kiss_fft_cpx * Fout_beg=Fout;
+    const int p=*factors++; /* the radix  */
+    const int m=*factors++; /* stage's fft length/p */
+    const kiss_fft_cpx * Fout_end = Fout + p*m;
+
+#ifdef _OPENMP
+    /* use openmp extensions at the */
+    /* top-level (not recursive) */
+    if (fstride==1 && p<=5)
+    {
+        int k;
+
+        /* execute the p different work units in different threads */
+#       pragma omp parallel for
+        for (k=0;k<p;++k)
+            kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
+        // all threads have joined by this point
+
+        switch (p) {
+            case 2: kf_bfly2(Fout,fstride,st,m); break;
+            case 3: kf_bfly3(Fout,fstride,st,m); break;
+            case 4: kf_bfly4(Fout,fstride,st,m); break;
+            case 5: kf_bfly5(Fout,fstride,st,m); break;
+            default: kf_bfly_generic(Fout,fstride,st,m,p); break;
+        }
+        return;
+    }
+#endif
+
+    if (m==1) {
+        do{
+            *Fout = *f;
+            f += fstride*in_stride;
+        }while(++Fout != Fout_end );
+    }else{
+        do{
+            /* recursive call:
+             * DFT of size m*p performed by doing
+             * p instances of smaller DFTs of size m,
+             * each one takes a decimated version of the input
+             */
+            kf_work( Fout , f, fstride*p, in_stride, factors,st);
+            f += fstride*in_stride;
+        }while( (Fout += m) != Fout_end );
+    }
+
+    Fout=Fout_beg;
+
+    /* recombine the p smaller DFTs  */
+    switch (p) {
+        case 2: kf_bfly2(Fout,fstride,st,m); break;
+        case 3: kf_bfly3(Fout,fstride,st,m); break;
+        case 4: kf_bfly4(Fout,fstride,st,m); break;
+        case 5: kf_bfly5(Fout,fstride,st,m); break;
+        default: kf_bfly_generic(Fout,fstride,st,m,p); break;
+    }
+}
+
+/*  facbuf is populated by p1,m1,p2,m2, ...
+    where
+    p[i] * m[i] = m[i-1]
+    m0 = n                  */
+static
+void kf_factor(int n,int * facbuf)
+{
+    int p=4;
+    double floor_sqrt;
+    floor_sqrt = floor( sqrt((double)n) );
+
+    /*factor out powers of 4, powers of 2, then any remaining primes */
+    do {
+        while (n % p) {
+            switch (p) {
+                case 4: p = 2; break;
+                case 2: p = 3; break;
+                default: p += 2; break;
+            }
+            if (p > floor_sqrt)
+                p = n;          /* no more factors, skip to end */
+        }
+        n /= p;
+        *facbuf++ = p;
+        *facbuf++ = n;
+    } while (n > 1);
+}
+
+/*
+ *
+ * User-callable function to allocate all necessary storage space for the fft.
+ *
+ * The return value is a contiguous block of memory, allocated with malloc.  As such,
+ * It can be freed with free(), rather than a kiss_fft-specific function.
+ * */
+kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
+{
+    kiss_fft_cfg st=NULL;
+    size_t memneeded = sizeof(struct kiss_fft_state)
+        + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
+
+    if ( lenmem==NULL ) {
+        st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
+    }else{
+        if (mem != NULL && *lenmem >= memneeded)
+            st = (kiss_fft_cfg)mem;
+        *lenmem = memneeded;
+    }
+    if (st) {
+        int i;
+        st->nfft=nfft;
+        st->inverse = inverse_fft;
+
+        for (i=0;i<nfft;++i) {
+            const double pi=3.141592653589793238462643383279502884197169399375105820974944;
+            double phase = -2*pi*i / nfft;
+            if (st->inverse)
+                phase *= -1;
+            kf_cexp(st->twiddles+i, phase );
+        }
+
+        kf_factor(nfft,st->factors);
+    }
+    return st;
+}
+
+
+void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
+{
+    if (fin == fout) {
+        /* NOTE: this is not really an in-place FFT algorithm. */
+        /* It just performs an out-of-place FFT into a temp buffer */
+        kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
+        kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
+        memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
+        KISS_FFT_TMP_FREE(tmpbuf);
+    }else{
+        kf_work( fout, fin, 1,in_stride, st->factors,st );
+    }
+}
+
+void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
+{
+    kiss_fft_stride(cfg,fin,fout,1);
+}
+
+
+void kiss_fft_cleanup(void)
+{
+    /* nothing needed any more */
+}
+
+int kiss_fft_next_fast_size(int n)
+{
+    while(1) {
+        int m=n;
+        while ( (m%2) == 0 ) m/=2;
+        while ( (m%3) == 0 ) m/=3;
+        while ( (m%5) == 0 ) m/=5;
+        if (m<=1)
+            break; /* n is completely factorable by twos, threes, and fives */
+        n++;
+    }
+    return n;
+}
--- /dev/null
+++ b/lib/kissfft/kiss_fft.h
@@ -1,0 +1,128 @@
+#ifndef KISS_FFT_H
+#define KISS_FFT_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ ATTENTION!
+ If you would like a :
+ -- a utility that will handle the caching of fft objects
+ -- real-only (no imaginary time component ) FFT
+ -- a multi-dimensional FFT
+ -- a command-line utility to perform ffts
+ -- a command-line utility to perform fast-convolution filtering
+
+ Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
+  in the tools/ directory.
+*/
+
+#ifdef USE_SIMD
+# include <xmmintrin.h>
+# define kiss_fft_scalar __m128
+#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
+#define KISS_FFT_FREE _mm_free
+#else
+#define KISS_FFT_MALLOC malloc
+#define KISS_FFT_FREE free
+#endif
+
+
+#ifdef FIXED_POINT
+#include <sys/types.h>
+# if (FIXED_POINT == 32)
+#  define kiss_fft_scalar int32_t
+# else
+#  define kiss_fft_scalar int16_t
+# endif
+#else
+# ifndef kiss_fft_scalar
+/*  default is float */
+#   define kiss_fft_scalar float
+# endif
+#endif
+
+#ifndef SOUNDPIPE_H
+
+typedef struct {
+    kiss_fft_scalar r;
+    kiss_fft_scalar i;
+}kiss_fft_cpx;
+
+typedef struct kiss_fft_state* kiss_fft_cfg;
+
+#endif
+
+/*
+ *  kiss_fft_alloc
+ *
+ *  Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
+ *
+ *  typical usage:      kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
+ *
+ *  The return value from fft_alloc is a cfg buffer used internally
+ *  by the fft routine or NULL.
+ *
+ *  If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
+ *  The returned value should be free()d when done to avoid memory leaks.
+ *
+ *  The state can be placed in a user supplied buffer 'mem':
+ *  If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
+ *      then the function places the cfg in mem and the size used in *lenmem
+ *      and returns mem.
+ *
+ *  If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
+ *      then the function returns NULL and places the minimum cfg
+ *      buffer size in *lenmem.
+ * */
+
+kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
+
+/*
+ * kiss_fft(cfg,in_out_buf)
+ *
+ * Perform an FFT on a complex input buffer.
+ * for a forward FFT,
+ * fin should be  f[0] , f[1] , ... ,f[nfft-1]
+ * fout will be   F[0] , F[1] , ... ,F[nfft-1]
+ * Note that each element is complex and can be accessed like
+    f[k].r and f[k].i
+ * */
+void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
+
+/*
+ A more generic version of the above function. It reads its input from every Nth sample.
+ * */
+void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
+
+/* If kiss_fft_alloc allocated a buffer, it is one contiguous
+   buffer and can be simply free()d when no longer needed*/
+#define kiss_fft_free free
+
+/*
+ Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
+ your compiler output to call this before you exit.
+*/
+void kiss_fft_cleanup(void);
+
+
+/*
+ * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
+ */
+int kiss_fft_next_fast_size(int n);
+
+/* for real ffts, we need an even size */
+#define kiss_fftr_next_fast_size_real(n) \
+        (kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+++ b/lib/kissfft/kiss_fftr.c
@@ -1,0 +1,159 @@
+/*
+Copyright (c) 2003-2004, Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+*/
+
+#include "kiss_fftr.h"
+#include "_kiss_fft_guts.h"
+
+struct kiss_fftr_state{
+    kiss_fft_cfg substate;
+    kiss_fft_cpx * tmpbuf;
+    kiss_fft_cpx * super_twiddles;
+#ifdef USE_SIMD
+    void * pad;
+#endif
+};
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
+{
+    int i;
+    kiss_fftr_cfg st = NULL;
+    size_t subsize, memneeded;
+
+    if (nfft & 1) {
+        fprintf(stderr,"Real FFT optimization must be even.\n");
+        return NULL;
+    }
+    nfft >>= 1;
+
+    kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
+    memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
+
+    if (lenmem == NULL) {
+        st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
+    } else {
+        if (*lenmem >= memneeded)
+            st = (kiss_fftr_cfg) mem;
+        *lenmem = memneeded;
+    }
+    if (!st)
+        return NULL;
+
+    st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
+    st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
+    st->super_twiddles = st->tmpbuf + nfft;
+    kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
+
+    for (i = 0; i < nfft/2; ++i) {
+        double phase =
+            -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
+        if (inverse_fft)
+            phase *= -1;
+        kf_cexp (st->super_twiddles+i,phase);
+    }
+    return st;
+}
+
+void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
+{
+    /* input buffer timedata is stored row-wise */
+    int k,ncfft;
+    kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
+
+    if ( st->substate->inverse) {
+        fprintf(stderr,"kiss fft usage error: improper alloc\n");
+        exit(1);
+    }
+
+    ncfft = st->substate->nfft;
+
+    /*perform the parallel fft of two real signals packed in real,imag*/
+    kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
+    /* The real part of the DC element of the frequency spectrum in st->tmpbuf
+     * contains the sum of the even-numbered elements of the input time sequence
+     * The imag part is the sum of the odd-numbered elements
+     *
+     * The sum of tdc.r and tdc.i is the sum of the input time sequence.
+     *      yielding DC of input time sequence
+     * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
+     *      yielding Nyquist bin of input time sequence
+     */
+
+    tdc.r = st->tmpbuf[0].r;
+    tdc.i = st->tmpbuf[0].i;
+    C_FIXDIV(tdc,2);
+    CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
+    CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
+    freqdata[0].r = tdc.r + tdc.i;
+    freqdata[ncfft].r = tdc.r - tdc.i;
+#ifdef USE_SIMD
+    freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
+#else
+    freqdata[ncfft].i = freqdata[0].i = 0;
+#endif
+
+    for ( k=1;k <= ncfft/2 ; ++k ) {
+        fpk    = st->tmpbuf[k];
+        fpnk.r =   st->tmpbuf[ncfft-k].r;
+        fpnk.i = - st->tmpbuf[ncfft-k].i;
+        C_FIXDIV(fpk,2);
+        C_FIXDIV(fpnk,2);
+
+        C_ADD( f1k, fpk , fpnk );
+        C_SUB( f2k, fpk , fpnk );
+        C_MUL( tw , f2k , st->super_twiddles[k-1]);
+
+        freqdata[k].r = HALF_OF(f1k.r + tw.r);
+        freqdata[k].i = HALF_OF(f1k.i + tw.i);
+        freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
+        freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
+    }
+}
+
+void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
+{
+    /* input buffer timedata is stored row-wise */
+    int k, ncfft;
+
+    if (st->substate->inverse == 0) {
+        fprintf (stderr, "kiss fft usage error: improper alloc\n");
+        exit (1);
+    }
+
+    ncfft = st->substate->nfft;
+
+    st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
+    st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
+    C_FIXDIV(st->tmpbuf[0],2);
+
+    for (k = 1; k <= ncfft / 2; ++k) {
+        kiss_fft_cpx fk, fnkc, fek, fok, tmp;
+        fk = freqdata[k];
+        fnkc.r = freqdata[ncfft - k].r;
+        fnkc.i = -freqdata[ncfft - k].i;
+        C_FIXDIV( fk , 2 );
+        C_FIXDIV( fnkc , 2 );
+
+        C_ADD (fek, fk, fnkc);
+        C_SUB (tmp, fk, fnkc);
+        C_MUL (fok, tmp, st->super_twiddles[k-1]);
+        C_ADD (st->tmpbuf[k],     fek, fok);
+        C_SUB (st->tmpbuf[ncfft - k], fek, fok);
+#ifdef USE_SIMD
+        st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
+#else
+        st->tmpbuf[ncfft - k].i *= -1;
+#endif
+    }
+    kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
+}
--- /dev/null
+++ b/lib/kissfft/kiss_fftr.h
@@ -1,0 +1,49 @@
+#ifndef KISS_FTR_H
+#define KISS_FTR_H
+
+#include "kiss_fft.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+
+ Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
+
+
+
+ */
+
+#ifndef SOUNDPIPE_H
+
+typedef struct kiss_fftr_state *kiss_fftr_cfg;
+
+#endif
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
+/*
+ nfft must be even
+
+ If you don't care to allocate space, use mem = lenmem = NULL
+*/
+
+
+void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
+/*
+ input timedata has nfft scalar points
+ output freqdata has nfft/2+1 complex points
+*/
+
+void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
+/*
+ input freqdata has  nfft/2+1 complex points
+ output timedata has nfft scalar points
+*/
+
+#define kiss_fftr_free free
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null
+++ b/lib/openlpc/COPYRIGHT.txt
@@ -1,0 +1,30 @@
+                                                               Copyright NOTICE
+                                    NOTICE
+                                       
+COPYRIGHT 1998 BY: Enzo Michelangeli and Future Dynamics
+
+   This software is being provided by the copyright holders under the
+   following license. By obtaining, using and/or copying this software, you
+   agree that you have read, understood, and will comply with the following
+   terms and conditions:
+   
+   Permission to use, copy, modify, and distribute this software and its
+   documentation for any purpose and without fee or royalty is hereby granted,
+   provided that the full text of this NOTICE appears on ALL copies of the
+   software and documentation or portions thereof, including modifications,
+   that you make. Any application making use of this code shall prominently
+   display the notice: "Portions of this software are based in part on code
+   developed by Future Dynamics".
+   
+   THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
+   REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT
+   NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE
+   SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT HOLDERS WILL BEAR NO
+   LIABILITY FOR ANY USE OF THIS SOFTWARE OR DOCUMENTATION.
+   
+   The name and trademarks of copyright holders may NOT be used in advertising
+   or publicity pertaining to the software without specific, written prior
+   permission. Title to copyright in this software and any associated
+   documentation will at all times remain with copyright holders.
--- /dev/null
+++ b/lib/openlpc/Makefile
@@ -1,0 +1,5 @@
+LPATHS += lib/openlpc/openlpc.o
+CFLAGS += -Ilib/openlpc/
+
+lib/openlpc/openlpc.o: lib/openlpc/openlpc.c
+	$(C99) $< -c $(CFLAGS) -o $@
--- /dev/null
+++ b/lib/openlpc/ftol.h
@@ -1,0 +1,31 @@
+#ifdef _MSC_VER
+
+__inline int lrintf(float flt)
+{
+    int intgr;
+
+    _asm
+    {
+        fld flt
+        fistp intgr
+    };
+
+    return intgr ;
+}
+#else
+#ifdef __GNUC__
+/* must define _GNU_SOURCE here or in the makefile */
+#include <math.h>
+#else
+
+#define FP_BITS(fp) (*(int *)&(fp))
+#define FIST_FLOAT_MAGIC_S (float)(7.0f * 2097152.0f)
+
+static int lrintf(float inval)
+{
+    float tmp = FIST_FLOAT_MAGIC_S + inval;
+    int res = ((FP_BITS(tmp)<<10)-0x80000000);
+    return res>>10;
+}
+#endif
+#endif
--- /dev/null
+++ b/lib/openlpc/openlpc.c
@@ -1,0 +1,766 @@
+/************************************************************************\
+
+  Low bitrate LPC CODEC derived from the public domain implementation
+  of Ron Frederick.
+
+  The basic design is preserved, except for several bug fixes and
+  the following modifications:
+
+  1. The pitch detector operates on the (downsampled) signal, not on
+  the residue. This appears to yield better performances, and it
+  lowers the processing load.
+  2. Each frame is elongated by 50% prefixing it with the last half
+  of the previous frame. This design, inherited from the original
+  code for windowing purposes, is exploited in order to provide
+  two independent pitch analyses: on the first 2/3, and on the
+  second 2/3 of the elongated frame (of course, they overlap by
+  half):
+
+  last half old frame	            new frame
+  --------------------========================================
+  <--------- first pitch region --------->
+                      <--------- second pitch region  ------->
+
+  Two voiced/unvoiced flags define the voicing status of each
+  region; only one value for the period is retained (if both
+  halves are voiced, the average is used).
+  The two flags are used by the synthesizer for the halves of
+  each frame to play back. Of course, this is non optimal but
+  is good enough (a half-frame would be too short for measuring
+  low pitches)
+  3. The parameters (one float for the period (pitch), one for the
+  gain, and ten for the LPC-10 filter) are quantized according
+  this procedure:
+  - the period is logarithmically compressed, then quantized
+  as 8-bit unsigned int (6 would actually suffice)
+  - the gain is logarithmically compressed (using a different
+  formula), then quantized at 6-bit unsigned int. The two
+  remaining bits are used for the voicing flags.
+  - the first two LPC parameters (k[1] and k[2]) are multiplied
+  by PI/2, and the arcsine of the result is quantized as
+  6 and 5 bit signed integers. This has proved more effective
+  than the log-area compression used by LPC-10.
+  - the remaining eight LPC parameters (k[3]...k[10]) are
+  quantized as, respectively, 5,4,4,3,3,3,3 and 2 bit signed
+  integers.
+  Finally, period and gain plus voicing flags are stored in the
+  first two bytes of the 7-byte parameters block, and the quantized
+  LPC parameters are packed into the remaining 5 bytes. Two bits
+  remain unassigned, and can be used for error detection or other
+  purposes.
+
+  The frame lenght is actually variable, and is simply passed as
+  initialization parameter to lpc_init(): this allows to experiment
+  with various frame lengths. Long frames reduce the bitrate, but
+  exceeding 320 samples (i.e. 40 ms, at 8000 samples/s) tend to
+  deteriorate the speech, that sounds like spoken by a person
+  affected by a stroke: the LPC parameters (modeling the vocal
+  tract) can't change fast enough for a natural-sounding synthesis.
+  25 ms per frame already yields a quite tight compression, corresponding
+  to 1000/40 * 7 * 8 = 1400 bps. The quality improves little with
+  frames shorter than 250 samples (32 frames/s), so this is a recommended
+  compromise. The bitrate is 32 * 7 * 8 = 1792 bps.
+
+  The synthesizer has been modified as well. For voiced subframes it
+  now uses a sawtooth excitation, instead of the original pulse train.
+  This idea, copied from MELP, reduces the buzzing-noise artifacts.
+  In order to compensate the non-white spectrum of the sawtooth, a
+  pre-emphasis is applied to the signal before the Durbin calculation.
+  The filter has (in s-space) two zeroes at (640, 0) Hz and two poles
+  at (3200, 0) Hz. These filters have been handoded, and may not be
+  optimal. Two other filters (anti-hum high-pass with corner at 100 Hz,
+  and pre-downsampling lowpass with corner at 300 Hz) are Butterworth
+  designs produced by the MkFilter package by A.J. Fisher
+  (http://www.cs.york.ac.uk/~fisher/mkfilter/).
+
+  The C style has been ANSI-fied.
+
+  Complexity: As any LPC CODEC, also this one is not very demanding:
+  for real-time use analysis and synthesis takes each about 6 - 8%
+  of the CPU cycles on a Cy686/166, when the code is compiled with
+  MSVC++ 4.2 with /Ox or gcc with -O3.
+  However, a floating point unit is absolutely required.
+
+
+\************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "soundpipe.h"
+#include "openlpc.h"
+#include "ftol.h"
+
+#define PREEMPH
+
+#define bcopy(a, b, n)	  memmove(b, a, n)
+
+#ifndef M_PI
+#define M_PI (3.1415926535897932384626433832795)
+#endif
+
+static float my_fs = 11025.0;
+#define LPC_FILTORDER		10
+#define FS		my_fs /* Sampling rate */
+#define MAXWINDOW	1000	/* Max analysis window length */
+
+typedef struct openlpc_e_state{
+	float   s[MAXWINDOW], y[MAXWINDOW], h[MAXWINDOW];
+    int     framelen, buflen;
+    float   xv1[3], yv1[3],
+            xv2[2], yv2[2],
+			xv3[1], yv3[3],
+			xv4[2], yv4[2];
+    float   w[MAXWINDOW], r[LPC_FILTORDER+1];
+} openlpc_e_state_t;
+
+typedef struct openlpc_d_state{
+		float Oldper, OldG, Oldk[LPC_FILTORDER + 1];
+        float bp[LPC_FILTORDER+1];
+        float exc;
+		int pitchctr, framelen, buflen;
+} openlpc_d_state_t;
+
+#define FC		200.0	/* Pitch analyzer filter cutoff */
+#define DOWN		5	/* Decimation for pitch analyzer */
+#define MINPIT		40.0	/* Minimum pitch (observed: 74) */
+#define MAXPIT		320.0	/* Maximum pitch (observed: 250) */
+
+#define MINPER		(int)(FS/(DOWN*MAXPIT)+.5)	/* Minimum period  */
+#define MAXPER		(int)(FS/(DOWN*MINPIT)+.5)	/* Maximum period  */
+
+#define REAL_MINPER	 (DOWN*MINPER) /* converted to samples units */
+
+#define WSCALE		1.5863	/* Energy loss due to windowing */
+
+#define BITS_FOR_LPC 38
+
+#define ARCSIN_Q /* provides better quantization of first two k[] at low bitrates */
+
+#if BITS_FOR_LPC == 38
+/* (38 bit LPC-10, 2.7 Kbit/s @ 20ms, 2.4 Kbit/s @ 22.5 ms */
+static int parambits[LPC_FILTORDER] = {6,5,5,4,4,3,3,3,3,2};
+#elif BITS_FOR_LPC == 32
+/* (32 bit LPC-10, 2.4 Kbit/s, not so good */
+static int parambits[LPC_FILTORDER] = {5,5,5,4,3,3,2,2,2,1};
+#else /* BITS_FOR_LPC == 80	*/
+/* 80-bit LPC10, 4.8 Kbit/s */
+static int parambits[LPC_FILTORDER] = {8,8,8,8,8,8,8,8,8,8};
+#endif
+
+static float logmaxminper;
+static int sizeofparm;	/* computed by lpc_init */
+
+static void auto_correl1(float *w, int n, float *r)
+{
+    int i, k;
+
+    for (k=0; k <= MAXPER; k++, n--) {
+        r[k] = 0.0;
+        for (i=0; i < n; i++) {
+            r[k] += (w[i] *  w[i+k]);
+        }
+    }
+}
+
+static void auto_correl2(float *w, int n, float *r)
+{
+    int i, k;
+
+    for (k=0; k <= LPC_FILTORDER; k++, n--) {
+        r[k] = 0.0;
+        for (i=0; i < n; i++) {
+            r[k] += (w[i] *  w[i+k]);
+        }
+    }
+}
+
+static void durbin(float r[], int p, float k[], float *g)
+{
+    int i, j;
+    float a[LPC_FILTORDER+1], at[LPC_FILTORDER+1], e;
+
+    for (i=0; i <= p; i++) a[i] = at[i] = 0.0;
+
+    e = r[0];
+    for (i=1; i <= p; i++) {
+        k[i] = -r[i];
+        for (j=1; j < i; j++) {
+            at[j] = a[j];
+            k[i] -= a[j] * r[i-j];
+        }
+        if (e == 0) {  /* fix by John Walker */
+            *g = 0;
+            return;
+        }
+        k[i] /= e;
+        a[i] = k[i];
+        for (j=1; j < i; j++) a[j] = at[j] + k[i] * at[i-j];
+        e *= 1.0f - k[i]*k[i];
+    }
+    if (e < 0) {
+        e = 0; /* fix by John Walker */
+    }
+    *g = (float)sqrt(e);
+}
+
+/* Enzo's streamlined pitch extractor - on the signal, not the residue */
+
+static void calc_pitch(float *w, int len, float *per)
+{
+    int i, j, rpos;
+    float d[MAXWINDOW/DOWN], r[MAXPER+1], rmax;
+    float rval, rm, rp;
+    float x, y;
+    float vthresh;
+
+    /* decimation */
+    for (i=0, j=0; i < len; i+=DOWN)
+        d[j++] = w[i];
+
+    auto_correl1(d, len/DOWN, r);
+
+    /* find peak between MINPER and MAXPER */
+    x = 1;
+    rpos = 0;
+    rmax = 0.0;
+    y = 0;
+    rm = 0;
+    rp = 0;
+
+    vthresh = 0.;
+
+    for (i = 1; i < MAXPER; i++) {
+        rm = r[i-1];
+        rp = r[i+1];
+        y = rm+r[i]+rp; /* find max of integral from i-1 to i+1 */
+
+        if ((y > rmax) && (r[i] > rm) && (r[i] > rp) && (i > MINPER)) {
+            rmax = y;
+            rpos = i;
+        }
+    }
+
+    /* consider adjacent values */
+    rm = r[rpos-1];
+    rp = r[rpos+1];
+
+#if 0
+    {
+        float a, b, c, x, y;
+        /* parabolic interpolation */
+        a = 0.5f * rm - rmax + 0.5f * rp;
+        b = -0.5f*rm*(2.0f*rpos+1.0f) + 2.0f*rpos*rmax + 0.5f*rp*(1.0f-2.0f*rpos);
+        c = 0.5f*rm*(rpos*rpos+rpos) + rmax*(1.0f-rpos*rpos) + 0.5f*rp*(rpos*rpos-rpos);
+
+        /* find max of interpolating parabole */
+        x = -b / (2.0f * a);
+        y = a*x*x + b*x + c;
+
+        rmax = y;
+        /* normalize, so that 0. < rval < 1. */
+        rval = (r[0] == 0 ? 1.0f : rmax / r[0]);
+    }
+#else
+    if(rpos > 0) {
+        x = ((rpos-1)*rm + rpos*r[rpos] + (rpos+1)*rp)/(rm+r[rpos]+rp);
+    }
+    /* normalize, so that 0. < rval < 1. */
+    rval = (r[0] == 0 ? 0 : r[rpos] / r[0]);
+#endif
+
+    /* periods near the low boundary and at low volumes
+    are usually spurious and
+    manifest themselves as annoying mosquito buzzes */
+
+    *per = 0;	/* default: unvoiced */
+    if ( x > MINPER &&  /* x could be < MINPER or even < 0 if pos == MINPER */
+        x < (MAXPER+1) /* same story */
+        ) {
+
+        vthresh = 0.6f;
+        if(r[0] > 0.002)	   /* at low volumes (< 0.002), prefer unvoiced */
+            vthresh = 0.25;       /* drop threshold at high volumes */
+
+        if(rval > vthresh)
+            *per = x * DOWN;
+    }
+}
+
+/* Initialization of various parameters */
+
+openlpc_encoder_state *create_openlpc_encoder_state(void)
+{
+    openlpc_encoder_state *state;
+
+    state = (openlpc_encoder_state *)calloc(1, sizeof(openlpc_encoder_state));
+
+    return state;
+}
+
+
+void init_openlpc_encoder_state(openlpc_encoder_state *st, int framelen)
+{
+    int i, j;
+
+    st->framelen = framelen;
+
+    st->buflen = framelen*3/2;
+    /*  (st->buflen > MAXWINDOW) return -1;*/
+
+    for(i=0, j=0; i<sizeof(parambits)/sizeof(parambits[0]); i++) {
+        j += parambits[i];
+    }
+    sizeofparm = (j+7)/8 + 2;
+    for (i = 0; i < st->buflen; i++) {
+        st->s[i] = 0.0;
+        st->h[i] = (float)(WSCALE*(0.54 - 0.46 * cos(2 * M_PI * i / (st->buflen-1.0))));
+    }
+    /* init the filters */
+    st->xv1[0] = st->xv1[1] = st->xv1[2] = st->yv1[0] = st->yv1[1] = st->yv1[2] = 0.0f;
+    st->xv2[0] = st->xv2[1] = st->yv2[0] = st->yv2[1] = 0.0f;
+    st->xv3[0] = st->yv3[0] = st->yv3[1] = st->yv3[2] = 0.0f;
+    st->xv4[0] = st->xv4[1] = st->yv4[0] = st->yv4[1] = 0.0f;
+
+    logmaxminper = (float)log((float)MAXPER/MINPER);
+
+}
+
+void destroy_openlpc_encoder_state(openlpc_encoder_state *st)
+{
+    if(st != NULL)
+    {
+        free(st);
+        st = NULL;
+    }
+}
+
+/* LPC Analysis (compression) */
+
+int openlpc_encode(const short *buf, unsigned char *parm, openlpc_encoder_state *st)
+{
+    int i, j;
+    float per, gain, k[LPC_FILTORDER+1];
+    float per1, per2;
+    float xv10, xv11, xv12, yv10, yv11, yv12,
+        xv20, xv21, yv20, yv21,
+        xv30, yv30, yv31, yv32,
+        xv40, xv41, yv40, yv41;
+
+    xv10 = st->xv1[0];
+    xv11 = st->xv1[1];
+    xv12 = st->xv1[2];
+    yv10 = st->yv1[0];
+    yv11 = st->yv1[1];
+    yv12 = st->yv1[2];
+    xv30 = st->xv3[0];
+    yv30 = st->yv3[0];
+    yv31 = st->yv3[1];
+    yv32 = st->yv3[2];
+    for(i = 0; i < LPC_FILTORDER + 1; i++) k[i] = 0;
+    /* convert short data in buf[] to signed lin. data in s[] and prefilter */
+    for (i=0, j=st->buflen - st->framelen; i < st->framelen; i++, j++) {
+
+        float u = (float)(buf[i]/32768.0f);
+
+        /* Anti-hum 2nd order Butterworth high-pass, 100 Hz corner frequency */
+        /* Digital filter designed by mkfilter/mkshape/gencode   A.J. Fisher
+        mkfilter -Bu -Hp -o 2 -a 0.0125 -l -z */
+
+        xv10 = xv11;
+        xv11 = xv12;
+        xv12 = (float)(u * 0.94597831f); /* /GAIN */
+
+        yv10 = yv11;
+        yv11 = yv12;
+        yv12 = (float)((xv10 + xv12) - 2 * xv11
+            + ( -0.8948742499f * yv10) + ( 1.8890389823f * yv11));
+
+        u = st->s[j] = yv12;	/* also affects input of next stage, to the LPC filter synth */
+
+        /* low-pass filter s[] -> y[] before computing pitch */
+        /* second-order Butterworth low-pass filter, corner at 300 Hz */
+        /* Digital filter designed by mkfilter/mkshape/gencode   A.J. Fisher
+        MKFILTER.EXE -Bu -Lp -o 2 -a 0.0375 -l -z */
+
+        /*st->xv3[0] = (float)(u / 2.127814584e+001);*/ /* GAIN */
+        xv30 = (float)(u * 0.04699658f); /* GAIN */
+        yv30 = yv31;
+        yv31 = yv32;
+        yv32 = xv30 + (float)(( -0.7166152306f * yv30) + (1.6696186545f * yv31));
+        st->y[j] = yv32;
+    }
+    st->xv1[0] = xv10;
+    st->xv1[1] = xv11;
+    st->xv1[2] = xv12;
+    st->yv1[0] = yv10;
+    st->yv1[1] = yv11;
+    st->yv1[2] = yv12;
+    st->xv3[0] = xv30;
+    st->yv3[0] = yv30;
+    st->yv3[1] = yv31;
+    st->yv3[2] = yv32;
+#ifdef PREEMPH
+    /* operate optional preemphasis s[] -> s[] on the newly arrived frame */
+    xv20 = st->xv2[0];
+    xv21 = st->xv2[1];
+    yv20 = st->yv2[0];
+    yv21 = st->yv2[1];
+    xv40 = st->xv4[0];
+    xv41 = st->xv4[1];
+    yv40 = st->yv4[0];
+    yv41 = st->yv4[1];
+    for (j=st->buflen - st->framelen; j < st->buflen; j++) {
+        float u = st->s[j];
+
+        /* handcoded filter: 1 zero at 640 Hz, 1 pole at 3200 */
+#define TAU (FS/3200.f)
+#define RHO (0.1f)
+        xv20 = xv21; 	/* e(n-1) */
+        xv21 = (float)(u * 1.584f);		/* e(n)	, add 4 dB to compensate attenuation */
+        yv20 = yv21;
+        yv21 = (float)(TAU/(1.f+RHO+TAU) * yv20 	 /* u(n) */
+            + (RHO+TAU)/(1.f+RHO+TAU) * xv21
+            - TAU/(1.f+RHO+TAU) * xv20);
+        u = yv21;
+
+        /* cascaded copy of handcoded filter: 1 zero at 640 Hz, 1 pole at 3200 */
+        xv40 = xv41;
+        xv41 = (float)(u * 1.584f);
+        yv40 = yv41;
+        yv41 = (float)(TAU/(1.f+RHO+TAU) * yv40
+            + (RHO+TAU)/(1.f+RHO+TAU) * xv41
+            - TAU/(1.f+RHO+TAU) * xv40);
+        u = yv41;
+
+        st->s[j] = u;
+    }
+    st->xv2[0] = xv20;
+    st->xv2[1] = xv21;
+    st->yv2[0] = yv20;
+    st->yv2[1] = yv21;
+    st->xv4[0] = xv40;
+    st->xv4[1] = xv41;
+    st->yv4[0] = yv40;
+    st->yv4[1] = yv41;
+#endif
+
+    /* operate windowing s[] -> w[] */
+
+    for (i=0; i < st->buflen; i++)
+        st->w[i] = st->s[i] * st->h[i];
+
+    /* compute LPC coeff. from autocorrelation (first 11 values) of windowed data */
+    auto_correl2(st->w, st->buflen, st->r);
+    durbin(st->r, LPC_FILTORDER, k, &gain);
+
+    /* calculate pitch */
+    calc_pitch(st->y, st->framelen, &per1);                 /* first 2/3 of buffer */
+    calc_pitch(st->y + st->buflen - st->framelen, st->framelen, &per2); /* last 2/3 of buffer */
+    if(per1 > 0 && per2 >0)
+        per = (per1+per2)/2;
+    else if(per1 > 0)
+        per = per1;
+    else if(per2 > 0)
+        per = per2;
+    else
+        per = 0;
+
+    /* logarithmic q.: 0 = MINPER, 256 = MAXPER */
+    parm[0] = (unsigned char)(per == 0? 0 : (unsigned char)(log(per/(REAL_MINPER)) / logmaxminper * (1<<8)));
+
+#ifdef LINEAR_G_Q
+    i = gain * (1<<7);
+    if(i > 255) 	/* bug fix by EM */
+        i = 255;
+#else
+    i = (int)(float)(256.0f * log(1 + (2.718-1.f)/10.f*gain)); /* deriv = 5.82 allowing to reserve 2 bits */
+    if(i > 255) i = 255;	 /* reached when gain = 10 */
+    i = (i+2) & 0xfc;
+#endif
+
+    parm[1] = (unsigned char)i;
+
+    if(per1 > 0)
+        parm[1] |= 1;
+    if(per2 > 0)
+        parm[1] |= 2;
+
+    for(j=2; j < sizeofparm; j++)
+        parm[j] = 0;
+
+    for (i=0; i < LPC_FILTORDER; i++) {
+        int bitamount = parambits[i];
+        int bitc8 = 8-bitamount;
+        int q = (1 << bitc8);  /* quantum: 1, 2, 4... */
+        float u = k[i+1];
+        int iu;
+#ifdef ARCSIN_Q
+        if(i < 2) u = (float)(asin(u)*2.f/M_PI);
+#endif
+        u *= 127;
+        if(u < 0)
+            u += (0.6f * q);
+        else
+            u += (0.4f * q); /* highly empirical! */
+
+        iu = lrintf(u);
+        iu = iu & 0xff; /* keep only 8 bits */
+
+        /* make room at the left of parm array shifting left */
+        for(j=sizeofparm-1; j >= 3; j--) {
+            parm[j] = (unsigned char)((parm[j] << bitamount) | (parm[j-1] >> bitc8));
+        }
+        parm[2] = (unsigned char)((parm[2] << bitamount) | (iu >> bitc8)); /* parm[2] */
+    }
+
+    bcopy(st->s + st->framelen, st->s, (st->buflen - st->framelen)*sizeof(st->s[0]));
+    bcopy(st->y + st->framelen, st->y, (st->buflen - st->framelen)*sizeof(st->y[0]));
+
+    return sizeofparm;
+}
+
+openlpc_decoder_state *create_openlpc_decoder_state(void)
+{
+    openlpc_decoder_state *state;
+
+    state = (openlpc_decoder_state *)calloc(1, sizeof(openlpc_decoder_state));
+
+    return state;
+}
+
+void init_openlpc_decoder_state(openlpc_decoder_state *st, int framelen)
+{
+    int i, j;
+
+    st->Oldper = 0.0f;
+    st->OldG = 0.0f;
+    for (i = 0; i <= LPC_FILTORDER; i++) {
+        st->Oldk[i] = 0.0f;
+        st->bp[i] = 0.0f;
+    }
+    st->pitchctr = 0;
+    st->exc = 0.0f;
+    logmaxminper = (float)log((float)MAXPER/MINPER);
+
+    for(i=0, j=0; i<sizeof(parambits)/sizeof(parambits[0]); i++) {
+        j += parambits[i];
+    }
+    sizeofparm = (j+7)/8 + 2;
+
+    /* test for a valid frame len? */
+    st->framelen = framelen;
+    st->buflen = framelen*3/2;
+}
+
+/* LPC Synthesis (decoding) */
+
+int openlpc_decode(sp_data *sp, unsigned char *parm, short *buf, openlpc_decoder_state *st)
+{
+    int i, j, flen=st->framelen;
+    float per, gain, k[LPC_FILTORDER+1];
+    float f, u, newgain, Ginc, Newper, perinc;
+    float Newk[LPC_FILTORDER+1], kinc[LPC_FILTORDER+1];
+    float gainadj;
+    int hframe;
+    float hper[2];
+    int ii;
+    float bp0, bp1, bp2, bp3, bp4, bp5, bp6, bp7, bp8, bp9, bp10;
+            float kj;
+
+    bp0 = st->bp[0];
+    bp1 = st->bp[1];
+    bp2 = st->bp[2];
+    bp3 = st->bp[3];
+    bp4 = st->bp[4];
+    bp5 = st->bp[5];
+    bp6 = st->bp[6];
+    bp7 = st->bp[7];
+    bp8 = st->bp[8];
+    bp9 = st->bp[9];
+    bp10 = st->bp[10];
+
+    per = (float)(parm[0]);
+
+    per = (float)(per == 0? 0: REAL_MINPER * exp(per/(1<<8) * logmaxminper));
+
+    hper[0] = hper[1] = per;
+
+    if((parm[1] & 0x1) == 0) hper[0] = 0;
+    if((parm[1] & 0x2) == 0) hper[1] = 0;
+
+#ifdef LINEAR_G_Q
+    gain = (float)parm[1] / (1<<7);
+#else
+    gain = (float)parm[1] / 256.f;
+    gain = (float)((exp(gain) - 1)/((2.718-1.f)/10));
+#endif
+
+    k[0] = 0.0;
+
+    for (i=LPC_FILTORDER-1; i >= 0; i--) {
+        int bitamount = parambits[i];
+        int bitc8 = 8-bitamount;
+        /* casting to char should set the sign properly */
+        char c = (char)(parm[2] << bitc8);
+
+        for(j=2; j<sizeofparm; j++)
+            parm[j] = (unsigned char)((parm[j] >> bitamount) | (parm[j+1] << bitc8));
+
+        k[i+1] = ((float)c / (1<<7));
+#ifdef ARCSIN_Q
+        if(i<2) k[i+1] = (float)sin(M_PI/2*k[i+1]);
+#endif
+    }
+
+    /* k[] are the same in the two subframes */
+    for (i=1; i <= LPC_FILTORDER; i++) {
+        Newk[i] = st->Oldk[i];
+        kinc[i] = (k[i] - st->Oldk[i]) / flen;
+    }
+
+    /* Loop on two half frames */
+
+    for(hframe=0, ii=0; hframe<2; hframe++) {
+
+        Newper = st->Oldper;
+        newgain = st->OldG;
+
+        Ginc = (gain - st->OldG) / (flen/2);
+        per = hper[hframe];
+
+        if (per == 0.0) {			 /* if unvoiced */
+            gainadj = /* 1.5874 * */ (float)sqrt(3.0f/st->buflen);
+        } else {
+            gainadj = (float)sqrt(per/st->buflen);
+        }
+
+        /* Interpolate period ONLY if both old and new subframes are voiced, gain and K always */
+
+        if (st->Oldper != 0 && per != 0) {
+            perinc = (per - st->Oldper) / (flen/2);
+        } else {
+            perinc = 0.0f;
+            Newper = per;
+        }
+
+        if (Newper == 0.f) st->pitchctr = 0;
+
+        for (i=0; i < flen/2; i++, ii++) {
+            if (Newper == 0.f) {
+                u = (float)(((sp_rand(sp)*(1/(1.0f+RAND_MAX))) - 0.5f ) * newgain * gainadj);
+            } else {			/* voiced: send a delta every per samples */
+                /* triangular excitation */
+                if (st->pitchctr == 0) {
+                    st->exc = newgain * 0.25f * gainadj;
+                    st->pitchctr = (int) Newper;
+                } else {
+                    st->exc -= newgain/Newper * 0.5f * gainadj;
+                    st->pitchctr--;
+                }
+                u = st->exc;
+            }
+            f = u;
+	        /* excitation */
+            kj = Newk[10];
+            f -= kj * bp9;
+            bp10 = bp9 + kj * f;
+
+            kj = Newk[9];
+            f -= kj * bp8;
+            bp9 = bp8 + kj * f;
+
+            kj = Newk[8];
+            f -= kj * bp7;
+            bp8 = bp7 + kj * f;
+
+            kj = Newk[7];
+            f -= kj * bp6;
+            bp7 = bp6 + kj * f;
+
+            kj = Newk[6];
+            f -= kj * bp5;
+            bp6 = bp5 + kj * f;
+
+            kj = Newk[5];
+            f -= kj * bp4;
+            bp5 = bp4 + kj * f;
+
+            kj = Newk[4];
+            f -= kj * bp3;
+            bp4 = bp3 + kj * f;
+
+            kj = Newk[3];
+            f -= kj * bp2;
+            bp3 = bp2 + kj * f;
+
+            kj = Newk[2];
+            f -= kj * bp1;
+            bp2 = bp1 + kj * f;
+
+            kj = Newk[1];
+            f -= kj * bp0;
+            bp1 = bp0 + kj * f;
+
+            bp0 = f;
+            u = f;
+
+            if (u  < -0.9999f) {
+                u = -0.9999f;
+            } else if (u > 0.9999f) {
+                u = 0.9999f;
+            }
+
+            buf[ii] = (short)lrintf(u * 32767.0f);
+
+            Newper += perinc;
+            newgain += Ginc;
+            for (j=1; j <= LPC_FILTORDER; j++) Newk[j] += kinc[j];
+
+        }
+
+        st->Oldper = per;
+        st->OldG = gain;
+    }
+    st->bp[0] = bp0;
+    st->bp[1] = bp1;
+    st->bp[2] = bp2;
+    st->bp[3] = bp3;
+    st->bp[4] = bp4;
+    st->bp[5] = bp5;
+    st->bp[6] = bp6;
+    st->bp[7] = bp7;
+    st->bp[8] = bp8;
+    st->bp[9] = bp9;
+    st->bp[10] = bp10;
+
+    for (j=1; j <= LPC_FILTORDER; j++) st->Oldk[j] = k[j];
+
+    return flen;
+}
+
+void destroy_openlpc_decoder_state(openlpc_decoder_state *st)
+{
+    if(st != NULL)
+    {
+        free(st);
+        st = NULL;
+    }
+}
+
+void openlpc_sr(float sr)
+{
+    my_fs = sr;
+}
+
+size_t openlpc_get_encoder_state_size(void)
+{
+    return sizeof(openlpc_encoder_state);
+}
+
+size_t openlpc_get_decoder_state_size(void)
+{
+    return sizeof(openlpc_decoder_state);
+}
--- /dev/null
+++ b/lib/openlpc/openlpc.h
@@ -1,0 +1,38 @@
+/*
+ * LPC subroutine declarations
+ */
+
+#ifndef OPENLPC_H
+#define OPENLPC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OPENLPC_FRAMESIZE_1_8	    250
+#define OPENLPC_FRAMESIZE_1_4	    320
+#define OPENLPC_ENCODED_FRAME_SIZE  7
+
+typedef struct openlpc_e_state openlpc_encoder_state;
+typedef struct openlpc_d_state openlpc_decoder_state;
+
+openlpc_encoder_state *create_openlpc_encoder_state(void);
+void init_openlpc_encoder_state(openlpc_encoder_state *st, int framelen);
+int  openlpc_encode(const short *in, unsigned char *out, openlpc_encoder_state *st);
+void destroy_openlpc_encoder_state(openlpc_encoder_state *st);
+
+openlpc_decoder_state *create_openlpc_decoder_state(void);
+void init_openlpc_decoder_state(openlpc_decoder_state *st, int framelen);
+int  openlpc_decode(sp_data *sp, unsigned char *in, short *out, openlpc_decoder_state *st);
+void destroy_openlpc_decoder_state(openlpc_decoder_state *st);
+
+void openlpc_sr(float sr);
+
+size_t openlpc_get_encoder_state_size(void);
+size_t openlpc_get_decoder_state_size(void);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif /* OPENLPC_H */
--- /dev/null
+++ b/lib/spa/Makefile
@@ -1,0 +1,6 @@
+LPATHS += lib/spa/spa.o
+CFLAGS += -Ilib/spa/
+HPATHS += lib/spa/spa.h
+
+lib/spa/spa.o: lib/spa/spa.c
+	$(C89) $(CFLAGS) $< -c -o $@
--- /dev/null
+++ b/lib/spa/spa.c
@@ -1,0 +1,92 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+
+#define CHECK_NULL_FILE(fp) if(fp == NULL) return SP_NOT_OK
+
+int spa_open(sp_data *sp, sp_audio *spa, const char *name, int mode)
+{
+    spa_header *header;
+    header = &spa->header;
+    spa->mode = SPA_NULL;
+    spa->offset = sizeof(spa_header);
+    if(mode == SPA_READ) {
+        spa->fp = fopen(name, "rb");
+        CHECK_NULL_FILE(spa->fp);
+        fread(header, spa->offset, 1, spa->fp);
+    } else if(mode == SPA_WRITE) {
+        spa->fp = fopen(name, "wb");
+        CHECK_NULL_FILE(spa->fp);
+        header->magic = 100;
+        header->nchan = sp->nchan;
+        header->len = sp->len;
+        header->sr = sp->sr;
+        fwrite(header, spa->offset, 1, spa->fp);
+    } else {
+        return SP_NOT_OK;
+    }
+
+    spa->mode = mode;
+
+    return SP_OK;
+}
+
+size_t spa_write_buf(sp_data *sp, sp_audio *spa, SPFLOAT *buf, uint32_t size)
+{
+    if(spa->mode != SPA_WRITE) {
+        return 0;
+    }
+    return fwrite(buf, sizeof(SPFLOAT), size, spa->fp);
+}
+
+size_t spa_read_buf(sp_data *sp, sp_audio *spa, SPFLOAT *buf, uint32_t size)
+{
+    if(spa->mode != SPA_READ) {
+        return 0;
+    }
+    return fread(buf, sizeof(SPFLOAT), size, spa->fp);
+}
+
+int spa_close(sp_audio *spa)
+{
+    if(spa->fp != NULL) fclose(spa->fp);
+    return SP_OK;
+}
+
+int sp_process_spa(sp_data *sp, void *ud, void (*callback)(sp_data *, void *))
+{
+    sp_audio spa;
+    if(spa_open(sp, &spa, sp->filename, SPA_WRITE) == SP_NOT_OK) {
+        fprintf(stderr, "Error: could not open file %s.\n", sp->filename);
+    }
+    while(sp->len > 0) {
+        callback(sp, ud);
+        spa_write_buf(sp, &spa, sp->out, sp->nchan);
+        sp->len--;
+        sp->pos++;
+    }
+    spa_close(&spa);
+    return SP_OK;
+}
+
+int sp_ftbl_loadspa(sp_data *sp, sp_ftbl **ft, const char *filename)
+{
+    sp_ftbl *ftp;
+    sp_audio spa;
+    size_t size;
+
+    *ft = malloc(sizeof(sp_ftbl));
+    ftp = *ft;
+
+    spa_open(sp, &spa, filename, SPA_READ);
+
+    size = spa.header.len;
+
+    ftp->tbl = malloc(sizeof(SPFLOAT) * (size + 1));
+    sp_ftbl_init(sp, ftp, size);
+
+    spa_read_buf(sp, &spa, ftp->tbl, ftp->size);
+    spa_close(&spa);
+    return SP_OK;
+}
--- /dev/null
+++ b/lib/spa/spa.h
@@ -1,0 +1,4 @@
+int spa_open(sp_data *sp, sp_audio *spa, const char *name, int mode);
+size_t spa_write_buf(sp_data *sp, sp_audio *spa, SPFLOAT *buf, uint32_t size);
+size_t spa_read_buf(sp_data *sp, sp_audio *spa, SPFLOAT *buf, uint32_t size);
+int spa_close(sp_audio *spa);
--- /dev/null
+++ b/modules/adsr.c
@@ -1,0 +1,97 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+enum { CLEAR, ATTACK, DECAY, SUSTAIN, RELEASE };
+
+int sp_adsr_create(sp_adsr **p)
+{
+    *p = malloc(sizeof(sp_adsr));
+    return SP_OK;
+}
+
+int sp_adsr_destroy(sp_adsr **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_adsr_init(sp_data *sp, sp_adsr *p)
+{
+    p->atk = 0.1;
+    p->dec = 0.1;
+    p->sus = 0.5;
+    p->rel = 0.3;
+    p->timer = 0;
+    p->a = 0;
+    p->b = 0;
+    p->y = 0;
+    p->x = 0;
+    p->prev = 0;
+    p->atk_time = p->atk * sp->sr;
+    p->mode = CLEAR;
+    return SP_OK;
+}
+
+static SPFLOAT tau2pole(sp_data *sp, sp_adsr *p, SPFLOAT tau)
+{
+    return exp(-1.0 / (tau * sp->sr));
+}
+
+static SPFLOAT adsr_filter(sp_data *sp, sp_adsr *p)
+{
+    p->y = p->b * p->x  + p->a * p->y;
+    return p->y;
+}
+
+int sp_adsr_compute(sp_data *sp, sp_adsr *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT pole;
+    if (p->prev < *in && p->mode != DECAY) {
+        p->mode = ATTACK;
+        p->timer = 0;
+        /* quick fix: uncomment if broken */
+        /* pole = tau2pole(sp, p, p->atk * 0.75); */
+        /* p->atk_time = p->atk * sp->sr * 1.5; */
+        pole = tau2pole(sp, p, p->atk * 0.6);
+        p->atk_time = p->atk * sp->sr;
+        p->a = pole;
+        p->b = 1 - pole;
+    } else if (p->prev > *in) {
+        p->mode = RELEASE;
+        pole = tau2pole(sp, p, p->rel);
+        p->a = pole;
+        p->b = 1 - pole;
+    }
+
+    p->x = *in;
+    p->prev = *in;
+
+    switch (p->mode) {
+        case CLEAR:
+            *out = 0;
+            break;
+        case ATTACK:
+            p->timer++;
+            *out = adsr_filter(sp, p);
+            /* quick fix: uncomment if broken */
+            /* if(p->timer > p->atk_time) { */
+            if (*out > 0.99) {
+                p->mode = DECAY;
+                pole = tau2pole(sp, p, p->dec);
+                p->a = pole;
+                p->b = 1 - pole;
+            }
+            break;
+        case DECAY:
+        case RELEASE:
+            p->x *= p->sus;
+            *out = adsr_filter(sp, p);
+        default:
+            break;
+    }
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/autowah.c
@@ -1,0 +1,211 @@
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+
+static float faustpower2_f(float value) {
+	return (value * value);
+}
+
+typedef struct {
+
+	float fRec0[3];
+	float fRec3[2];
+	float fRec2[2];
+	float fRec1[2];
+	float fRec4[2];
+	float fRec5[2];
+	FAUSTFLOAT fVslider0;
+	int fSamplingFreq;
+	int iConst0;
+	float fConst1;
+	float fConst2;
+	float fConst3;
+	float fConst4;
+	float fConst5;
+	float fConst6;
+	FAUSTFLOAT fVslider1;
+	FAUSTFLOAT fVslider2;
+
+} autowah;
+
+autowah* newautowah() {
+	autowah* dsp = (autowah*)malloc(sizeof(autowah));
+	return dsp;
+}
+
+void deleteautowah(autowah* dsp) {
+	free(dsp);
+}
+
+void instanceInitautowah(autowah* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->fVslider0 = (FAUSTFLOAT)0.;
+	dsp->iConst0 = min(192000, max(1, dsp->fSamplingFreq));
+	dsp->fConst1 = (1413.72f / (float)dsp->iConst0);
+	dsp->fConst2 = exp((0.f - (100.f / (float)dsp->iConst0)));
+	dsp->fConst3 = (1.f - dsp->fConst2);
+	dsp->fConst4 = exp((0.f - (10.f / (float)dsp->iConst0)));
+	dsp->fConst5 = (1.f - dsp->fConst4);
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 2); i0 = (i0 + 1)) {
+			dsp->fRec3[i0] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec2[i1] = 0.f;
+
+		}
+
+	}
+	dsp->fConst6 = (2827.43f / (float)dsp->iConst0);
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 2); i2 = (i2 + 1)) {
+			dsp->fRec1[i2] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i3;
+		for (i3 = 0; (i3 < 2); i3 = (i3 + 1)) {
+			dsp->fRec4[i3] = 0.f;
+
+		}
+
+	}
+	dsp->fVslider1 = (FAUSTFLOAT)100.;
+	dsp->fVslider2 = (FAUSTFLOAT)0.1;
+	/* C99 loop */
+	{
+		int i4;
+		for (i4 = 0; (i4 < 2); i4 = (i4 + 1)) {
+			dsp->fRec5[i4] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i5;
+		for (i5 = 0; (i5 < 3); i5 = (i5 + 1)) {
+			dsp->fRec0[i5] = 0.f;
+
+		}
+
+	}
+
+}
+
+void initautowah(autowah* dsp, int samplingFreq) {
+	instanceInitautowah(dsp, samplingFreq);
+}
+
+void buildUserInterfaceautowah(autowah* dsp, UIGlue* interface) {
+	interface->addVerticalSlider(interface->uiInterface, "level", &dsp->fVslider2, 0.1f, 0.f, 1.f, 0.01f);
+	interface->addVerticalSlider(interface->uiInterface, "wah", &dsp->fVslider0, 0.f, 0.f, 1.f, 0.01f);
+	interface->addVerticalSlider(interface->uiInterface, "wet_dry", &dsp->fVslider1, 100.f, 0.f, 100.f, 1.f);
+}
+
+void computeautowah(autowah* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* input0 = inputs[0];
+	FAUSTFLOAT* output0 = outputs[0];
+	float fSlow0 = (float)dsp->fVslider0;
+	float fSlow1 = (float)dsp->fVslider1;
+	float fSlow2 = (0.01f * (fSlow1 * (float)dsp->fVslider2));
+	float fSlow3 = ((1.f - (0.01f * fSlow1)) + (1.f - fSlow0));
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			float fTemp0 = (float)input0[i];
+			float fTemp1 = fabs(fTemp0);
+			dsp->fRec3[0] = max(fTemp1, ((dsp->fConst4 * dsp->fRec3[1]) + (dsp->fConst5 * fTemp1)));
+			dsp->fRec2[0] = ((dsp->fConst2 * dsp->fRec2[1]) + (dsp->fConst3 * dsp->fRec3[0]));
+			float fTemp2 = min(1.f, dsp->fRec2[0]);
+			float fTemp3 = pow(2.f, (2.3f * fTemp2));
+			float fTemp4 = (1.f - (dsp->fConst1 * (fTemp3 / pow(2.f, (1.f + (2.f * (1.f - fTemp2)))))));
+			dsp->fRec1[0] = ((0.999f * dsp->fRec1[1]) + (0.001f * (0.f - (2.f * (fTemp4 * cos((dsp->fConst6 * fTemp3)))))));
+			dsp->fRec4[0] = ((0.999f * dsp->fRec4[1]) + (0.001f * faustpower2_f(fTemp4)));
+			dsp->fRec5[0] = ((0.999f * dsp->fRec5[1]) + (0.0001f * pow(4.f, fTemp2)));
+			dsp->fRec0[0] = (0.f - (((dsp->fRec1[0] * dsp->fRec0[1]) + (dsp->fRec4[0] * dsp->fRec0[2])) - (fSlow2 * (dsp->fRec5[0] * fTemp0))));
+			output0[i] = (FAUSTFLOAT)((fSlow0 * (dsp->fRec0[0] - dsp->fRec0[1])) + (fSlow3 * fTemp0));
+			dsp->fRec3[1] = dsp->fRec3[0];
+			dsp->fRec2[1] = dsp->fRec2[0];
+			dsp->fRec1[1] = dsp->fRec1[0];
+			dsp->fRec4[1] = dsp->fRec4[0];
+			dsp->fRec5[1] = dsp->fRec5[0];
+			dsp->fRec0[2] = dsp->fRec0[1];
+			dsp->fRec0[1] = dsp->fRec0[0];
+		}
+
+	}
+
+}
+
+static void addVerticalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_autowah *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_autowah_create(sp_autowah **p)
+{
+    *p = malloc(sizeof(sp_autowah));
+    return SP_OK;
+}
+
+int sp_autowah_destroy(sp_autowah **p)
+{
+    sp_autowah *pp = *p;
+    autowah *dsp = pp->faust;
+    deleteautowah (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_autowah_init(sp_data *sp, sp_autowah *p)
+{
+    autowah *dsp = newautowah();
+    UIGlue UI;
+    p->argpos = 0;
+    UI.addVerticalSlider= addVerticalSlider;
+    UI.uiInterface = p;
+    buildUserInterfaceautowah(dsp, &UI);
+    initautowah(dsp, sp->sr);
+
+    p->level = p->args[0];
+    p->wah = p->args[1];
+    p->mix = p->args[2];
+
+    p->faust = dsp;
+    return SP_OK;
+}
+
+int sp_autowah_compute(sp_data *sp, sp_autowah *p, SPFLOAT *in, SPFLOAT *out)
+{
+    autowah *dsp = p->faust;
+    SPFLOAT *faust_out[] = {out};
+    SPFLOAT *faust_in[] = {in};
+    computeautowah(dsp, 1, faust_in, faust_out);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/base.c
@@ -1,0 +1,204 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#ifndef NO_LIBSNDFILE
+#include <sndfile.h>
+#endif
+#include "soundpipe.h"
+
+int sp_create(sp_data **spp)
+{
+    sp_data *sp;
+    SPFLOAT *out;
+    *spp = (sp_data *) malloc(sizeof(sp_data));
+    sp = *spp;
+    sprintf(sp->filename, "test.wav");
+    sp->nchan = 1;
+    out = malloc(sizeof(SPFLOAT) * sp->nchan);
+    *out = 0;
+    sp->out = out;
+    sp->sr = 44100;
+    sp->len = 5 * sp->sr;
+    sp->pos = 0;
+    sp->rand = 0;
+    return 0;
+}
+
+int sp_createn(sp_data **spp, int nchan)
+{
+    sp_data *sp;
+    SPFLOAT *out;
+    *spp = (sp_data *) malloc(sizeof(sp_data));
+    sp = *spp;
+    sprintf(sp->filename, "test.wav");
+    sp->nchan = nchan;
+    out = malloc(sizeof(SPFLOAT) * sp->nchan);
+    *out = 0;
+    sp->out = out;
+    sp->sr = 44100;
+    sp->len = 5 * sp->sr;
+    sp->pos = 0;
+    sp->rand = 0;
+    return 0;
+}
+
+int sp_destroy(sp_data **spp)
+{
+    sp_data *sp = *spp;
+    free(sp->out);
+    free(*spp);
+    return 0;
+}
+
+#ifndef NO_LIBSNDFILE
+
+int sp_process(sp_data *sp, void *ud, void (*callback)(sp_data *, void *))
+{
+    SNDFILE *sf[sp->nchan];
+    char tmp[140];
+    SF_INFO info;
+    memset(&info, 0, sizeof(SF_INFO));
+    SPFLOAT buf[sp->nchan][SP_BUFSIZE];
+    info.samplerate = sp->sr;
+    info.channels = 1;
+    info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
+    int numsamps, i, chan;
+
+    if (sp->nchan == 1) {
+        sf[0] = sf_open(sp->filename, SFM_WRITE, &info);
+    } else {
+        for (chan = 0; chan < sp->nchan; chan++) {
+            sprintf(tmp, "%02d_%s", chan, sp->filename);
+            sf[chan] = sf_open(tmp, SFM_WRITE, &info);
+        }
+    }
+
+    while (sp->len > 0) {
+        if (sp->len < SP_BUFSIZE) {
+            numsamps = sp->len;
+        } else {
+            numsamps = SP_BUFSIZE;
+        }
+
+        for (i = 0; i < numsamps; i++) {
+            callback(sp, ud);
+            for (chan = 0; chan < sp->nchan; chan++) {
+                buf[chan][i] = sp->out[chan];
+            }
+            sp->pos++;
+        }
+
+        for (chan = 0; chan < sp->nchan; chan++) {
+#ifdef USE_DOUBLE
+            sf_write_double(sf[chan], buf[chan], numsamps);
+#else
+            sf_write_float(sf[chan], buf[chan], numsamps);
+#endif
+        }
+        sp->len -= numsamps;
+    }
+    for (i = 0; i < sp->nchan; i++) {
+        sf_close(sf[i]);
+    }
+    return 0;
+}
+
+#endif
+
+int sp_process_raw(sp_data *sp, void *ud, void (*callback)(sp_data *, void *))
+{
+    int chan;
+    if (sp->len == 0) {
+        while(1) {
+            callback(sp, ud);
+            for (chan = 0; chan < sp->nchan; chan++) {
+                fwrite(&sp->out[chan], sizeof(SPFLOAT), 1, stdout);
+            }
+            sp->len--;
+        }
+    } else {
+        while (sp->len > 0) {
+            callback(sp, ud);
+            for (chan = 0; chan < sp->nchan; chan++) {
+                fwrite(&sp->out[chan], sizeof(SPFLOAT), 1, stdout);
+            }
+            sp->len--;
+            sp->pos++;
+        }
+    }
+    return SP_OK;
+}
+
+int sp_process_plot(sp_data *sp, void *ud, void (*callback)(sp_data *, void *))
+{
+    int chan;
+    fprintf(stdout, "sp_out =  [ ... \n");
+    while (sp->len > 0) {
+        callback(sp, ud);
+        for (chan = 0; chan < sp->nchan; chan++) {
+            /* fwrite(&sp->out[chan], sizeof(SPFLOAT), 1, stdout); */
+            fprintf(stdout, "%g ", sp->out[chan]);
+        }
+        fprintf(stdout, "; ...\n");
+        sp->len--;
+        sp->pos++;
+    }
+    fprintf(stdout, "];\n");
+
+    fprintf(stdout, "plot(sp_out);\n");
+    fprintf(stdout, "title('Plot generated by Soundpipe');\n");
+    fprintf(stdout, "xlabel('Time (samples)');\n");
+    fprintf(stdout, "ylabel('Amplitude');\n");
+    return SP_OK;
+}
+
+int sp_auxdata_alloc(sp_auxdata *aux, size_t size)
+{
+    aux->ptr = malloc(size);
+    aux->size = size;
+    memset(aux->ptr, 0, size);
+    return SP_OK;
+}
+
+int sp_auxdata_free(sp_auxdata *aux)
+{
+    free(aux->ptr);
+    return SP_OK;
+}
+
+
+SPFLOAT sp_midi2cps(SPFLOAT nn)
+{
+    return pow(2, (nn - 69.0) / 12.0) * 440.0;
+}
+
+int sp_set(sp_param *p, SPFLOAT val) {
+    p->state = 1;
+    p->val = val;
+    return SP_OK;
+}
+
+int sp_out(sp_data *sp, uint32_t chan, SPFLOAT val)
+{
+    if (chan > sp->nchan - 1) {
+        fprintf(stderr, "sp_out: Invalid channel\n");
+        return SP_NOT_OK;
+    }
+    sp->out[chan] = val;
+    return SP_OK;
+}
+
+uint32_t sp_rand(sp_data *sp)
+{
+    uint32_t val = (1103515245 * sp->rand + 12345) % SP_RANDMAX;
+    sp->rand = val;
+    return val;
+}
+
+void sp_srand(sp_data *sp, uint32_t val)
+{
+    sp->rand = val;
+}
--- /dev/null
+++ b/modules/bigverb.c
@@ -1,0 +1,44 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include "tangled/bigverb.h"
+#include "soundpipe.h"
+
+int sp_bigverb_create(sp_bigverb **p)
+{
+    *p = malloc(sizeof(sp_bigverb));
+    return SP_OK;
+}
+
+int sp_bigverb_destroy(sp_bigverb **p)
+{
+    sp_bigverb *bv;
+    bv = *p;
+    sk_bigverb_del(bv->bv);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_bigverb_init(sp_data *sp, sp_bigverb *p)
+{
+    p->feedback = 0.97;
+    p->lpfreq = 10000;
+    p->bv = sk_bigverb_new(sp->sr);
+
+    sk_bigverb_size(p->bv, p->feedback);
+    sk_bigverb_cutoff(p->bv, p->lpfreq);
+    return SP_OK;
+}
+
+int sp_bigverb_compute(sp_data *sp,
+                       sp_bigverb *p,
+                       SPFLOAT *in1,
+                       SPFLOAT *in2,
+                       SPFLOAT *out1,
+                       SPFLOAT *out2)
+{
+    sk_bigverb_size(p->bv, p->feedback);
+    sk_bigverb_cutoff(p->bv, p->lpfreq);
+    sk_bigverb_tick(p->bv, *in1, *in2, out1, out2);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/biscale.c
@@ -1,0 +1,27 @@
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_biscale_create(sp_biscale **p)
+{
+    *p = malloc(sizeof(sp_biscale));
+    return SP_OK;
+}
+
+int sp_biscale_destroy(sp_biscale **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_biscale_init(sp_data *sp, sp_biscale *p)
+{
+    p->min = 0;
+    p->max = 1;
+    return SP_OK;
+}
+
+int sp_biscale_compute(sp_data *sp, sp_biscale *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = p->min + (*in + 1.0) / 2.0 * (p->max - p->min);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/bitcrush.c
@@ -1,0 +1,56 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+int sp_bitcrush_create(sp_bitcrush **p)
+{
+    *p = malloc(sizeof(sp_bitcrush));
+    return SP_OK;
+}
+
+int sp_bitcrush_destroy(sp_bitcrush **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_bitcrush_init(sp_data *sp, sp_bitcrush *p)
+{
+    p->bitdepth = 8;
+    p->srate = 10000;
+
+    p->incr = 1000;
+    p->sample_index = 0;
+    p->index = 0.0;
+    p->value = 0.0;
+    return SP_OK;
+}
+
+int sp_bitcrush_compute(sp_data *sp, sp_bitcrush *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT bits = pow(2, floor(p->bitdepth));
+    SPFLOAT foldamt = sp->sr / p->srate;
+    SPFLOAT sig;
+    *out = *in * 65536.0;
+    *out += 32768;
+    *out *= (bits / 65536.0);
+    *out = floor(*out);
+    *out = *out * (65536.0 / bits) - 32768;
+    sig = *out;
+    p->incr = foldamt;
+
+    /* apply downsampling */
+    if (p->index < (SPFLOAT)p->sample_index) {
+        p->index += p->incr;
+        p->value = sig;
+    }
+
+    *out = p->value;
+
+    p->sample_index++;
+
+    *out /= 65536.0;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/blsaw.c
@@ -1,0 +1,171 @@
+/*
+ * Saw
+ *
+ * This code was generated by Faust. It utilizes the saw2 Faust
+ * module coded by Julius Smith. See oscillator.lib for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+static float faustpower2_f(float value) {
+	return (value * value);
+
+}
+
+typedef struct {
+
+	float fRec0[2];
+	float fVec0[2];
+	float fVec1[2];
+	int fSamplingFreq;
+	int iConst0;
+	FAUSTFLOAT fHslider0;
+	FAUSTFLOAT fHslider1;
+	float fConst1;
+	float fConst2;
+} blsaw;
+
+blsaw* newblsaw() {
+	blsaw* dsp = (blsaw*)malloc(sizeof(blsaw));
+	return dsp;
+}
+
+void deleteblsaw(blsaw* dsp) {
+	free(dsp);
+}
+
+void instanceInitblsaw(blsaw* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->iConst0 = min(192000, max(1, dsp->fSamplingFreq));
+	dsp->fHslider0 = (FAUSTFLOAT)1.;
+	dsp->fHslider1 = (FAUSTFLOAT)440.;
+	dsp->fConst1 = (float)dsp->iConst0;
+	dsp->fConst2 = (2.f / dsp->fConst1);
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 2); i0 = (i0 + 1)) {
+			dsp->fRec0[i0] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fVec0[i1] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 2); i2 = (i2 + 1)) {
+			dsp->fVec1[i2] = 0.f;
+
+		}
+
+	}
+
+}
+
+void initblsaw(blsaw* dsp, int samplingFreq) {
+	instanceInitblsaw(dsp, samplingFreq);
+}
+
+void buildUserInterfaceblsaw(blsaw* dsp, UIGlue* interface) {
+	interface->addHorizontalSlider(interface->uiInterface, "freq", &dsp->fHslider1, 440.f, 0.f, 20000.f, 0.0001f);
+	interface->addHorizontalSlider(interface->uiInterface, "amp", &dsp->fHslider0, 1.f, 0.f, 1.f, 0.0001f);
+}
+
+void computeblsaw(blsaw* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* output0 = outputs[0];
+	float fSlow0 = (float)dsp->fHslider1;
+	float fSlow1 = ((float)dsp->iConst0 * ((float)dsp->fHslider0 / fSlow0));
+	float fSlow2 = (dsp->fConst2 * fSlow0);
+	float fSlow3 = (dsp->fConst1 / fSlow0);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			dsp->fRec0[0] = fmod((1.f + dsp->fRec0[1]), fSlow3);
+			float fTemp0 = faustpower2_f(((fSlow2 * dsp->fRec0[0]) - 1.f));
+			dsp->fVec0[0] = fTemp0;
+			dsp->fVec1[0] = 0.25f;
+			output0[i] = (FAUSTFLOAT)(fSlow1 * ((fTemp0 - dsp->fVec0[1]) * dsp->fVec1[1]));
+			dsp->fRec0[1] = dsp->fRec0[0];
+			dsp->fVec0[1] = dsp->fVec0[0];
+			dsp->fVec1[1] = dsp->fVec1[0];
+
+		}
+
+	}
+
+}
+
+
+static void addHorizontalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_blsaw *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_blsaw_create(sp_blsaw **p)
+{
+    *p = malloc(sizeof(sp_blsaw));
+    return SP_OK;
+}
+
+int sp_blsaw_destroy(sp_blsaw **p)
+{
+    sp_blsaw *pp = *p;
+    blsaw *dsp = pp->ud;
+    deleteblsaw (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_blsaw_init(sp_data *sp, sp_blsaw *p)
+{
+    blsaw *dsp = newblsaw();
+    UIGlue UI;
+    p->argpos = 0;
+    UI.addHorizontalSlider= addHorizontalSlider;
+    UI.uiInterface = p;
+    buildUserInterfaceblsaw(dsp, &UI);
+    initblsaw(dsp, sp->sr);
+
+
+    p->freq = p->args[0];
+    p->amp = p->args[1];
+
+    p->ud = dsp;
+    return SP_OK;
+}
+
+int sp_blsaw_compute(sp_data *sp, sp_blsaw *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    blsaw *dsp = p->ud;
+    SPFLOAT out1 = 0;
+    SPFLOAT *faust_out[] = {&out1};
+    SPFLOAT *faust_in[] = {in};
+    computeblsaw(dsp, 1, faust_in, faust_out);
+
+    *out = out1;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/blsquare.c
@@ -1,0 +1,185 @@
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+float fmodf(float dummy0, float dummy1);
+static float faustpower2_f(float value) {
+	return (value * value);
+}
+
+typedef struct {
+	float fVec2[4096];
+	int iVec0[2];
+	float fRec0[2];
+	float fVec1[2];
+	FAUSTFLOAT fHslider0;
+	int fSamplingFreq;
+	float fConst0;
+	FAUSTFLOAT fHslider1;
+	FAUSTFLOAT fHslider2;
+	float fConst1;
+	float fConst2;
+	int IOTA;
+} blsquare;
+
+blsquare* newblsquare() {
+	blsquare* dsp = (blsquare*)malloc(sizeof(blsquare));
+	return dsp;
+}
+
+void deleteblsquare(blsquare* dsp) {
+	free(dsp);
+}
+
+
+void instanceInitblsquare(blsquare* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->fHslider0 = (FAUSTFLOAT)1.;
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 2); i0 = (i0 + 1)) {
+			dsp->iVec0[i0] = 0;
+
+		}
+
+	}
+	dsp->fConst0 = (float)min(192000, max(1, dsp->fSamplingFreq));
+	dsp->fHslider1 = (FAUSTFLOAT)0.5;
+	dsp->fHslider2 = (FAUSTFLOAT)440.;
+	dsp->fConst1 = (0.25f * dsp->fConst0);
+	dsp->fConst2 = (1.f / dsp->fConst0);
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec0[i1] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 2); i2 = (i2 + 1)) {
+			dsp->fVec1[i2] = 0.f;
+
+		}
+
+	}
+	dsp->IOTA = 0;
+	/* C99 loop */
+	{
+		int i3;
+		for (i3 = 0; (i3 < 4096); i3 = (i3 + 1)) {
+			dsp->fVec2[i3] = 0.f;
+
+		}
+
+	}
+
+}
+
+void initblsquare(blsquare* dsp, int samplingFreq) {
+	instanceInitblsquare(dsp, samplingFreq);
+}
+
+void buildUserInterfaceblsquare(blsquare* dsp, UIGlue* interface) {
+	interface->addHorizontalSlider(interface->uiInterface, "frequency", &dsp->fHslider2, 440.f, 0.f, 20000.f, 0.0001f);
+	interface->addHorizontalSlider(interface->uiInterface, "amp", &dsp->fHslider0, 1.f, 0.f, 1.f, 1e-05f);
+	interface->addHorizontalSlider(interface->uiInterface, "width", &dsp->fHslider1, 0.5f, 0.f, 1.f, 0.f);
+}
+
+void computeblsquare(blsquare* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* output0 = outputs[0];
+	float fSlow0 = (float)dsp->fHslider0;
+	float fSlow1 = max((float)dsp->fHslider2, 23.4489f);
+	float fSlow2 = max(0.f, min(2047.f, (dsp->fConst0 * ((float)dsp->fHslider1 / fSlow1))));
+	int iSlow3 = (int)fSlow2;
+	int iSlow4 = (1 + iSlow3);
+	float fSlow5 = ((float)iSlow4 - fSlow2);
+	float fSlow6 = (dsp->fConst1 / fSlow1);
+	float fSlow7 = (dsp->fConst2 * fSlow1);
+	float fSlow8 = (fSlow2 - (float)iSlow3);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			dsp->iVec0[0] = 1;
+			dsp->fRec0[0] = fmodf((dsp->fRec0[1] + fSlow7), 1.f);
+			float fTemp0 = faustpower2_f(((2.f * dsp->fRec0[0]) - 1.f));
+			dsp->fVec1[0] = fTemp0;
+			float fTemp1 = (fSlow6 * ((fTemp0 - dsp->fVec1[1]) * (float)dsp->iVec0[1]));
+			dsp->fVec2[(dsp->IOTA & 4095)] = fTemp1;
+			output0[i] = (FAUSTFLOAT)(fSlow0 * (0.f - (((fSlow5 * dsp->fVec2[((dsp->IOTA - iSlow3) & 4095)]) + (fSlow8 * dsp->fVec2[((dsp->IOTA - iSlow4) & 4095)])) - fTemp1)));
+			dsp->iVec0[1] = dsp->iVec0[0];
+			dsp->fRec0[1] = dsp->fRec0[0];
+			dsp->fVec1[1] = dsp->fVec1[0];
+			dsp->IOTA = (dsp->IOTA + 1);
+
+		}
+
+	}
+
+}
+
+static void addHorizontalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_blsquare *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_blsquare_create(sp_blsquare **p)
+{
+    *p = malloc(sizeof(sp_blsquare));
+    return SP_OK;
+}
+
+int sp_blsquare_destroy(sp_blsquare **p)
+{
+    sp_blsquare *pp = *p;
+    blsquare *dsp = pp->ud;
+    deleteblsquare (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_blsquare_init(sp_data *sp, sp_blsquare *p)
+{
+    blsquare *dsp = newblsquare(); UIGlue UI;
+    p->argpos = 0;
+    UI.addHorizontalSlider= addHorizontalSlider;
+    UI.uiInterface = p;
+    buildUserInterfaceblsquare(dsp, &UI);
+    initblsquare(dsp, sp->sr);
+
+
+    p->freq = p->args[0];
+    p->amp = p->args[1];
+    p->width = p->args[2];
+
+    p->ud = dsp;
+    return SP_OK;
+}
+
+int sp_blsquare_compute(sp_data *sp, sp_blsquare *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    blsquare *dsp = p->ud;
+    SPFLOAT out1 = 0;
+    SPFLOAT *faust_out[] = {&out1};
+    SPFLOAT *faust_in[] = {in};
+    computeblsquare(dsp, 1, faust_in, faust_out);
+
+    *out = out1;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/bltriangle.c
@@ -1,0 +1,202 @@
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+float fmodf(float dummy0, float dummy1);
+static float faustpower2_f(float value) {
+	return (value * value);
+
+}
+
+typedef struct {
+
+	float fVec2[4096];
+	int iVec0[2];
+	float fRec1[2];
+	float fVec1[2];
+	float fRec0[2];
+	int fSamplingFreq;
+	int iConst0;
+	float fConst1;
+	FAUSTFLOAT fHslider0;
+	FAUSTFLOAT fHslider1;
+	float fConst2;
+	float fConst3;
+	float fConst4;
+	float fConst5;
+	int IOTA;
+
+} bltriangle;
+
+bltriangle* newbltriangle() {
+	bltriangle* dsp = (bltriangle*)malloc(sizeof(bltriangle));
+	return dsp;
+}
+
+void deletebltriangle(bltriangle* dsp) {
+	free(dsp);
+}
+
+void instanceInitbltriangle(bltriangle* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 2); i0 = (i0 + 1)) {
+			dsp->iVec0[i0] = 0;
+
+		}
+
+	}
+	dsp->iConst0 = min(192000, max(1, dsp->fSamplingFreq));
+	dsp->fConst1 = (4.f / (float)dsp->iConst0);
+	dsp->fHslider0 = (FAUSTFLOAT)440.;
+	dsp->fHslider1 = (FAUSTFLOAT)1.;
+	dsp->fConst2 = (float)dsp->iConst0;
+	dsp->fConst3 = (0.5f * dsp->fConst2);
+	dsp->fConst4 = (0.25f * dsp->fConst2);
+	dsp->fConst5 = (1.f / dsp->fConst2);
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec1[i1] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 2); i2 = (i2 + 1)) {
+			dsp->fVec1[i2] = 0.f;
+
+		}
+
+	}
+	dsp->IOTA = 0;
+	/* C99 loop */
+	{
+		int i3;
+		for (i3 = 0; (i3 < 4096); i3 = (i3 + 1)) {
+			dsp->fVec2[i3] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i4;
+		for (i4 = 0; (i4 < 2); i4 = (i4 + 1)) {
+			dsp->fRec0[i4] = 0.f;
+
+		}
+
+	}
+
+}
+
+void initbltriangle(bltriangle* dsp, int samplingFreq) {
+	instanceInitbltriangle(dsp, samplingFreq);
+}
+
+void buildUserInterfacebltriangle(bltriangle* dsp, UIGlue* interface) {
+	interface->addHorizontalSlider(interface->uiInterface, "freq", &dsp->fHslider0, 440.f, 0.f, 20000.f, 0.0001f);
+	interface->addHorizontalSlider(interface->uiInterface, "amp", &dsp->fHslider1, 1.f, 0.f, 1.f, 1e-05f);
+}
+
+void computebltriangle(bltriangle* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* output0 = outputs[0];
+	float fSlow0 = (float)dsp->fHslider0;
+	float fSlow1 = (dsp->fConst1 * (fSlow0 * (float)dsp->fHslider1));
+	float fSlow2 = max(fSlow0, 23.4489f);
+	float fSlow3 = max(0.f, min(2047.f, (dsp->fConst3 / fSlow2)));
+	int iSlow4 = (int)fSlow3;
+	int iSlow5 = (1 + iSlow4);
+	float fSlow6 = ((float)iSlow5 - fSlow3);
+	float fSlow7 = (dsp->fConst4 / fSlow2);
+	float fSlow8 = (dsp->fConst5 * fSlow2);
+	float fSlow9 = (fSlow3 - (float)iSlow4);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			dsp->iVec0[0] = 1;
+			dsp->fRec1[0] = fmodf((dsp->fRec1[1] + fSlow8), 1.f);
+			float fTemp0 = faustpower2_f(((2.f * dsp->fRec1[0]) - 1.f));
+			dsp->fVec1[0] = fTemp0;
+			float fTemp1 = (fSlow7 * ((fTemp0 - dsp->fVec1[1]) * (float)dsp->iVec0[1]));
+			dsp->fVec2[(dsp->IOTA & 4095)] = fTemp1;
+			dsp->fRec0[0] = (0.f - (((fSlow6 * dsp->fVec2[((dsp->IOTA - iSlow4) & 4095)]) + (fSlow9 * dsp->fVec2[((dsp->IOTA - iSlow5) & 4095)])) - ((0.999f * dsp->fRec0[1]) + fTemp1)));
+			output0[i] = (FAUSTFLOAT)(fSlow1 * dsp->fRec0[0]);
+			dsp->iVec0[1] = dsp->iVec0[0];
+			dsp->fRec1[1] = dsp->fRec1[0];
+			dsp->fVec1[1] = dsp->fVec1[0];
+			dsp->IOTA = (dsp->IOTA + 1);
+			dsp->fRec0[1] = dsp->fRec0[0];
+
+		}
+
+	}
+
+}
+
+static void addHorizontalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_bltriangle *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_bltriangle_create(sp_bltriangle **p)
+{
+    *p = malloc(sizeof(sp_bltriangle));
+    return SP_OK;
+}
+
+int sp_bltriangle_destroy(sp_bltriangle **p)
+{
+    sp_bltriangle *pp = *p;
+    bltriangle *dsp = pp->ud;
+    deletebltriangle (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_bltriangle_init(sp_data *sp, sp_bltriangle *p)
+{
+    bltriangle *dsp = newbltriangle(); UIGlue UI;
+    p->argpos = 0;
+    UI.addHorizontalSlider= addHorizontalSlider;
+    UI.uiInterface = p;
+    buildUserInterfacebltriangle(dsp, &UI);
+    initbltriangle(dsp, sp->sr);
+
+
+    p->freq = p->args[0];
+    p->amp = p->args[1];
+
+    p->ud = dsp;
+    return SP_OK;
+}
+
+int sp_bltriangle_compute(sp_data *sp, sp_bltriangle *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    bltriangle *dsp = p->ud;
+    SPFLOAT out1 = 0;
+    SPFLOAT *faust_out[] = {&out1};
+    SPFLOAT *faust_in[] = {in};
+    computebltriangle(dsp, 1, faust_in, faust_out);
+
+    *out = out1;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/brown.c
@@ -1,0 +1,47 @@
+/*
+ * Brown
+ *
+ * Brownian noise algorithm based on implementation found here:
+ * http://vellocet.com/dsp/noise/VRand.h
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_brown_create(sp_brown **p)
+{
+    *p = malloc(sizeof(sp_brown));
+    return SP_OK;
+}
+
+int sp_brown_destroy(sp_brown **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_brown_init(sp_data *sp, sp_brown *p)
+{
+    p->brown = 0.0;
+    return SP_OK;
+}
+
+int sp_brown_compute(sp_data *sp, sp_brown *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT r;
+    while (1) {
+        r = (sp_rand(sp) % SP_RANDMAX) / (SPFLOAT)(SP_RANDMAX);
+        r = ((r * 2) - 1) * 0.5;
+        p->brown += r;
+        if (p->brown < -8.0f || p->brown > 8.0f) {
+            p->brown -= r;
+        } else {
+            break;
+        }
+    }
+
+    *out = p->brown * 0.0625;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/butbp.c
@@ -1,0 +1,86 @@
+/*
+ * Butbp
+ *
+ * This is an implementation of a 2nd-order butterworth
+ * bandpass filter, discretized using the bilinear transform.
+ *
+ * For more information on using the BLT on 2nd-order
+ * butterworth filters, see:
+ *
+ * https://ccrma.stanford.edu/~jos/filters/Example_Second_Order_Butterworth_Lowpass.html
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define ROOT2 (1.4142135623730950488)
+
+#ifndef M_PI
+#define M_PI		3.14159265358979323846	/* pi */
+#endif
+
+#include "soundpipe.h"
+
+int sp_butbp_create(sp_butbp **p)
+{
+    *p = malloc(sizeof(sp_butbp));
+    return SP_OK;
+}
+
+int sp_butbp_destroy(sp_butbp **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_butbp_init(sp_data *sp, sp_butbp *p)
+{
+    p->freq = 1000;
+    p->bw = 10;
+    p->pidsr = M_PI / sp->sr * 1.0;
+    p->tpidsr = 2 * M_PI / sp->sr * 1.0;
+    p->a[5] = p->a[6] = 0.0;
+    p->lfreq = 0.0;
+    p->lbw = 0.0;
+    return SP_OK;
+}
+
+int sp_butbp_compute(sp_data *sp, sp_butbp *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT *a;
+    SPFLOAT t, y;
+    SPFLOAT bw, fr;
+
+    a = p->a;
+    if (p->bw <= 0.0) {
+       *out = 0;
+       return SP_OK;
+    }
+
+    bw = p->bw;
+    fr = p->freq;
+
+    if (bw != p->lbw || fr != p->lfreq) {
+        SPFLOAT c, d;
+        p->lfreq = fr;
+        p->lbw = bw;
+
+        /* Perform BLT and store components */
+        c = 1.0 / tan((SPFLOAT)(p->pidsr * bw));
+        d = 2.0 * cos((SPFLOAT)(p->tpidsr * fr));
+        a[0] = 1.0 / (1.0 + c);
+        a[1] = 0.0;
+        a[2] = -a[0];
+        a[3] = - c * d * a[0];
+        a[4] = (c - 1.0) * a[0];
+    }
+
+    /* a5 = t(n - 1); a6 = t(n - 2) */
+    t = *in - a[3]*a[5] - a[4]*a[6];
+    y = t*a[0] + a[1]*a[5] + a[2]*a[6];
+    a[6] = a[5];
+    a[5] = t;
+    *out = y;
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/butbr.c
@@ -1,0 +1,80 @@
+/*
+ * Butbr
+ *
+ * This is an implementation of a 2nd-order butterworth
+ * band-reject filter, discretized using the bilinear transform.
+ *
+ * For more information on using the BLT on 2nd-order
+ * butterworth filters, see:
+ *
+ * https://ccrma.stanford.edu/~jos/filters/Example_Second_Order_Butterworth_Lowpass.html
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+int sp_butbr_create(sp_butbr **p)
+{
+    *p = malloc(sizeof(sp_butbr));
+    return SP_OK;
+}
+
+int sp_butbr_destroy(sp_butbr **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_butbr_init(sp_data *sp, sp_butbr *p)
+{
+    p->freq = 1000;
+    p->bw = 1000;
+    p->pidsr = M_PI / sp->sr * 1.0;
+    p->tpidsr = 2 * M_PI / sp->sr * 1.0;
+    p->a[5] = p->a[6] = 0.0;
+    p->lfreq = 0.0;
+    p->lbw = 0.0;
+    return SP_OK;
+}
+
+int sp_butbr_compute(sp_data *sp, sp_butbr *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT *a;
+    SPFLOAT t, y;
+    SPFLOAT bw, freq;
+
+    if (p->bw <= 0.0) {
+      *out = 0;
+      return SP_OK;
+    }
+
+    a = p->a;
+    bw = p->bw;
+    freq = p->freq;
+
+    if (bw != p->lbw || freq != p->lfreq) {
+        SPFLOAT c, d;
+        p->lfreq = freq;
+        p->lbw = bw;
+        c = tan((SPFLOAT)(p->pidsr * bw));
+        d = 2.0 * cos((SPFLOAT)(p->tpidsr * freq));
+        a[0] = 1.0 / (1.0 + c);
+        a[1] = -d * a[0];
+        a[2] = a[0];
+        a[3] = a[1];
+        a[4] = (1.0 - c) * a[0];
+    }
+
+    t = *in - a[3]*a[5] - a[4]*a[6];
+    y = t*a[0] + a[1]*a[5] + a[2]*a[6];
+    a[6] = a[5];
+    a[5] = t;
+    *out = y;
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/buthp.c
@@ -1,0 +1,83 @@
+/*
+ * Buthp
+ *
+ * This is an implementation of a 2nd-order butterworth
+ * highpass filter, discretized using the bilinear transform.
+ *
+ * For more information on using the BLT on 2nd-order
+ * butterworth filters, see:
+ *
+ * https://ccrma.stanford.edu/~jos/filters/Example_Second_Order_Butterworth_Lowpass.html
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define ROOT2 (1.4142135623730950488)
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#include "soundpipe.h"
+
+static int filter(SPFLOAT *in, SPFLOAT *out, SPFLOAT *a)
+{
+    SPFLOAT t, y;
+
+    /* a5 = t(n - 1); a6 = t(n - 2) */
+    t = *in - a[3]*a[5] - a[4]*a[6];
+    y = t*a[0] + a[1]*a[5] + a[2]*a[6];
+    a[6] = a[5];
+    a[5] = t;
+    *out = y;
+    return SP_OK;
+}
+
+
+int sp_buthp_create(sp_buthp **p)
+{
+    *p = malloc(sizeof(sp_buthp));
+    return SP_OK;
+}
+
+int sp_buthp_destroy(sp_buthp **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_buthp_init(sp_data *sp, sp_buthp *p)
+{
+    p->freq = 1000;
+    p->pidsr = M_PI / sp->sr * 1.0;
+    p->a[5] = p->a[6] = 0.0;
+    p->lfreq = 0.0;
+    return SP_OK;
+}
+
+int sp_buthp_compute(sp_data *sp, sp_buthp *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (p->freq <= 0.0) {
+        *out = 0;
+        return SP_OK;
+    }
+
+    if (p->freq != p->lfreq) {
+        SPFLOAT *a, c;
+        a = p->a;
+        p->lfreq = p->freq;
+        /* derive C constant used in BLT */
+        c = tan((SPFLOAT)(p->pidsr * p->freq));
+
+        /* perform BLT, store components */
+        a[0] = 1.0 / (1.0 + c*ROOT2 + c*c);
+        a[1] = -2*a[0];
+        a[2] = a[0];
+        a[3] = 2.0 * (c*c - 1.0) * a[0];
+        a[4] = (1.0 - c*ROOT2 + c*c) * a[0];
+    }
+
+    filter(in, out, p->a);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/butlp.c
@@ -1,0 +1,81 @@
+/*
+ * Butlp
+ *
+ * This is an implementation of a 2nd-order butterworth
+ * lowpass filter, discretized using the bilinear transform.
+ *
+ * For more information on using the BLT on 2nd-order
+ * butterworth filters, see:
+ *
+ * https://ccrma.stanford.edu/~jos/filters/Example_Second_Order_Butterworth_Lowpass.html
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define ROOT2 (1.4142135623730950488)
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#include "soundpipe.h"
+
+static int filter(SPFLOAT *in, SPFLOAT *out, SPFLOAT *a)
+{
+    SPFLOAT t, y;
+    /* a5 = t(n - 1); a6 = t(n - 2) */
+    t = *in - a[3]*a[5] - a[4]*a[6];
+    y = t*a[0] + a[1]*a[5] + a[2]*a[6];
+    a[6] = a[5];
+    a[5] = t;
+    *out = y;
+    return SP_OK;
+}
+
+int sp_butlp_create(sp_butlp **p)
+{
+    *p = malloc(sizeof(sp_butlp));
+    return SP_OK;
+}
+
+int sp_butlp_destroy(sp_butlp **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_butlp_init(sp_data *sp, sp_butlp *p)
+{
+    p->freq = 1000;
+    p->pidsr = M_PI / sp->sr * 1.0;
+    p->a[5] = p->a[6] = 0.0;
+    p->lfreq = 0.0;
+    return SP_OK;
+}
+
+int sp_butlp_compute(sp_data *sp, sp_butlp *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (p->freq <= 0.0) {
+        *out = 0;
+        return SP_NOT_OK;
+    }
+
+    if (p->freq != p->lfreq) {
+        SPFLOAT *a, c;
+        a = p->a;
+        p->lfreq = p->freq;
+        /* derive C constant used in BLT */
+        c = 1.0 / tan((SPFLOAT)(p->pidsr * p->lfreq));
+
+        /* perform BLT, store components */
+        a[0] = 1.0 / (1.0 + c*ROOT2 + c*c);
+        a[1] = 2*a[0];
+        a[2] = a[0];
+        a[3] = 2.0 * (1.0 - c*c) * a[0];
+        a[4] = (1.0 - c*ROOT2 + c*c) * a[0];
+    }
+
+    filter(in, out, p->a);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/clamp.c
@@ -1,0 +1,31 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_clamp_create(sp_clamp **p)
+{
+    *p = malloc(sizeof(sp_clamp));
+    return SP_OK;
+}
+
+int sp_clamp_destroy(sp_clamp **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_clamp_init(sp_data *sp, sp_clamp *p)
+{
+    p->min = 0;
+    p->max = 1;
+    return SP_OK;
+}
+
+int sp_clamp_compute(sp_data *sp, sp_clamp *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in < p->min) *out = p->min;
+    else if (*in > p->max) *out = p->max;
+    else *out = *in;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/clock.c
@@ -1,0 +1,35 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_clock_create(sp_clock **p)
+{
+    *p = malloc(sizeof(sp_clock));
+    return SP_OK;
+}
+
+int sp_clock_destroy(sp_clock **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_clock_init(sp_data *sp, sp_clock *p)
+{
+    p->subdiv = 1.0;
+    p->bpm = 120;
+    p->counter = 0;
+    return SP_OK;
+}
+
+int sp_clock_compute(sp_data *sp, sp_clock *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = 0.0;
+    if (p->counter == 0 || *in != 0) {
+        *out = 1.0;
+        p->counter = (int)(sp->sr * (60.0 / (p->bpm * p->subdiv))) + 1;
+    }
+    p->counter--;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/compressor.c
@@ -1,0 +1,167 @@
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT SPFLOAT
+#endif
+
+typedef struct {
+	float fRec2[2];
+	float fRec1[2];
+	float fRec0[2];
+	int fSamplingFreq;
+	int iConst0;
+	float fConst1;
+	FAUSTFLOAT fHslider0;
+	FAUSTFLOAT fHslider1;
+	float fConst2;
+	FAUSTFLOAT fHslider2;
+	FAUSTFLOAT fHslider3;
+} compressor;
+
+static compressor* newcompressor() {
+	compressor* dsp = (compressor*)malloc(sizeof(compressor));
+	return dsp;
+}
+
+static void deletecompressor(compressor* dsp) {
+	free(dsp);
+}
+
+static void instanceInitcompressor(compressor* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->iConst0 = min(192000, max(1, dsp->fSamplingFreq));
+	dsp->fConst1 = (2.f / (float)dsp->iConst0);
+	dsp->fHslider0 = (FAUSTFLOAT)0.1;
+	dsp->fHslider1 = (FAUSTFLOAT)1.;
+	dsp->fConst2 = (1.f / (float)dsp->iConst0);
+	dsp->fHslider2 = (FAUSTFLOAT)0.1;
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 2); i0 = (i0 + 1)) {
+			dsp->fRec2[i0] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec1[i1] = 0.f;
+
+		}
+
+	}
+	dsp->fHslider3 = (FAUSTFLOAT)0.;
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 2); i2 = (i2 + 1)) {
+			dsp->fRec0[i2] = 0.f;
+
+		}
+
+	}
+
+}
+
+static void initcompressor(compressor* dsp, int samplingFreq) {
+	instanceInitcompressor(dsp, samplingFreq);
+}
+
+static void buildUserInterfacecompressor(compressor* dsp, UIGlue* interface) {
+	interface->addHorizontalSlider(interface->uiInterface, "ratio", &dsp->fHslider1, 1.f, 1.f, 40.f, 0.001f);
+	interface->addHorizontalSlider(interface->uiInterface, "thresh", &dsp->fHslider3, 0.f, -80.f, 0.f, 0.001f);
+	interface->addHorizontalSlider(interface->uiInterface, "atk", &dsp->fHslider0, 0.1f, 0.f, 10.f, 0.001f);
+	interface->addHorizontalSlider(interface->uiInterface, "rel", &dsp->fHslider2, 0.1f, 0.f, 10.f, 0.001f);
+}
+
+static void computecompressor(compressor* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* input0 = inputs[0];
+	FAUSTFLOAT* output0 = outputs[0];
+	float fSlow0 = (float)dsp->fHslider0;
+	float fSlow1 = exp((0.f - (dsp->fConst1 / fSlow0)));
+	float fSlow2 = ((1.f - fSlow1) * ((1.f / (float)dsp->fHslider1) - 1.f));
+	float fSlow3 = exp((0.f - (dsp->fConst2 / fSlow0)));
+	float fSlow4 = exp((0.f - (dsp->fConst2 / (float)dsp->fHslider2)));
+	float fSlow5 = (float)dsp->fHslider3;
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			float fTemp0 = (float)input0[i];
+			float fTemp1 = fabs(fTemp0);
+			float fTemp2 = ((dsp->fRec1[1] > fTemp1)?fSlow4:fSlow3);
+			dsp->fRec2[0] = ((dsp->fRec2[1] * fTemp2) + ((1.f - fTemp2) * fTemp1));
+			dsp->fRec1[0] = dsp->fRec2[0];
+			dsp->fRec0[0] = ((fSlow1 * dsp->fRec0[1]) + (fSlow2 * max(((20.f * log10(dsp->fRec1[0])) - fSlow5), 0.f)));
+			output0[i] = (FAUSTFLOAT)(pow(10.f, (0.05f * dsp->fRec0[0])) * fTemp0);
+			dsp->fRec2[1] = dsp->fRec2[0];
+			dsp->fRec1[1] = dsp->fRec1[0];
+			dsp->fRec0[1] = dsp->fRec0[0];
+
+		}
+
+	}
+
+}
+
+static void addHorizontalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_compressor *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_compressor_create(sp_compressor **p)
+{
+    *p = malloc(sizeof(sp_compressor));
+    return SP_OK;
+}
+
+int sp_compressor_destroy(sp_compressor **p)
+{
+    sp_compressor *pp = *p;
+    compressor *dsp = pp->faust;
+    deletecompressor (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_compressor_init(sp_data *sp, sp_compressor *p)
+{
+    compressor *dsp = newcompressor();
+    UIGlue UI;
+    p->argpos = 0;
+    UI.addHorizontalSlider= addHorizontalSlider;
+    UI.uiInterface = p;
+    buildUserInterfacecompressor(dsp, &UI);
+    initcompressor(dsp, sp->sr);
+
+
+    p->ratio = p->args[0];
+    p->thresh = p->args[1];
+    p->atk = p->args[2];
+    p->rel = p->args[3];
+
+    p->faust = dsp;
+    return SP_OK;
+}
+
+int sp_compressor_compute(sp_data *sp, sp_compressor *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    compressor *dsp = p->faust;
+    SPFLOAT *faust_out[] = {out};
+    SPFLOAT *faust_in[] = {in};
+    computecompressor(dsp, 1, faust_in, faust_out);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/count.c
@@ -1,0 +1,46 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_count_create(sp_count **p)
+{
+    *p = malloc(sizeof(sp_count));
+    return SP_OK;
+}
+
+int sp_count_destroy(sp_count **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_count_init(sp_data *sp, sp_count *p)
+{
+    p->count = 4;
+    p->curcount = -1;
+    p->mode = 0;
+    return SP_OK;
+}
+
+int sp_count_compute(sp_data *sp, sp_count *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in){
+        if (p->mode == 0) {
+            p->curcount = (p->curcount + 1) % p->count;
+        } else {
+            if (p->curcount == -2) {
+                *out = -2;
+                return SP_OK;
+            }
+            if (p->curcount >= p->count - 1) {
+                p->curcount = -2;
+            } else {
+                if (p->curcount == -1) p->curcount = 0;
+                else p->curcount++;
+            }
+        }
+    }
+    *out = p->curcount;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/crossfade.c
@@ -1,0 +1,28 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_crossfade_create(sp_crossfade **p)
+{
+    *p = malloc(sizeof(sp_crossfade));
+    return SP_OK;
+}
+
+int sp_crossfade_destroy(sp_crossfade **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_crossfade_init(sp_data *sp, sp_crossfade *p)
+{
+    p->pos = 0.5;
+    return SP_OK;
+}
+
+int sp_crossfade_compute(sp_data *sp, sp_crossfade *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out)
+{
+    *out = *in2 * p->pos + *in1 * (1 - p->pos);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/data/adsr.lua
@@ -1,0 +1,75 @@
+sptbl["adsr"] = {
+
+    files = {
+        module = "adsr.c",
+        header = "adsr.h",
+        example = "ex_adsr.c",
+    },
+
+    func = {
+        create = "sp_adsr_create",
+        destroy = "sp_adsr_destroy",
+        init = "sp_adsr_init",
+        compute = "sp_adsr_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT",
+                description = "Attack",
+                default = 0.1
+            },
+            {
+                name = "dec",
+                type = "SPFLOAT",
+                description ="Decay",
+                default = 0.1
+            },
+            {
+                name = "sus",
+                type = "SPFLOAT",
+                description ="Sustain (in range 0-1)",
+                default = 0.5
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT",
+                description ="Release",
+                default = 0.3
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[ADSR generator
+This is an ADSR generator whose curves are created using a one-pole low pass filter.
+
+NOTE: The attack, decay, and release parameters are "fuzzy" values that don't
+exactly correspond to duration in seconds. More accurately, they are special
+tau constant units that feed into the filter used to generate the envelope. The
+attack value specificly undergoes some "creative" modificiations in order to
+create snappier attack times. It is highly recommend to tune parameters by
+ear rather than to read the values literally.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "gate",
+            description = "Gate signal. This should be a steady state signal that jumps from 0 to 1."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "ADSR envelope."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/autowah.lua
@@ -1,0 +1,62 @@
+sptbl["autowah"] = {
+
+    files = {
+        module = "autowah.c",
+        header = "autowah.h",
+        example = "ex_autowah.c",
+    },
+
+    func = {
+        create = "sp_autowah_create",
+        destroy = "sp_autowah_destroy",
+        init = "sp_autowah_init",
+        compute = "sp_autowah_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "level",
+                type = "SPFLOAT*",
+                description = "Overall level (between 0 and 1)",
+                default = 0.1
+            },
+            {
+                name = "wah",
+                type = "SPFLOAT*",
+                description ="wah amount",
+                default = 0
+            },
+            {
+                name = "mix",
+                type = "SPFLOAT*",
+                description ="Wet/dry amount (100 = wet, 0 = dry)",
+                default = 100
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Automatic wah pedal
+	An automatic wah effect, ported from Guitarix via Faust. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Audio input"
+        },
+    },
+    
+    outputs = {
+        {
+            name = "output",
+            description = "Audio output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/biscale.lua
@@ -1,0 +1,57 @@
+sptbl["biscale"] = {
+
+    files = {
+        module = "biscale.c",
+        header = "biscale.h",
+        example = "ex_biscale.c",
+    },
+
+    func = {
+        create = "sp_biscale_create",
+        destroy = "sp_biscale_destroy",
+        init = "sp_biscale_init",
+        compute = "sp_biscale_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description = "Minimum value to scale to.",
+                default = 0
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description ="Maximum value to scale to.",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Bipolar Scale
+
+    This module scales from bipolar [-1, 1] to another range defined by min and max.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "Signal to be scaled."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Scaled signal out"
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/bitcrush.lua
@@ -1,0 +1,56 @@
+sptbl["bitcrush"] = {
+
+    files = {
+        module = "bitcrush.c",
+        header = "bitcrush.h",
+        example = "ex_bitcrush.c",
+    },
+
+    func = {
+        create = "sp_bitcrush_create",
+        destroy = "sp_bitcrush_destroy",
+        init = "sp_bitcrush_init",
+        compute = "sp_bitcrush_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "bitdepth",
+                type = "SPFLOAT",
+                description = "Bit depth. Expects an integer in the range of 1-16. Fractional values will be truncated.",
+                default = 8
+            },
+            {
+                name = "srate",
+                type = "SPFLOAT",
+                description ="Sampling rate.",
+                default = 10000
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Digital signal degradation
+
+    Bitcrusher will digitally degrade a signal by altering the bitdepth and sampling-rate. ]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output"
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/blsaw.lua
@@ -1,0 +1,57 @@
+sptbl["blsaw"] = {
+
+    files = {
+        module = "blsaw.c",
+        header = "blsaw.h",
+        example = "ex_blsaw.c",
+    },
+
+    func = {
+        create = "sp_blsaw_create",
+        destroy = "sp_blsaw_destroy",
+        init = "sp_blsaw_init",
+        compute = "sp_blsaw_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT*",
+                description = "Frequency, (range 0-20000)",
+                default = 440
+            },
+            {
+                name = "amp",
+                type = "SPFLOAT*",
+                description ="Amplitude (range 0-1).",
+		default = 1.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Bandlimited sawtooth oscillator
+This is a bandlimited sawtooth oscillator ported from the "sawtooth" function from the Faust
+programming language.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This doesn't do anything."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/blsquare.lua
@@ -1,0 +1,63 @@
+sptbl["blsquare"] = {
+
+    files = {
+        module = "blsquare.c",
+        header = "blsquare.h",
+        example = "ex_blsquare.c",
+    },
+
+    func = {
+        create = "sp_blsquare_create",
+        destroy = "sp_blsquare_destroy",
+        init = "sp_blsquare_init",
+        compute = "sp_blsquare_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT*",
+                description = "Frequency, (range 0-20000)",
+                default = 440
+            },
+            {
+                name = "amp",
+                type = "SPFLOAT*",
+                description ="Amplitude (range 0-1).",
+		default = 1.0
+            },
+            {
+                name = "width",
+                type = "SPFLOAT*",
+                description ="Duty cycle width (range 0-1).",
+		default = 0.5
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Bandlimited square oscillator
+This is a bandlimited square oscillator ported from the "squaretooth" function from the Faust
+programming language.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This doesn't do anything."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/bltriangle.lua
@@ -1,0 +1,57 @@
+sptbl["bltriangle"] = {
+
+    files = {
+        module = "bltriangle.c",
+        header = "bltriangle.h",
+        example = "ex_bltriangle.c",
+    },
+
+    func = {
+        create = "sp_bltriangle_create",
+        destroy = "sp_bltriangle_destroy",
+        init = "sp_bltriangle_init",
+        compute = "sp_bltriangle_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT*",
+                description = "Frequency, (range 0-20000)",
+                default = 440
+            },
+            {
+                name = "amp",
+                type = "SPFLOAT*",
+                description ="Amplitude (range 0-1).",
+		default = 1.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Bandlimited triangle oscillator
+This is a bandlimited triangle oscillator ported from the "triangletooth" function from the Faust
+programming language.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This doesn't do anything."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/brown.lua
@@ -1,0 +1,37 @@
+sptbl["brown"] = {
+
+    files = {
+        module = "brown.c",
+        header = "brown.h",
+        example = "ex_brown.c",
+    },
+
+    func = {
+        create = "sp_brown_create",
+        destroy = "sp_brown_destroy",
+        init = "sp_brown_init",
+        compute = "sp_brown_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[Brownian noise generator.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Brownian noise output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/butbp.lua
@@ -1,0 +1,54 @@
+sptbl["butbp"] = {
+
+    files = {
+        module = "butbp.c",
+        header = "butbp.h",
+        example = "ex_butbp.c",
+    },
+
+    func = {
+        create = "sp_butbp_create",
+        destroy = "sp_butbp_destroy",
+        init = "sp_butbp_init",
+        compute = "sp_butbp_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Center Frequency. (in Hertz)",
+                default = 1000
+            },
+            {
+                name = "bw",
+                type = "SPFLOAT",
+                description = "Bandwidth. (in Hertz)",
+                default = 10
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Bandpass Butterworth filter]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input signal that the filter should be applied to."
+        },
+    },
+
+    outputs = {
+        {
+            name = "output",
+            description = "Output signal (input modified by the filter)."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/butbr.lua
@@ -1,0 +1,54 @@
+sptbl["butbr"] = {
+
+    files = {
+        module = "butbr.c",
+        header = "butbr.h",
+        example = "ex_butbr.c",
+    },
+
+    func = {
+        create = "sp_butbr_create",
+        destroy = "sp_butbr_destroy",
+        init = "sp_butbr_init",
+        compute = "sp_butbr_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Center Frequency. (in Hertz)",
+                default = 1000
+            },
+            {
+                name = "bw",
+                type = "SPFLOAT",
+                description = "Bandwidth. (in Hertz)",
+                default = 1000
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Band-reject Butterworth filter]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "output",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/buthp.lua
@@ -1,0 +1,48 @@
+sptbl["buthp"] = {
+
+    files = {
+        module = "buthp.c",
+        header = "buthp.h",
+        example = "ex_buthp.c",
+    },
+
+    func = {
+        create = "sp_buthp_create",
+        destroy = "sp_buthp_destroy",
+        init = "sp_buthp_init",
+        compute = "sp_buthp_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Cutoff Frequency.",
+                default = 1000
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Highpass Butterworth filter]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "output",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/butlp.lua
@@ -1,0 +1,48 @@
+sptbl["butlp"] = {
+
+    files = {
+        module = "butlp.c",
+        header = "butlp.h",
+        example = "ex_butlp.c",
+    },
+
+    func = {
+        create = "sp_butlp_create",
+        destroy = "sp_butlp_destroy",
+        init = "sp_butlp_init",
+        compute = "sp_butlp_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Cutoff Frequency.",
+                default = 1000
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Lowpass Butterworth filter]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "output",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/clamp.lua
@@ -1,0 +1,61 @@
+sptbl["clamp"] = {
+
+    files = {
+        module = "clamp.c",
+        header = "clamp.h",
+        example = "ex_clamp.c",
+    },
+
+    func = {
+        create = "sp_clamp_create",
+        destroy = "sp_clamp_destroy",
+        init = "sp_clamp_init",
+        compute = "sp_clamp_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description = "Minimum value.",
+                default = 0
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description ="Maximum value.",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Performs a clamp operation on an input signal.
+
+This module performs what is known as a "clamp" operation, which sets the
+bounds of a signal in between a minimum and a maximum value. Anything exceeding
+the bounds in either direction will be set to the closest valid value. In
+other words: if x is less than minimum, set x to the minimum;
+if x is greater than the maximum, set x to be the maximum.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input audio signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Output audio signal."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/clock.lua
@@ -1,0 +1,56 @@
+sptbl["clock"] = {
+
+    files = {
+        module = "clock.c",
+        header = "clock.h",
+        example = "ex_clock.c",
+    },
+
+    func = {
+        create = "sp_clock_create",
+        destroy = "sp_clock_destroy",
+        init = "sp_clock_init",
+        compute = "sp_clock_compute",
+    },
+
+    params = {
+
+        optional = {
+            {
+                name = "bpm",
+                type = "SPFLOAT",
+                description = "Clock tempo, in beats per minute.",
+                default = 120
+            },
+            {
+                name = "subdiv",
+                type = "SPFLOAT",
+                description ="Clock subdivision. 2 = eighths, 4 = 16ths, etc.",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Resettable clock with subdivisions
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When non-zero, will reset clock"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Clock output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/compressor.lua
@@ -1,0 +1,69 @@
+sptbl["compressor"] = {
+
+    files = {
+        module = "compressor.c",
+        header = "compressor.h",
+        example = "ex_compressor.c",
+    },
+
+    func = {
+        create = "sp_compressor_create",
+        destroy = "sp_compressor_destroy",
+        init = "sp_compressor_init",
+        compute = "sp_compressor_compute",
+    },
+
+    params = {
+        mandatory = {
+        },
+
+        optional = {
+            {
+                name = "ratio",
+                type = "SPFLOAT*",
+                description = "Ratio to compress with, a value > 1 will compress",
+                default = 1
+            },
+            {
+                name = "thresh",
+                type = "SPFLOAT*",
+                description ="Threshold (in dB) 0 = max",
+                default = 0
+            },
+            {
+                name = "atk",
+                type = "SPFLOAT*",
+                description ="Compressor attack",
+                default = 0.1
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT*",
+                description ="Compressor release",
+                default = 0.1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Compressor]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/count.lua
@@ -1,0 +1,57 @@
+sptbl["count"] = {
+
+    files = {
+        module = "count.c",
+        header = "count.h",
+        example = "ex_count.c",
+    },
+
+    func = {
+        create = "sp_count_create",
+        destroy = "sp_count_destroy",
+        init = "sp_count_init",
+        compute = "sp_count_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "count",
+                type = "SPFLOAT",
+                description = "Number to count up to (count - 1). Decimal points will be truncated.",
+                default = 4
+            },
+            {
+                name = "mode",
+                type = "SPFLOAT",
+                description = "Counting mode. 0 = wrap-around, 1 = count up to N -1, then stop and spit out -1",
+                default = 0
+            },
+        },
+    },
+
+    modtype = "module",
+
+    description = [[Trigger-based fixed counter
+
+    The signal output will count from 0 to [N-1], and then
+repeat itself. Count will start when it has been triggered, otherwise it will be -1.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When non-zero, this value will increment."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/crossfade.lua
@@ -1,0 +1,57 @@
+sptbl["crossfade"] = {
+
+    files = {
+        module = "crossfade.c",
+        header = "crossfade.h",
+        example = "ex_crossfade.c",
+    },
+
+    func = {
+        create = "sp_crossfade_create",
+        destroy = "sp_crossfade_destroy",
+        init = "sp_crossfade_init",
+        compute = "sp_crossfade_compute",
+    },
+
+    params = {
+        mandatory = {
+        },
+
+        optional = {
+            {
+                name = "pos",
+                type = "SPFLOAT",
+                description = "Crossfade position. 0 = all signal 1, 1 = all signal 2",
+                default = 0.5
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Crossfade two signals. 
+This module will perform a linear crossfade between two input signals.
+]],
+
+    ninputs = 2,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in1",
+            description = "Input signal 1."
+        },
+        {
+            name = "in2",
+            description = "Input signal 2."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/delay.lua
@@ -1,0 +1,58 @@
+sptbl["delay"] = {
+
+    files = {
+        module = "delay.c",
+        header = "delay.h",
+        example = "ex_delay.c",
+    },
+
+    func = {
+        create = "sp_delay_create",
+        destroy = "sp_delay_destroy",
+        init = "sp_delay_init",
+        compute = "sp_delay_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "time",
+                type = "SPFLOAT",
+                description = "Delay time, in seconds.",
+                default = 1.0
+            }
+        },
+
+        optional = {
+            {
+                name = "feedback",
+                type = "SPFLOAT",
+                description = "Feedback amount. Should be a value between 0-1.",
+                default = 0.0
+            }
+        }
+
+    },
+
+    modtype = "module",
+
+    description = [[Adds a delay to an incoming signal with optional feedback.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/diode.lua
@@ -1,0 +1,62 @@
+sptbl["diode"] = {
+
+    files = {
+        module = "diode.c",
+        header = "diode.h",
+        example = "ex_diode.c",
+    },
+
+    func = {
+        create = "sp_diode_create",
+        destroy = "sp_diode_destroy",
+        init = "sp_diode_init",
+        compute = "sp_diode_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "",
+                default = 1000
+            },
+            {
+                name = "res",
+                type = "SPFLOAT",
+                description ="",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Diode-ladder virtual analogue low-pass filter
+This is a diode-ladder filter designed by Will Pirkle. 
+More information can be found in this paper here: 
+http://www.willpirkle.com/Downloads/AN-6DiodeLadderFilter.pdf
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out_left",
+            description = "Stereo left output for diode."
+        },
+        {
+            name = "out_right",
+            description = "Stereo right output for diode."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/dmetro.lua
@@ -1,0 +1,54 @@
+sptbl["dmetro"] = {
+
+    files = {
+        module = "dmetro.c",
+        header = "dmetro.h",
+        example = "ex_dmetro.c",
+    },
+
+    func = {
+        create = "sp_dmetro_create",
+        destroy = "sp_dmetro_destroy",
+        init = "sp_dmetro_init",
+        compute = "sp_dmetro_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "time",
+                type = "SPFLOAT",
+                description ="Time between triggers (in seconds). This will update at the start of each trigger.",
+                default = 1.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Delta Metro
+
+    Produce a set of triggers spaced apart by time.
+
+An implementation note: while dmetro does indeed use sample
+precision, it will intentionally add 1 sample to the
+duration time as a way to avoid divide-by-zero errors. A
+dmetro of one second will really be one second and 1 sample.
+For most musical purposes, this is negligible. For more
+scientific purposes, this could cause problems, and it is
+recommended to find or build another module.]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Trigger output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/dtrig.lua
@@ -1,0 +1,71 @@
+sptbl["dtrig"] = {
+
+    files = {
+        module = "dtrig.c",
+        header = "dtrig.h",
+        example = "ex_dtrig.c",
+    },
+
+    func = {
+        create = "sp_dtrig_create",
+        destroy = "sp_dtrig_destroy",
+        init = "sp_dtrig_init",
+        compute = "sp_dtrig_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "ft",
+                type = "sp_ftbl *",
+                description = "An ftable containing times in seconds.",
+                default = "N/A"
+            }
+        },
+
+        optional = {
+            {
+                name = "loop",
+                type = "int",
+                description = "When set to 1, dtrig will wrap around and loop again.",
+                default = 0
+            },
+            {
+                name = "delay",
+                type = "SPFLOAT",
+                description = "This sets a delay (in seconds) on the triggered output when it is initially triggered. This is useful for rhythmic sequences with rests in the beginnings.",
+                default = 0
+            },
+            {
+                name = "scale",
+                type = "SPFLOAT",
+                description = "Scales the timing signal. A scale of 1 is normal, a scale of 2 will double the duration, and a scale of 0.5 will halve it.",
+                default = 0
+            }
+        }
+    },
+
+    modtype = "module",
+
+    description = [[ Delta trigger
+
+    This is able to create spaced out triggers. It is set off by a single trigger.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "trigger input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "These are the triggered outputs."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/expon.lua
@@ -1,0 +1,64 @@
+sptbl["expon"] = {
+
+    files = {
+        module = "expon.c",
+        header = "expon.h",
+        example = "ex_expon.c",
+    },
+
+    func = {
+        create = "sp_expon_create",
+        destroy = "sp_expon_destroy",
+        init = "sp_expon_init",
+        compute = "sp_expon_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "a",
+                type = "SPFLOAT",
+                description = "Inital point.",
+                default = 1.0
+            },
+            {
+                name = "dur",
+                type = "SPFLOAT",
+                description = "Duration (in seconds)",
+                default = 1.0
+            },
+            {
+                name = "b",
+                type = "SPFLOAT",
+                description = "End point",
+                default = 1.0
+            }
+        },
+
+    },
+
+    modtype = "module",
+
+    description = [[Produce a line segment with exponential slope
+This will generate a line from value A to value B in given amount of time. 
+When it reaches it's target, it will stay at that value. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When nonzero, will retrigger line segment"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/foo.lua
@@ -1,0 +1,103 @@
+sptbl["foo"] = {
+
+    files = {
+        module = "foo.c",
+        header = "foo.h",
+        example = "ex_foo.c",
+    },
+
+    func = {
+        create = "sp_foo_create",
+        destroy = "sp_foo_destroy",
+        init = "sp_foo_init",
+        compute = "sp_foo_compute",
+        other = {
+            sp_foo_set = {
+                description = "foo_set description goes here.",
+                args = {
+                    {
+                        name = "var1",
+                        type = "SPFLOAT",
+                        description = "This is the first parameter",
+                        default = 1.5
+                    },
+                    {
+                        name = "var2",
+                        type = "SPFLOAT",
+                        description = "This is the second parameter",
+                        default = 1.5
+                    }
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "bar",
+                type = "sp_ftbl *",
+                description = "This is a mandatory table value. It does not have a default value, so we set it to 'N/A'. Any that does not or cannot have a default value should set this default value to 'N/A'.",
+                default = "N/A"
+            },
+            {
+                name = "bar2",
+                type = "SPFLOAT",
+                description = "This is a mandatory parameter. In soundpipe, users will always need to specify this value, but a default value has been giving in case it is needed to write more complicated engines in the future.",
+                default = 123
+            }
+        },
+
+        optional = {
+            {
+                name = "blah_1",
+                type = "SPFLOAT",
+                description = "This is an optional parameter. These values are always set to a value by default, and can be set after the init function has been called.",
+                default = 1.5
+            },
+            {
+                name = "blah_2",
+                type = "SPFLOAT",
+                description ="This is yet another optional parameter...",
+                default = 456.7
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[A short title describing the module
+
+    This is a description of the entire module.  This is not a real module. This description should be a comprehensive sumary of what this function does.
+
+Inside the Lua table, this is expressed as a multiline string, however it does not adhere to the tradtional 80 column rule found in programming.
+
+Write as much text as needed here...
+]],
+
+    ninputs = 2,
+    noutputs = 2,
+
+    inputs = {
+        {
+            name = "clock",
+            description = "this is the clock source for a made up plugin."
+        },
+        {
+            name = "input",
+            description = "this is the audio input for a made up plugin."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out_left",
+            description = "Stereo left output for foo."
+        },
+        {
+            name = "out_right",
+            description = "Stereo right output for foo."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/gen_padsynth.lua
@@ -1,0 +1,46 @@
+sptbl["gen_padsynth"] = {
+
+    files = {
+        module = "ftbl.c",
+        header = "ftbl.h",
+        example = "extra/ex_padsynth.c",
+    },
+
+    func = {
+        name = "sp_gen_padsynth",
+    },
+
+    params = {
+        {
+            name = "amps",
+            type = "sp_ftbl *",
+            description = [[ftable of amplitudes to use]],
+            default = "N/A"
+        },
+        {
+            name = "f",
+            type = "SPFLOAT",
+            description = [[Base frequency.]],
+            default = 440.0
+        },
+        {
+            name = "bw",
+            type = "SPFLOAT",
+            description = [[Bandwidth.]],
+            default = 40.0
+        },
+    },
+
+    modtype = "gen",
+
+    description = [[An implementation of the Padsynth Algorithm by Paul Nasca. 
+
+This is a basic implemenation of the padsynth algorithm. More information can be found here:
+
+http://zynaddsubfx.sourceforge.net/doc/PADsynth/PADsynth.htm
+
+This gen routine requires libfftw, and is not compiled by default. See config.mk for more info.
+    
+    ]],
+
+}
--- /dev/null
+++ b/modules/data/gen_rand.lua
@@ -1,0 +1,26 @@
+sptbl["gen_rand"] = {
+
+    files = {
+        module = "ftbl.c",
+        header = "ftbl.h",
+        example = "ex_gen_rand.c",
+    },
+
+    func = {
+        name = "sp_gen_rand",
+    },
+
+    params = {
+        {
+            name = "argstring",
+            type = "char *",
+            description = [[A string of value pairs. The first value is the value, then the probability.]],
+            default = "1 0.5 2 0.5"
+        },
+    },
+
+    modtype = "gen",
+
+    description = [[Generates a user defined random number distribution.]],
+
+}
--- /dev/null
+++ b/modules/data/gen_scrambler.lua
@@ -1,0 +1,29 @@
+sptbl["gen_scrambler"] = {
+
+    files = {
+        module = "ftbl.c",
+        header = "ftbl.h",
+        example = "ex_scrambler.c",
+    },
+
+    func = {
+        name = "sp_gen_scrambler",
+    },
+    params = {
+        {
+            name = "dest",
+            type = "&sp_ftbl *",
+            description = [[destination ftable]],
+            default = 123456
+        },
+    },
+
+    modtype = "gen",
+
+    description = [[Scrambles phase of ftable.
+This gen routine will copy the ftable, apply an FFT, apply
+a random phase, and then do an inverse FFT. This effect 
+is ideal for creating pad-like sounds. 
+]],
+
+}
--- /dev/null
+++ b/modules/data/gen_sine.lua
@@ -1,0 +1,17 @@
+sptbl["gen_sine"] = {
+
+    files = {
+        module = "ftbl.c",
+        header = "ftbl.h",
+        example = "ex_gen_sine.c",
+    },
+
+    func = {
+        name = "sp_gen_sine",
+    },
+
+    modtype = "gen",
+
+    description = [[generates a sampled sinusoid]],
+
+}
--- /dev/null
+++ b/modules/data/gen_triangle.lua
@@ -1,0 +1,17 @@
+sptbl["gen_triangle"] = {
+
+    files = {
+        module = "ftbl.c",
+        header = "ftbl.h",
+        example = "ex_gen_triangle.c",
+    },
+
+    func = {
+        name = "sp_gen_triangle",
+    },
+
+    modtype = "gen",
+
+    description = [[generates a sampled triangle wave]],
+
+}
--- /dev/null
+++ b/modules/data/in.lua
@@ -1,0 +1,58 @@
+sptbl["in"] = {
+
+    files = {
+        module = "in.c",
+        header = "in.h",
+        example = "ex_in.c",
+    },
+
+    func = {
+        create = "sp_in_create",
+        destroy = "sp_in_destroy",
+        init = "sp_in_init",
+        compute = "sp_in_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[Reads from standard input.
+
+    Expects type of SPFLOAT, which by default is a float. If the input data is larger than the number of samples, you will get a complaint about a broken pipe (but it will still work). If there is no input data from STDIN, it will hang.
+
+
+
+
+The expected use case of sp_in is to utilize pipes from the commandline, like so:
+
+
+
+
+cat /dev/urandom | ./my_program
+
+
+
+
+Assuming my_program is using sp_in, this will write /dev/urandom (essentially white noise) to an audio file.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This doesn't do anything."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/incr.lua
@@ -1,0 +1,72 @@
+sptbl["incr"] = {
+
+    files = {
+        module = "incr.c",
+        header = "incr.h",
+        example = "ex_incr.c",
+    },
+
+    func = {
+        create = "sp_incr_create",
+        destroy = "sp_incr_destroy",
+        init = "sp_incr_init",
+        compute = "sp_incr_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "val",
+                type = "SPFLOAT",
+                description = "Initial value",
+                default = 0.5
+            },
+        },
+        optional = {
+            {
+                name = "step",
+                type = "SPFLOAT",
+                description = "Step value to increment by.",
+                default = 0.1
+            },
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description = "Minimum value",
+                default = 0
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description = "Maximum value",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Trigger-based Incrementor
+When triggered, this module will increment and decrement a value bounded between a min
+and max. Initially, this was designed for the specific use case of interfacing with the
+griffin knob. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Trigger signal. When positive, the value will increase. When negative, the value will decrease."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "incr output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/jcrev.lua
@@ -1,0 +1,48 @@
+sptbl["jcrev"] = {
+
+    files = {
+        module = "jcrev.c",
+        header = "jcrev.h",
+        example = "ex_jcrev.c",
+    },
+
+    func = {
+        create = "sp_jcrev_create",
+        destroy = "sp_jcrev_destroy",
+        init = "sp_jcrev_init",
+        compute = "sp_jcrev_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[John Chowning reverberator
+
+    This is was built using the JC reverb implentation found in FAUST. According to the source code, the specifications for
+this implementation were found on an old SAIL DART backup tape.
+
+  This class is derived from the CLM JCRev function, which is based on the use of
+  networks of simple allpass and comb delay filters.  This class implements three series
+  allpass units, followed by four parallel comb filters, and two decorrelation delay lines in
+  parallel at the output.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal Output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/line.lua
@@ -1,0 +1,64 @@
+sptbl["line"] = {
+
+    files = {
+        module = "line.c",
+        header = "line.h",
+        example = "ex_line.c",
+    },
+
+    func = {
+        create = "sp_line_create",
+        destroy = "sp_line_destroy",
+        init = "sp_line_init",
+        compute = "sp_line_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "a",
+                type = "SPFLOAT",
+                description = "Inital point.",
+                default = 0.0
+            },
+            {
+                name = "dur",
+                type = "SPFLOAT",
+                description = "Duration (in seconds)",
+                default = 0.5
+            },
+            {
+                name = "b",
+                type = "SPFLOAT",
+                description = "End point",
+                default = 1.0
+            }
+        },
+
+    },
+
+    modtype = "module",
+
+    description = [[Produce a line segment with linear slope
+This will generate a line from value A to value B in given amount of time. 
+When it reaches it's target, it will stay at that value. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When non-zero, will retrigger the line slope."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/lpc.lua
@@ -1,0 +1,82 @@
+sptbl["lpc"] = {
+
+    files = {
+        module = "lpc.c",
+        header = "lpc.h",
+        example = "ex_lpc.c",
+    },
+
+    func = {
+        create = "sp_lpc_create",
+        destroy = "sp_lpc_destroy",
+        init = "sp_lpc_init",
+        compute = "sp_lpc_compute",
+        other = {
+            sp_lpc_synth = {
+                description = [[Toggle synth mode. 
+                Instead of reading an input, manipulate the parameters in  
+                a scaled ftable.]],
+                args = { 
+                    {
+                        name = "ft",
+                        type = "sp_ftbl *",
+                        description = "ftable of size 7",
+                        default = "N/A"
+                    }
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "framesize",
+                type = "int",
+                description = "Sets the frame size for the encoder.",
+                default = 512
+            },
+        },
+
+        optional = {
+        }
+    },
+
+    modtype = "module",
+
+    description = [[A linear predictive coding filter.
+This module is a wrapper for the open source library openlpc, which implements
+the LPC10 audio codec optimized for speech signals. This module takes in an
+input signal, downsamples it, and produces a decoded LPC10 audio signal, which
+has a similar sound to that of a speak and spell. In this context, the LPC
+signal is meant to be more of a audio effect rather than a utility for
+communication. 
+
+Because the LPC10 encoder
+relies on frames for encoding, the output signal has a few milliseconds of
+delay. The delay can be calculated in seconds as (framesize * 4) / samplerate.
+
+In addition to using the LPC as a decoder/encoder, this module can also be 
+set to synth mode. Instead of reading from an input signal, the LPC can
+instead read from parameters set directly in a scaled ftable. 
+
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input signal to be processed with LPC."
+        },
+    },
+
+    outputs = {
+        {
+            name = "output",
+            description = "LPC encoded signal."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/maygate.lua
@@ -1,0 +1,57 @@
+sptbl["maygate"] = {
+
+    files = {
+        module = "maygate.c",
+        header = "maygate.h",
+        example = "ex_maygate.c",
+    },
+
+    func = {
+        create = "sp_maygate_create",
+        destroy = "sp_maygate_destroy",
+        init = "sp_maygate_init",
+        compute = "sp_maygate_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "prob",
+                type = "SPFLOAT",
+                description = "Probability of maygate. This is a value between 0-1. The closer to 1, the more likely the maygate will let a signal through.",
+                default = 0
+            },
+
+            {
+                name = "mode",
+                type = "int",
+                description = "If mode is nonzero, maygate will output one sample triggers instead of a gate signal.",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[A randomly open or closed "maybe gate"
+
+    It takes in a trigger, and then it will randomly decide to turn the gate on or not. One particular application for maygate is to arbitrarily turn on/off sends to effects. One specific example of this could be a randomized reverb throw on a snare.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "This expects a trigger signal."
+        }
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal out."
+        }
+    }
+
+}
--- /dev/null
+++ b/modules/data/metro.lua
@@ -1,0 +1,50 @@
+sptbl["metro"] = {
+
+    files = {
+        module = "metro.c",
+        header = "metro.h",
+        --example = "ex_tone.c",
+    },
+
+    func = {
+        create = "sp_metro_create",
+        destroy = "sp_metro_destroy",
+        init = "sp_metro_init",
+        compute = "sp_metro_compute",
+    },
+
+    params = {
+        optional = {
+             {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "The frequency to repeat.",
+                default = 2.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Metronome
+
+    Metro produces a series of 1-sample ticks at a regular rate. Typically, this is used alongside trigger-driven modules.]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This input doesn't do anything"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal out."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/noise.lua
@@ -1,0 +1,48 @@
+sptbl["noise"] = {
+
+    files = {
+        module = "noise.c",
+        header = "noise.h",
+        example = "ex_noise.c",
+    },
+
+    func = {
+        create = "sp_noise_create",
+        destroy = "sp_noise_destroy",
+        init = "sp_noise_init",
+        compute = "sp_noise_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "amp",
+                type = "SPFLOAT",
+                description = "Amplitude. (Value between 0-1).",
+                default = 1.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[White noise generator]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This doesn't do anything"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/nsmp.lua
@@ -1,0 +1,100 @@
+sptbl["nsmp"] = {
+
+    files = {
+        module = "nsmp.c",
+        header = "nsmp.h",
+        example = "ex_nsmp.c",
+    },
+
+    func = {
+        create = "sp_nsmp_create",
+        destroy = "sp_nsmp_destroy",
+        init = "sp_nsmp_init",
+        compute = "sp_nsmp_compute",
+        other = {
+            sp_nsmp_print_index = {
+                description = "Prints the index and their corresponding keyword",
+                args = {
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "ft",
+                type = "sp_ftbl*",
+                description = "ftbl of the audio file. It should be mono.",
+                default = "N/A"
+            },
+            {
+                name = "sr",
+                type = "int",
+                description = "samplerate.",
+                default = "N/A"
+            },
+            {
+                name = "init",
+                type = "const char*",
+                description = "ini file.",
+                default = "N/A"
+            }
+        },
+
+        optional = {
+            {
+                name = "index",
+                type = "uint32_t",
+                description = "This is an optional parameter. These values are always set to a value by default, and can be set after the init function has been called.",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Nanosamp: a tiny sampler built for Soundpipe
+
+    A nanosamp file is comprised of a mono audio file and an ini file. Nanosamp is geared towards percussive and found sound sample players, and is intended to be combined with soundpipe modules.
+
+The ini file contains mappings that correspond to the audio file. Such an entry would look like this:
+
+
+
+[keyword]
+
+pos = 2.3
+
+size = 3
+
+
+</pre>
+In this instance, an entry called "keyword" has been made, starting at 2.3 seconds in the
+audio file, with a length of 3 seconds. An example file oneart.ini has been created in the
+examples folder.
+
+The SoundPipe implementation of nanosamp will automatically index the entries
+in the order they appear in the INI file and must be selected this way by changing the index
+parameter. Soundpipe will only select the new entry when the trigger input is a non-zero value.
+
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trigger",
+            description = "Trigger input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "signal out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/osc.lua
@@ -1,0 +1,68 @@
+sptbl["osc"] = {
+
+    files = {
+        module = "osc.c",
+        header = "osc.h",
+        example = "ex_osc.c",
+    },
+
+    func = {
+        create = "sp_osc_create",
+        destroy = "sp_osc_destroy",
+        init = "sp_osc_init",
+        compute = "sp_osc_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "tbl",
+                type = "sp_ftbl *",
+                description = "Wavetable to read from. Note: the size of this table must be a power of 2.",
+                default = "N/A"
+            },
+            {
+                name = "phase",
+                type = "SPFLOAT",
+                description ="Initial phase of waveform, expects a value 0-1",
+                default = 0
+            }
+        },
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Frequency (in Hz)",
+                default = 440
+            },
+            {
+                name = "amp",
+                type = "SPFLOAT",
+                description ="Amplitude (typically a value between 0 and 1).",
+                default = 0.2
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[ Table-lookup oscilator with linear interpolation]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This does nothing."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/oscmorph.lua
@@ -1,0 +1,84 @@
+sptbl["oscmorph"] = {
+
+    files = {
+        module = "oscmorph.c",
+        header = "oscmorph.h",
+        example = "ex_oscmorph.c",
+    },
+
+    func = {
+        create = "sp_oscmorph_create",
+        destroy = "sp_oscmorph_destroy",
+        init = "sp_oscmorph_init",
+        compute = "sp_oscmorph_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "tbl",
+                type = "sp_ftbl **",
+                description = "An array of ftables to read from. Note: the size of these tables must be a power of 2 (and the same size as well).",
+                default = "N/A"
+            },
+            {
+                name = "nft",
+                type = "int",
+                description ="Number of ftbls",
+                default = 2
+            },
+            {
+                name = "phase",
+                type = "SPFLOAT",
+                description ="Initial phase of waveform, expects a value 0-1",
+                default = 0
+            }
+        },
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Frequency (in Hz)",
+                default = 440
+            },
+            {
+                name = "amp",
+                type = "SPFLOAT",
+                description ="Amplitude (typically a value between 0 and 1).",
+                default = 0.2
+            },
+            {
+                name = "wtpos",
+                type = "SPFLOAT",
+                description ="Wavetable position. (scaled 0-1)",
+                default = 0.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Wavetable morphing oscillator
+
+This is an oscillator with linear interpolation that is capable of morphing 
+between an arbitrary number of wavetables. 
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "This does nothing."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/paulstretch.lua
@@ -1,0 +1,66 @@
+sptbl["paulstretch"] = {
+
+    files = {
+        module = "paulstretch.c",
+        header = "paulstretch.h",
+        example = "ex_paulstretch.c",
+    },
+
+    func = {
+        create = "sp_paulstretch_create",
+        destroy = "sp_paulstretch_destroy",
+        init = "sp_paulstretch_init",
+        compute = "sp_paulstretch_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "ft",
+                type = "sp_ftbl *",
+                description = "ftable containing audio data",
+                default = "N/A"
+            },
+            {
+                name = "windowsize",
+                type = "SPFLOAT",
+                description = "Window size, in seconds.",
+                default = 10
+            },
+            {
+                name = "stretch",
+                type = "SPFLOAT",
+                description = "Stretch factor, 1.0 is no stretch.",
+                default = 10
+            },
+        },
+
+        optional = {
+        }
+    },
+
+    modtype = "module",
+
+    description = [[An extreme time-stretching algorithm by Paul Nasca Octavian
+This is an implementation of the popular paulstretch algorithm used for time
+stretching an audio signal to create ambient textures. Ideally, this algorithm 
+is best used for stretching signals by very very long amounts. 
+
+This version of paulstretch will take an ftable and loop through it, make
+it an ideal means for creating sustained pads. 
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/peaklim.lua
@@ -1,0 +1,63 @@
+sptbl["peaklim"] = {
+
+    files = {
+        module = "peaklim.c",
+        header = "peaklim.h",
+        example = "ex_peaklim.c",
+    },
+
+    func = {
+        create = "sp_peaklim_create",
+        destroy = "sp_peaklim_destroy",
+        init = "sp_peaklim_init",
+        compute = "sp_peaklim_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT",
+                description = "Attack time, in seconds",
+                default = 0.01
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT",
+                description ="Release time, in seconds",
+                default = 0.1
+            },
+            {
+                name = "thresh",
+                type = "SPFLOAT",
+                description ="Threshold, in dB",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Peak limiter 
+This is a simple peak limiting algorithm, based off code from the Stanford
+Music-424 class.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Output signal."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/phaser.lua
@@ -1,0 +1,114 @@
+sptbl["phaser"] = {
+
+    files = {
+        module = "phaser.c",
+        header = "phaser.h",
+        example = "ex_phaser.c",
+    },
+
+    func = {
+        create = "sp_phaser_create",
+        destroy = "sp_phaser_destroy",
+        init = "sp_phaser_init",
+        compute = "sp_phaser_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "MaxNotch1Freq",
+                type = "SPFLOAT*",
+                description = "Between 20 and 10000",
+                default = 800 
+            },
+            {
+                name = "MinNotch1Freq",
+                type = "SPFLOAT*",
+                description ="Between 20 and 5000",
+                default = 100
+            },
+            {
+                name = "Notch_width",
+                type = "SPFLOAT*",
+                description ="Between 10 and 5000",
+                default = 1000
+            },
+            {
+                name = "NotchFreq",
+                type = "SPFLOAT*",
+                description ="Between 1.1 and 4",
+                default = 1.5 
+            },
+            {
+                name = "VibratoMode",
+                type = "SPFLOAT*",
+                description ="1 or 0",
+                default = 1
+            },
+            {
+                name = "depth",
+                type = "SPFLOAT*",
+                description ="Between 0 and 1",
+                default = 1
+            },
+            {
+                name = "feedback_gain",
+                type = "SPFLOAT*",
+                description ="Between 0 and 1",
+                default = 0
+            },
+            {
+                name = "invert",
+                type = "SPFLOAT*",
+                description ="1 or 0",
+                default = 0
+            },
+            {
+                name = "level",
+                type = "SPFLOAT*",
+                description ="-60 to 10 dB",
+                default = 0
+            },
+            {
+                name = "lfobpm",
+                type = "SPFLOAT*",
+                description ="Between 24 and 360",
+                default = 30
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[A stereo phaser
+
+	This is a stereo phaser, generated from Faust code taken from the 
+Guitarix project.
+]],
+
+    ninputs = 2,
+    noutputs = 2,
+
+    inputs = {
+        {
+            name = "input1",
+            description = "Left audio input signal."
+        },
+        {
+            name = "input2",
+            description = "Right audio input signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out_left",
+            description = "Stereo left output for phaser."
+        },
+        {
+            name = "out_right",
+            description = "Stereo right output for phaser."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/phasor.lua
@@ -1,0 +1,59 @@
+sptbl["phasor"] = {
+
+    files = {
+        module = "phasor.c",
+        header = "phasor.h",
+        example = "ex_phasor.c",
+    },
+
+    func = {
+        create = "sp_phasor_create",
+        destroy = "sp_phasor_destroy",
+        init = "sp_phasor_init",
+        compute = "sp_phasor_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "iphs",
+                type = "SPFLOAT",
+                description = "initial phase",
+                default = 0
+            },
+        },
+
+        optional = {
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Frequency.",
+                default = 1.5
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Normalized sawtooth wave from 0 to 1
+
+    Phasors are often used when building table-lookup oscillators.]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "this doesn't do anything."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/pinknoise.lua
@@ -1,0 +1,48 @@
+sptbl["pinknoise"] = {
+
+    files = {
+        module = "pinknoise.c",
+        header = "pinknoise.h",
+        example = "ex_pinknoise.c",
+    },
+
+    func = {
+        create = "sp_pinknoise_create",
+        destroy = "sp_pinknoise_destroy",
+        init = "sp_pinknoise_init",
+        compute = "sp_pinknoise_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "amp",
+                type = "SPFLOAT",
+                description = "Amplitude. (Value between 0-1).",
+                default = 1.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Pink pinknoise generator]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This doesn't do anything"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/prop.lua
@@ -1,0 +1,80 @@
+sptbl["prop"] = {
+
+    files = {
+        module = "prop.c",
+        header = "prop.h",
+        example = "ex_prop.c",
+    },
+
+    func = {
+        create = "sp_prop_create",
+        destroy = "sp_prop_destroy",
+        init = "sp_prop_init",
+        compute = "sp_prop_compute",
+        other = {
+            sp_prop_reset = {
+                description = "Resets prop back to starting position.",
+                args = {
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "str",
+                type = "const char *",
+                description = "Prop string to be parsed.",
+                default = "N/A"
+            },
+        },
+
+        optional = {
+            {
+                name = "bpm",
+                type = "SPFLOAT",
+                description = "Beats per minute of the prop string.",
+                default = 60
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Simple rhythmic notation gate generator
+
+    Creates a gate using a simple rhythmic notation system called prop. When it reaches the end of the prop string, it will loop back to the beginning.
+
+Prop has a few basic rules:
+
+1. A '+' denotes a on. A '-' denotes an off (rest). They each have an equal duration of a quarter note.
+
+2. On and off values can be strung together to create equally spaced gates: +-+--
+
+3. When notes are enclosed in parantheses '()' following a positive integer N, their duration is reduced N times: ++2(+-)
+
+4. When notes are enclosed in brackets '[]' following a positive integer N, their duration is scaled by a factor of N: ++2[++]
+
+5. Parenthesis and brackets can be nested: +- 2[3(+2(++)+)]2(++)
+
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummy",
+            description = "This is doesn't do anything."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Gate output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/pshift.lua
@@ -1,0 +1,60 @@
+sptbl["pshift"] = {
+
+    files = {
+        module = "pshift.c",
+        header = "pshift.h",
+        example = "ex_pshift.c",
+    },
+
+    func = {
+        create = "sp_pshift_create",
+        destroy = "sp_pshift_destroy",
+        init = "sp_pshift_init",
+        compute = "sp_pshift_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "shift",
+                type = "SPFLOAT*",
+                description = "Pitch shift (in semitones), range -24/24.",
+                default = 0
+            },
+            {
+                name = "window",
+                type = "SPFLOAT*",
+                description ="Window size (in samples), max 10000",
+                default = 1000
+            },
+            {
+                name = "xfade",
+                type = "SPFLOAT*",
+                description ="Crossfade (in samples), max 10000",
+                default = 10
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Time-domain pitch shifter.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/randh.lua
@@ -1,0 +1,57 @@
+sptbl["randh"] = {
+
+    files = {
+        module = "randh.c",
+        header = "randh.h",
+        example = "ex_randh.c",
+    },
+
+    func = {
+        create = "sp_randh_create",
+        destroy = "sp_randh_destroy",
+        init = "sp_randh_init",
+        compute = "sp_randh_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description ="Minimum value to use.",
+                default = 0
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description ="Maximum value to use.",
+                default = 1
+            },
+            {
+                name = "freq",
+                type = "SPFLOAT",
+                description = "Frequency of randomization (in Hz)",
+                default = 10
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Random number generator with hold time.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/random.lua
@@ -1,0 +1,54 @@
+sptbl["random"] = {
+
+    files = {
+        module = "random.c",
+        header = "random.h",
+        example = "ex_random.c",
+    },
+
+    func = {
+        create = "sp_random_create",
+        destroy = "sp_random_destroy",
+        init = "sp_random_init",
+        compute = "sp_random_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description = "Minimum value.",
+                default = -0.2
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description ="Maximum value.",
+                default = 0.2
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Random values within a range]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "dummmy",
+            description = "This does nothing."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/reverse.lua
@@ -1,0 +1,50 @@
+sptbl["reverse"] = {
+
+	files = {
+	    module = "reverse.c",
+	    header = "reverse.h",
+	    example = "ex_reverse.c",
+	},
+
+	func = {
+	    create = "sp_reverse_create",
+	    destroy = "sp_reverse_destroy",
+	    init = "sp_reverse_init",
+	    compute = "sp_reverse_compute",
+	},
+
+	params = {
+	    mandatory = {
+	        {
+	            name = "delay",
+	            type = "SPFLOAT",
+	            description = "Delay time in seconds.",
+	            default = "1.0"
+	        }
+	    }
+	},
+
+	modtype = "module",
+
+	description = [[Signal reverser
+
+	Reverse will store a signal inside a buffer and play it back reversed.]],
+
+	ninputs = 1,
+	noutputs = 1,
+
+	inputs = {
+	    {
+	        name = "input",
+	        description = "Signal input."
+	    }
+	},
+
+	outputs = {
+	    {
+	        name = "out",
+	        description = "Signal output."
+	    }
+	}
+
+}
--- /dev/null
+++ b/modules/data/rpt.lua
@@ -1,0 +1,54 @@
+sptbl["rpt"] = {
+
+    files = {
+        module = "rpt.c",
+        header = "rpt.h",
+        example = "ex_rpt.c",
+    },
+
+    func = {
+        create = "sp_rpt_create",
+        destroy = "sp_rpt_destroy",
+        init = "sp_rpt_init",
+        compute = "sp_rpt_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "maxdur",
+                type = "SPFLOAT",
+                description = "Maximum delay duration in seconds. This will set the buffer size.",
+                default = "0.7"
+            }
+        },
+    },
+
+    modtype = "module",
+
+    description = [[Trigger based beat-repeat stuttering effect
+
+    When the input is a non-zero value, rpt will load up the buffer and loop a certain number of times. Speed and repeat amounts can be set with the sp_rpt_set function.]],
+
+    ninputs = 2,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When this value is non-zero, it will start the repeater."
+        },
+        {
+            name = "input",
+            description = "The signal to be repeated."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal out. This is passive unless explicity triggered in the input."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/samphold.lua
@@ -1,0 +1,44 @@
+sptbl["samphold"] = {
+
+    files = {
+        module = "samphold.c",
+        header = "samphold.h",
+        example = "ex_samphold.c",
+    },
+
+    func = {
+        create = "sp_samphold_create",
+        destroy = "sp_samphold_destroy",
+        init = "sp_samphold_init",
+        compute = "sp_samphold_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[Classic sample and hold]],
+
+    ninputs = 2,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Will hold the current input value when non-zero."
+        },
+        {
+            name = "input",
+            description = "Audio input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        }
+    }
+
+}
--- /dev/null
+++ b/modules/data/saturator.lua
@@ -1,0 +1,54 @@
+sptbl["saturator"] = {
+
+    files = {
+        module = "saturator.c",
+        header = "saturator.h",
+        example = "ex_saturator.c",
+    },
+
+    func = {
+        create = "sp_saturator_create",
+        destroy = "sp_saturator_destroy",
+        init = "sp_saturator_init",
+        compute = "sp_saturator_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "drive",
+                type = "SPFLOAT",
+                description ="Input gain into the distortion section, in decibels. Controls overall amount of distortion.",
+                default = 1.0
+            },
+            {
+                name = "dcoffset",
+                type = "SPFLOAT",
+                description = "Constant linear offset applied to the signal. A small offset will introduce odd harmonics into the distoration spectrum, whereas a zero offset will have only even harmonics.",
+                default = 0.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Soft clip saturating distortion, based on examples from Abel/Berners' Music 424 course at Stanford.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/scale.lua
@@ -1,0 +1,57 @@
+sptbl["scale"] = {
+
+    files = {
+        module = "scale.c",
+        header = "scale.h",
+        example = "ex_scale.c",
+    },
+
+    func = {
+        create = "sp_scale_create",
+        destroy = "sp_scale_destroy",
+        init = "sp_scale_init",
+        compute = "sp_scale_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description = "Minimum value to scale to.",
+                default = 0
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description ="Maximum value to scale to.",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Bipolar Scale
+
+    This module scales from unipolar [0, 1] to another range defined by min and max.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "Signal to be scaled."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Scaled signal out"
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/sdelay.lua
@@ -1,0 +1,50 @@
+sptbl["sdelay"] = {
+
+    files = {
+        module = "sdelay.c",
+        header = "sdelay.h",
+        example = "ex_sdelay.c",
+    },
+
+    func = {
+        create = "sp_sdelay_create",
+        destroy = "sp_sdelay_destroy",
+        init = "sp_sdelay_init",
+        compute = "sp_sdelay_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "size",
+                type = "SPFLOAT",
+                description = "Size of delay (in samples)",
+                default = "128"
+            },
+        }
+
+    },
+
+    modtype = "module",
+
+    description = [[Delays a signal by a number of samples.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input signal to be delayed."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output for sdelay."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/slice.lua
@@ -1,0 +1,69 @@
+sptbl["slice"] = {
+
+    files = {
+        module = "slice.c",
+        header = "slice.h",
+        example = "ex_slice.c",
+    },
+
+    func = {
+        create = "sp_slice_create",
+        destroy = "sp_slice_destroy",
+        init = "sp_slice_init",
+        compute = "sp_slice_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "vals",
+                type = "sp_ftbl *",
+                description = "A table containing slice points, in samples",
+                default = "N/A"
+            },
+            {
+                name = "buf",
+                type = "sp_ftbl *",
+                description = "The buffer containing the audio samples.",
+                default = "N/A"
+            }
+        },
+
+        optional = {
+            {
+                name = "id",
+                type = "SPFLOAT",
+                description = "Value position.",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Yet another slice-based sample player.
+This module takes in an audio buffer and a table with slice points. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When non-zero, (re)triggers samples"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+        {
+            name = "out_right",
+            description = "Stereo right output for slice."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/smoothdelay.lua
@@ -1,0 +1,74 @@
+sptbl["smoothdelay"] = {
+
+    files = {
+        module = "smoothdelay.c",
+        header = "smoothdelay.h",
+        example = "ex_smoothdelay.c",
+    },
+
+    func = {
+        create = "sp_smoothdelay_create",
+        destroy = "sp_smoothdelay_destroy",
+        init = "sp_smoothdelay_init",
+        compute = "sp_smoothdelay_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "maxdel",
+                type = "SPFLOAT",
+                description = "Maximum delay time (in seconds)",
+                default = 1.0
+            },
+            {
+                name = "interp",
+                type = "uint32_t",
+                description = "interpolation time (in samples)",
+                default = 1024
+            }
+        },
+
+        optional = {
+            {
+                name = "feedback",
+                type = "SPFLOAT",
+                description ="",
+                default = 0.1
+            },
+            {
+                name = "del",
+                type = "SPFLOAT",
+                description = "",
+                default = 0.5
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Smooth variable delay line without varispeed pitch.
+Smooth delay is based off the sdelay module in Faust. The smooth delay 
+algorithm involves a double delay line. Any time the delay time changes, 
+the delay time of buffer not heard changes, then is crossfaded to 
+that buffer.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/smoother.lua
@@ -1,0 +1,65 @@
+sptbl["smoother"] = {
+
+    files = {
+        module = "smoother.c",
+        header = "smoother.h",
+        example = "ex_smoother.c",
+    },
+
+    func = {
+        create = "sp_smoother_create",
+        destroy = "sp_smoother_destroy",
+        init = "sp_smoother_init",
+        compute = "sp_smoother_compute",
+        other = {
+            sp_smoother_reset = {
+                description = "Resets internal buffers, snapping to input value instead of ramping to it.",
+                args = {
+                    {
+                        name = "input",
+                        type = "SPFLOAT *",
+                        description = "input value to snap to.",
+                        default = 0.0
+                    },
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+        },
+        optional = {
+            {
+                name = "smooth",
+                type = "SPFLOAT",
+                description = "Smooth time amount.",
+                default = 0.01
+            },
+        },
+    },
+
+    modtype = "module",
+
+    description = [[ Smootheramento-style control signal smoothing
+
+    Useful for smoothing out low-resolution signals and applying glissando to filters.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "Signal input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/spa.lua
@@ -1,0 +1,49 @@
+sptbl["spa"] = {
+
+    files = {
+        module = "spa.c",
+        header = "spa.h",
+        example = "ex_spa.c",
+    },
+
+    func = {
+        create = "sp_spa_create",
+        destroy = "sp_spa_destroy",
+        init = "sp_spa_init",
+        compute = "sp_spa_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "filename",
+                type = "const char *",
+                description = "Filename of SPA file",
+                default = "N/A"
+            },
+        },
+
+    },
+
+    modtype = "module",
+
+    description = [[Stream a Soundpipe Audio File
+Similar to sp_diskin, sp_spa will stream a file in the internal soundpipe
+audio format. Such a format is useful for instances where you need to read
+audio files, but can't use libsndfile. 
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Output to spa."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/sparec.lua
@@ -1,0 +1,56 @@
+sptbl["sparec"] = {
+
+    files = {
+        module = "sparec.c",
+        header = "sparec.h",
+        example = "ex_sparec.c",
+    },
+
+    func = {
+        create = "sp_sparec_create",
+        destroy = "sp_sparec_destroy",
+        init = "sp_sparec_init",
+        compute = "sp_sparec_compute",
+        other = {
+            sp_sparec_close = {
+                description = "Close spa file and writes the rest of the data in the buffer.",
+                args = {
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "filename",
+                type = "const char *",
+                description = "Filename to write to",
+                default = "N/A"
+            },
+        },
+
+    },
+
+    modtype = "module",
+
+    description = [[Writes signal to spa file.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Copy of input signal."
+        }
+    }
+
+}
--- /dev/null
+++ b/modules/data/switch.lua
@@ -1,0 +1,50 @@
+sptbl["switch"] = {
+
+    files = {
+        module = "switch.c",
+        header = "switch.h",
+        example = "ex_switch.c",
+    },
+
+    func = {
+        create = "sp_switch_create",
+        destroy = "sp_switch_destroy",
+        init = "sp_switch_init",
+        compute = "sp_switch_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[Switch between two signals
+
+    By default, the incoming first signal is selected. When triggered, the output signal will switch to the other signal.]],
+
+    ninputs = 3,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When non-zero, will switch to the other signal."
+        },
+        {
+            name = "input_1",
+            description = "Signal input 1. This is the default signal."
+        },
+        {
+            name = "input_2",
+            description = "Signal input 2."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tadsr.lua
@@ -1,0 +1,66 @@
+sptbl["tadsr"] = {
+
+    files = {
+        module = "tadsr.c",
+        header = "tadsr.h",
+        example = "ex_tadsr.c",
+    },
+
+    func = {
+        create = "sp_tadsr_create",
+        destroy = "sp_tadsr_destroy",
+        init = "sp_tadsr_init",
+        compute = "sp_tadsr_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT",
+                description = "Attack time",
+                default = 0.1
+            },
+            {
+                name = "dec",
+                type = "SPFLOAT",
+                description ="Decay time",
+                default = 0.1
+            },
+            {
+                name = "sus",
+                type = "SPFLOAT",
+                description ="Sustain Level",
+                default = 0.5
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT",
+                description ="release",
+                default = 0.5
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Triggerable classic ADSR envelope]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Expects trigger signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Envelope signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/talkbox.lua
@@ -1,0 +1,59 @@
+sptbl["talkbox"] = {
+
+    files = {
+        module = "talkbox.c",
+        header = "talkbox.h",
+        example = "ex_talkbox.c",
+    },
+
+    func = {
+        create = "sp_talkbox_create",
+        destroy = "sp_talkbox_destroy",
+        init = "sp_talkbox_init",
+        compute = "sp_talkbox_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "quality",
+                type = "SPFLOAT",
+                description = "Quality of the talkbox sound. 0=lowest fidelity. 1=highest fidelity",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[A high talkbox emulation, simular to a vocoder.
+This is the talkbox plugin ported from the MDA plugin suite. In many ways,
+this Talkbox functions like a vocoder: it takes in a source signal (usually
+speech), which then excites an excitation signal 
+(usually a harmonically rich signal like a saw wave). This particular algorithm
+uses linear-predictive coding (LPC), making speech intelligibility better 
+than most vocoder implementations.
+]],
+
+    ninputs = 2,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "source",
+            description = "Input signal that shapes the excitation. Also known as the modulator."
+        },
+        {
+            name = "excitation",
+            description = "The signal to be excited. Also known as the carrier."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Talkbox signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tblrec.lua
@@ -1,0 +1,56 @@
+sptbl["tblrec"] = {
+
+    files = {
+        module = "tblrec.c",
+        header = "tblrec.h",
+        example = "ex_tblrec.c",
+    },
+
+    func = {
+        create = "sp_tblrec_create",
+        destroy = "sp_tblrec_destroy",
+        init = "sp_tblrec_init",
+        compute = "sp_tblrec_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "bar",
+                type = "sp_ftbl *",
+                description = "",
+                default = "N/A"
+            },
+        },
+    },
+
+    modtype = "module",
+
+    description = [[Record a signal to an ftable.
+This module will write audio-rate signals to a preallocated soundpipe ftable. 
+Every time record is enabled, it will got back to index 0 overwrite any
+previous information that was on it. 
+]],
+
+    ninputs = 2,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "in",
+            description = "Input signal."
+        },
+        {
+            name = "trig",
+            description = "When non-zero, will toggle recording on/off. Recording will set the playhead back to zero"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Copy of the input signal."
+        }
+    }
+
+}
--- /dev/null
+++ b/modules/data/tdiv.lua
@@ -1,0 +1,64 @@
+sptbl["tdiv"] = {
+
+    files = {
+        module = "tdiv.c",
+        header = "tdiv.h",
+        example = "ex_tdiv.c",
+    },
+
+    func = {
+        create = "sp_tdiv_create",
+        compute = "sp_tdiv_compute",
+        destroy = "sp_tdiv_destroy",
+        init = "sp_tdiv_init",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "num",
+                type = "SPFLOAT",
+                description = "Triggers every N times.",
+                default = 2
+            },
+            {
+                name = "offset",
+                type = "SPFLOAT",
+                description = "Offset amoutn.",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Trigger divider.
+This module will take in a trigger signal, and output a trigger signal
+every N times.
+
+For instance, when N = 3:
+
+in: * * * * * * * * *
+
+out *     *     *   
+
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trigger",
+            description = "Trigger. Should be a non-zero value"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tenv.lua
@@ -1,0 +1,60 @@
+sptbl["tenv"] = {
+
+    files = {
+        module = "tenv.c",
+        header = "tenv.h",
+        example = "ex_tenv.c",
+    },
+
+    func = {
+        create = "sp_tenv_create",
+        destroy = "sp_tenv_destroy",
+        init = "sp_tenv_init",
+        compute = "sp_tenv_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT",
+                description = "Attack time, in seconds.",
+                default = 0.1
+            },
+            {
+                name = "hold",
+                type = "SPFLOAT",
+                description = "Hold time, in seconds.",
+                default = 0.3
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT",
+                description = "Release time, in seconds.",
+                default = 0.2
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Trigger based linear AHD envelope generator]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Trigger input. When non-zero, the envelope will (re)trigger."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output"
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tenv2.lua
@@ -1,0 +1,58 @@
+sptbl["tenv2"] = {
+
+    files = {
+        module = "tenv2.c",
+        header = "tenv2.h",
+        example = "ex_tenv2.c",
+    },
+
+    func = {
+        create = "sp_tenv2_create",
+        destroy = "sp_tenv2_destroy",
+        init = "sp_tenv2_init",
+        compute = "sp_tenv2_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT",
+                description = "Attack time (in seconds).",
+                default = 0.1
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT",
+                description ="Release time (in seconds).",
+                default = 0.1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Linear 2-stage Attack/Release envelope generator
+
+    This envelope takes 2 triggers. When triggered once,
+the envelope will rise to 1 according to the attack time. When triggered again, it will decay to 0 according to
+the decay time.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Expects a trigger signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal out (a unipolar envelope)."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tenvx.lua
@@ -1,0 +1,65 @@
+sptbl["tenvx"] = {
+
+    files = {
+        module = "tenvx.c",
+        header = "tenvx.h",
+        example = "ex_tenvx.c",
+    },
+
+    func = {
+        create = "sp_tenvx_create",
+        destroy = "sp_tenvx_destroy",
+        init = "sp_tenvx_init",
+        compute = "sp_tenvx_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT",
+                description = "Tau attack time, in seconds. Must be non-zero.",
+                default = 0.1
+            },
+            {
+                name = "hold",
+                type = "SPFLOAT",
+                description = "Hold time, in seconds. The duration of the gate signal.",
+                default = 0.3
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT",
+                description = "Tau release time, in seconds. Must be non-zero.",
+                default = 0.2
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Trigger based exponential AHD envelope generator.
+    This envelope generator emulates the exponential behavior of analogue 
+envelope generators by passing a gate signal (whose duration is specified via
+the hold parameter) through a one-pole filter, whose filter coefficeints are
+calculated in terms of tau.  
+    ]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Trigger input. When non-zero, the envelope will (re)trigger."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output"
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tgate.lua
@@ -1,0 +1,49 @@
+sptbl["tgate"] = {
+
+    files = {
+        module = "tgate.c",
+        header = "tgate.h",
+        example = "ex_tgate.c",
+    },
+
+    func = {
+        create = "sp_tgate_create",
+        destroy = "sp_tgate_destroy",
+        init = "sp_tgate_init",
+        compute = "sp_tgate_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "time",
+                type = "SPFLOAT",
+                description = "Duration of the gate (in seconds)",
+                default = 0.5
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[A triggerable gate.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trigger",
+            description = "When non-zero, will (re)trigger gate."
+        },
+    },
+
+    outputs = {
+        {
+            name = "gate",
+            description = "Gate output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/thresh.lua
@@ -1,0 +1,55 @@
+sptbl["thresh"] = {
+
+    files = {
+        module = "thresh.c",
+        header = "thresh.h",
+        example = "ex_thresh.c",
+    },
+
+    func = {
+        create = "sp_thresh_create",
+        destroy = "sp_thresh_destroy",
+        init = "sp_thresh_init",
+        compute = "sp_thresh_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "thresh",
+                type = "SPFLOAT",
+                description = "Threshold level",
+                default = 1.5
+            },
+            {
+                name = "mode",
+                type = "int",
+                description ="Sets when to detect theshold crossings. 0 = from below. 1 = from above. 2 = from above/below",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Trigger generator for signals that cross a given threshold. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Input signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "trig",
+            description = "Produces a trigger when the input signal crosses the set threshold."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/timer.lua
@@ -1,0 +1,44 @@
+sptbl["timer"] = {
+
+    files = {
+        module = "timer.c",
+        header = "timer.h",
+        example = "ex_timer.c",
+    },
+
+    func = {
+        create = "sp_timer_create",
+        destroy = "sp_timer_destroy",
+        init = "sp_timer_init",
+        compute = "sp_timer_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[Tap-tempo like timer
+
+When triggered, timer will begin an internal stopwatch until it is triggered again.
+The output of the timer will be the time elapsed in seconds.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "clock",
+            description = "When non-zero, will start/stop the timer."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Outputs the time of the last timer (in seconds)."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tin.lua
@@ -1,0 +1,40 @@
+sptbl["tin"] = {
+
+    files = { 
+        module = "tin.c",
+        header = "tin.h",
+        example = "ex_tin.c",
+    },
+    
+    func = {
+        create = "sp_tin_create",
+        destroy = "sp_tin_destroy",
+        init = "sp_tin_init",
+        compute = "sp_tin_compute",
+    },
+    
+    params = {
+    },
+    
+    modtype = "module",
+    
+    description = [[Similar to in, tin reads SPFLOATs (by default, this is a 4 byte binary float) from standard input every time it is triggered. behaves like a sample and hold, retaining the previous value (initial set to 0) until triggered. ]], 
+    
+    ninputs = 1,
+    noutputs = 1,
+    
+    inputs = { 
+        {
+            name = "trig",
+            description = "When non-zero, reads a single SPFLOAT from standard input."
+        },
+    },
+    
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/trand.lua
@@ -1,0 +1,55 @@
+sptbl["trand"] = {
+
+    files = {
+        module = "trand.c",
+        header = "trand.h",
+        example = "ex_trand.c",
+    },
+
+    func = {
+        create = "sp_trand_create",
+        destroy = "sp_trand_destroy",
+        init = "sp_trand_init",
+        compute = "sp_trand_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "min",
+                type = "SPFLOAT",
+                description ="Minimum value to use.",
+                default = 0
+            },
+            {
+                name = "max",
+                type = "SPFLOAT",
+                description ="Maximum value to use.",
+                default = 1
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Triggered random number generator.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "When non-zero, will create a new random value."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tseg.lua
@@ -1,0 +1,70 @@
+sptbl["tseg"] = {
+
+    files = {
+        module = "tseg.c",
+        header = "tseg.h",
+        example = "ex_tseg.c",
+    },
+
+    func = {
+        create = "sp_tseg_create",
+        destroy = "sp_tseg_destroy",
+        init = "sp_tseg_init",
+        compute = "sp_tseg_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "ibeg",
+                type = "SPFLOAT",
+                description = "Beginning value.",
+                default = 0.001; 
+            },
+        },
+
+        optional = {
+            {
+                name = "end",
+                type = "SPFLOAT",
+                description = "End parameter to go to.",
+                default = 1.0
+            },
+            {
+                name = "dur",
+                type = "SPFLOAT",
+                description = "duration to rise to (in seconds).",
+                default = 1.0
+            },
+            {
+                name = "type",
+                type = "SPFLOAT",
+                description ="The type of line, which determines slope of line",
+                default = -1.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[This module creates a series of line segments. 
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "trigger."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "tseg output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/tseq.lua
@@ -1,0 +1,59 @@
+sptbl["tseq"] = {
+
+    files = {
+        module = "tseq.c",
+        header = "tseq.h",
+        example = "ex_tseq.c",
+    },
+
+    func = {
+        create = "sp_tseq_create",
+        destroy = "sp_tseq_destroy",
+        init = "sp_tseq_init",
+        compute = "sp_tseq_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "ft",
+                type = "sp_ftbl *",
+                description = "An ftable of values",
+                default = "N/A"
+            },
+        },
+
+        optional = {
+            {
+                name = "shuf",
+                type = "int",
+                description = "When shuf is non-zero, randomly pick a value rather than cycle through sequentially.",
+                default = 0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[Function table looper
+
+    TSeq runs through values in an ftable. It will change values when the trigger input is a non-zero value, and wrap around when it reaches the end.]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "trig",
+            description = "Trigger."
+        },
+    },
+
+    outputs = {
+        {
+            name = "val",
+            description = "Value from current position in the ftable."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/voc.lua
@@ -1,0 +1,53 @@
+sptbl["voc"] = {
+
+    files = {
+        module = "voc.c",
+        header = "voc.h",
+        example = "ex_voc.c",
+    },
+
+    func = {
+        create = "sp_voc_create",
+        destroy = "sp_voc_destroy",
+        init = "sp_voc_init",
+        compute = "sp_voc_compute",
+    },
+
+    params = {
+    },
+
+    modtype = "module",
+
+    description = [[A vocal tract physical model.
+
+Based on the Pink Trombone algorithm by Neil Thapen, Voc implements a physical 
+model of the vocal tract glottal pulse wave. The tract model is based on the 
+classic Kelly-Lochbaum
+segmented cylindrical 1d waveguide model, and the glottal pulse wave is a
+LF glottal pulse model. 
+
+The soundpipe source code for Voc is generated via ctangle, one half of the
+literate documentation system known CWEB. The CWEB are maintained in a 
+separate repository. They are hosted on github here: 
+http://www.github.com/paulbatchelor/voc
+
+This documentation is a stub. For a full overview on proper usage, consult
+the "Top-level functions" section of the documented code, a copy of which
+can be found at the Voc project page pbat.ch/proj/voc, or generate the PDF
+from the github page described above.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Voc output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/vocoder.lua
@@ -1,0 +1,68 @@
+sptbl["vocoder"] = {
+
+    files = {
+        module = "vocoder.c",
+        header = "vocoder.h",
+        example = "ex_vocoder.c",
+    },
+
+    func = {
+        create = "sp_vocoder_create",
+        destroy = "sp_vocoder_destroy",
+        init = "sp_vocoder_init",
+        compute = "sp_vocoder_compute",
+    },
+
+    params = {
+        mandatory = {
+        },
+
+        optional = {
+            {
+                name = "atk",
+                type = "SPFLOAT*",
+                description = "Attack time . (Range 0.001 and 0.5 seconds)",
+                default = 0.1
+            },
+            {
+                name = "rel",
+                type = "SPFLOAT*",
+                description = "Release time",
+                default = 0.1
+            },
+            {
+                name = "bwratio",
+                type = "SPFLOAT*",
+                description = "Coeffecient to adjust the bandwidth of each band (0.1 - 2)",
+                default = 0.5
+            },
+            
+        }
+    },
+
+    modtype = "module",
+
+    description = [[16-band channel vocoder]],
+
+    ninputs = 2,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "source",
+            description = "Source signal (also known as carrier)."
+        },
+        {
+            name = "excite",
+            description = "Excitation signal (also known as modulator)"
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/wavin.lua
@@ -1,0 +1,73 @@
+sptbl["wavin"] = {
+
+    files = {
+        module = "wavin.c",
+        header = "wavin.h",
+        example = "ex_wavin.c",
+    },
+
+    func = {
+        create = "sp_wavin_create",
+        destroy = "sp_wavin_destroy",
+        init = "sp_wavin_init",
+        compute = "sp_wavin_compute",
+        other = {
+            sp_wavin_seek = {
+                description = "Seeks to position in file.",
+                args = {
+                    {
+                        name = "sample",
+                        type = "unsigned long",
+                        description = "Sample position",
+                        default = 0
+                    }
+                }
+            },
+            sp_wavin_get_sample = {
+                description = "Get a particular sample from the file.",
+                args = {
+                    {
+                        name = "pos",
+                        type = "SPFLOAT",
+                        description = "Sample position",
+                        default = 0
+                    }
+                }
+            }
+        }
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "filename",
+                type = "const char *",
+                description = "Filename",
+                default = "N/A"
+            },
+        },
+
+    },
+
+    modtype = "module",
+
+    description = [[Reads a mono WAV file.
+
+This module reads a mono WAV file from disk. It uses the public-domain 
+dr_wav library for decoding, so it can be a good substitute for libsndfile.
+]],
+
+    ninputs = 0,
+    noutputs = 1,
+
+    inputs = {
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "output signal."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/wavout.lua
@@ -1,0 +1,52 @@
+sptbl["wavout"] = {
+
+    files = {
+        module = "wavout.c",
+        header = "wavout.h",
+        example = "ex_wavout.c",
+    },
+
+    func = {
+        create = "sp_wavout_create",
+        destroy = "sp_wavout_destroy",
+        init = "sp_wavout_init",
+        compute = "sp_wavout_compute",
+    },
+
+    params = {
+        mandatory = {
+            {
+                name = "filename",
+                type = "const char*",
+                description = "The filename of the output file.",
+                default = "N/A"
+            }
+        },
+    },
+
+    modtype = "module",
+
+    description = [[Writes a mono signal to a WAV file.
+This module uses the public-domain dr_wav library to write WAV files
+to disk. This module is ideal for instances where GPL-licensed libsndfile 
+cannot be used for legal reasons.
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Mono input signal."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "A passthrough signal: a copy of the input signal."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/wpkorg35.lua
@@ -1,0 +1,65 @@
+sptbl["wpkorg35"] = {
+
+    files = {
+        module = "wpkorg35.c",
+        header = "wpkorg35.h",
+        example = "ex_wpkorg35.c",
+    },
+
+    func = {
+        create = "sp_wpkorg35_create",
+        destroy = "sp_wpkorg35_destroy",
+        init = "sp_wpkorg35_init",
+        compute = "sp_wpkorg35_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "cutoff",
+                type = "SPFLOAT",
+                description = "Filter cutoff",
+                default = 1000
+            },
+            {
+                name = "res",
+                type = "SPFLOAT",
+                description ="Filter resonance (should be between 0-2)",
+                default = 1.0
+            },
+            {
+                name = "saturation",
+                type = "SPFLOAT",
+                description ="Filter saturation.",
+                default = 0.0
+            },
+        }
+    },
+
+    modtype = "module",
+
+    description = [[ Analogue model of the Korg 35 Lowpass Filter
+
+Original port done by Will Pirkle:
+http://www.willpirkle.com/Downloads/AN-5Korg35_V3.pdf
+
+]],
+
+    ninputs = 1,
+    noutputs = 1,
+
+    inputs = {
+        {
+            name = "input",
+            description = "Audio input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out",
+            description = "Signal output."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/data/zitarev.lua
@@ -1,0 +1,139 @@
+sptbl["zitarev"] = {
+
+    files = {
+        module = "zitarev.c",
+        header = "zitarev.h",
+        example = "ex_zitarev.c",
+    },
+
+    func = {
+        create = "sp_zitarev_create",
+        destroy = "sp_zitarev_destroy",
+        init = "sp_zitarev_init",
+        compute = "sp_zitarev_compute",
+    },
+
+    params = {
+        optional = {
+            {
+                name = "in_delay",
+                type = "SPFLOAT*",
+                description = "Delay in ms before reverberation begins.",
+                default = 60,
+                min = 10,
+                max = 100 
+                
+            },
+            {
+                name = "lf_x",
+                type = "SPFLOAT*",
+                description ="Crossover frequency separating low and middle frequencies (Hz).",
+                default = 200,
+                min = 200,
+                max = 1000
+            },
+            {
+                name = "rt60_low",
+                type = "SPFLOAT*",
+                description ="Time (in seconds) to decay 60db in low-frequency band.",
+                default = 3.0,
+                min = 1,
+                max = 8 
+            },
+            {
+                name = "rt60_mid",
+                type = "SPFLOAT*",
+                description ="Time (in seconds) to decay 60db in mid-frequency band.",
+                default = 2.0,
+                min = 1,
+                max = 8
+            },
+            {
+                name = "hf_damping",
+                type = "SPFLOAT*",
+                description ="Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60.",
+                default = 6000.0,
+                min = 1500,
+                max = 0.5 * 48000
+            },
+            {
+                name = "eq1_freq",
+                type = "SPFLOAT*",
+                description ="Center frequency of second-order Regalia Mitra peaking equalizer section 1.",
+                default = 315.0,
+                min = 40,
+                max = 2500
+            },
+            {
+                name = "eq1_level",
+                type = "SPFLOAT*",
+                description ="Peak level in dB of second-order Regalia-Mitra peaking equalizer section 1",
+                default = 0,
+                min = -15,
+                max = 15
+            },
+            {
+                name = "eq2_freq",
+                type = "SPFLOAT*",
+                description ="Center frequency of second-order Regalia Mitra peaking equalizer section 2.",
+                default = 1500.0,
+                min = 160,
+                max = 10000
+            },
+            {
+                name = "eq2_level",
+                type = "SPFLOAT*",
+                description ="Peak level in dB of second-order Regalia-Mitra peaking equalizer section 2",
+                default = 0 ,
+                min = -15,
+                max = 15
+            },
+            {
+                name = "mix",
+                type = "SPFLOAT*",
+                description ="0 = all dry, 1 = all wet",
+                default = 1,
+                min = 0,
+                max = 1
+            },
+            {
+                name = "level",
+                type = "SPFLOAT*",
+                description ="Output scale factor (in dB).",
+                default = -20,
+                min = -70,
+                max = 40
+            }
+        }
+    },
+
+    modtype = "module",
+
+    description = [[8 FDN stereo zitareverb algorithm, imported from Faust.]],
+
+    ninputs = 2,
+    noutputs = 2,
+
+    inputs = {
+        {
+            name = "input_1",
+            description = "First input."
+        },
+        {
+            name = "input_2",
+            description = "Second input."
+        },
+    },
+
+    outputs = {
+        {
+            name = "out_1",
+            description = "Channel 1 output. Most likely for the left channel."
+        },
+        {
+            name = "out_2",
+            description = "Channel 2 output. Mose likely for the right channel."
+        },
+    }
+
+}
--- /dev/null
+++ b/modules/dcblocker.c
@@ -1,0 +1,37 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#define SK_DCBLOCKER_PRIV
+#include "tangled/dcblocker.h"
+#include "soundpipe.h"
+
+int sp_dcblocker_create(sp_dcblocker **p)
+{
+    *p = malloc(sizeof(sp_dcblocker));
+    return SP_OK;
+}
+
+int sp_dcblocker_destroy(sp_dcblocker **p)
+{
+    sp_dcblocker *pp;
+    pp = *p;
+    free(pp->dcblocker);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_dcblocker_init(sp_data *sp, sp_dcblocker *p)
+{
+    p->dcblocker = malloc(sizeof(sk_dcblocker));
+    sk_dcblocker_init(p->dcblocker);
+    return SP_OK;
+}
+
+int sp_dcblocker_compute(sp_data *sp, sp_dcblocker *p,
+                         SPFLOAT *in, SPFLOAT *out)
+{
+    *out = sk_dcblocker_tick(p->dcblocker, *in);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/delay.c
@@ -1,0 +1,42 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+int sp_delay_create(sp_delay **p)
+{
+    *p = malloc(sizeof(sp_delay));
+    return SP_OK;
+}
+
+int sp_delay_destroy(sp_delay **p)
+{
+    sp_delay *pp = *p;
+    sp_auxdata_free(&pp->buf);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_delay_init(sp_data *sp, sp_delay *p, SPFLOAT time)
+{
+    p->time = time;
+    p->bufsize = round(time * sp->sr);
+    sp_auxdata_alloc(&p->buf, p->bufsize * sizeof(SPFLOAT));
+    p->bufpos = 0;
+    p->feedback = 0;
+    p->last = 0;
+    return SP_OK;
+}
+
+int sp_delay_compute(sp_data *sp, sp_delay *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT delay = 0, sig = 0;
+    SPFLOAT *buf = (SPFLOAT *)p->buf.ptr;
+    delay = buf[p->bufpos];
+    sig = (delay * p->feedback) + *in;
+    buf[p->bufpos] = sig;
+    p->bufpos = (p->bufpos + 1) % p->bufsize;
+    *out = delay;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/diode.c
@@ -1,0 +1,177 @@
+/*
+ * Diode
+ *
+ * Based on Will Pirkle's Diode Ladder filter design.
+ * Code adapted from the CCRMA Chugin WPDiodeLadder
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+int sp_diode_create(sp_diode **p)
+{
+    *p = malloc(sizeof(sp_diode));
+    return SP_OK;
+}
+
+int sp_diode_destroy(sp_diode **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+static SPFLOAT sp_diode_opva_fdbk_out(sp_data *sp, sp_diode *p, int filt)
+{
+    return p->opva_beta[filt] *
+        (p->opva_z1[filt] + p->opva_fdbk[filt] * p->opva_delta[filt]);
+}
+
+static SPFLOAT sp_diode_opva_compute(sp_data *sp, sp_diode *p, SPFLOAT in, int filt)
+{
+    /*
+	double x_in = (xn*m_dGamma + m_dFeedback + m_dEpsilon*getFeedbackOutput());
+
+	double vn = (m_da0*x_in - m_dZ1)*m_dAlpha;
+	double out = vn + m_dZ1;
+	m_dZ1 = vn + out;
+    */
+
+	/* m_dBeta*(m_dZ1 + m_dFeedback*m_dDelta); */
+
+	SPFLOAT x_in = (in*p->opva_gamma[filt]
+        + p->opva_fdbk[filt]
+        + p->opva_eps[filt] * sp_diode_opva_fdbk_out(sp, p, filt));
+	SPFLOAT vn = (p->opva_a0[filt]*x_in -
+        p->opva_z1[filt])*p->opva_alpha[filt];
+	SPFLOAT out = vn + p->opva_z1[filt];
+	p->opva_z1[filt] = vn + out;
+    return out;
+}
+
+static void sp_diode_update(sp_data *sp, sp_diode *p)
+{
+	/* calculate alphas */
+	SPFLOAT G1, G2, G3, G4;
+	SPFLOAT wd = 2*M_PI*p->freq;
+	SPFLOAT T  = 1/(SPFLOAT)sp->sr;
+	SPFLOAT wa = (2/T)*tan(wd*T/2);
+	SPFLOAT g = wa*T/2;
+    int i;
+
+    /* Big G's */
+	G4 = 0.5*g/(1.0 + g);
+	G3 = 0.5*g/(1.0 + g - 0.5*g*G4);
+	G2 = 0.5*g/(1.0 + g - 0.5*g*G3);
+	G1 = g/(1.0 + g - g*G2);
+
+    /* big G value gamma */
+    p->gamma  = G4*G3*G2*G1;
+
+    p->SG[0] =  G4*G3*G2;
+	p->SG[1] =  G4*G3;
+	p->SG[2] =  G4;
+	p->SG[3] =  1.0;
+
+	/* set alphas */
+    for (i = 0; i < 4; i++) p->opva_alpha[i] = g/(1.0 + g);
+
+    /* set betas */
+    p->opva_beta[0] = 1.0/(1.0 + g - g*G2);
+	p->opva_beta[1] = 1.0/(1.0 + g - 0.5*g*G3);
+	p->opva_beta[2] = 1.0/(1.0 + g - 0.5*g*G4);
+	p->opva_beta[3] = 1.0/(1.0 + g);
+
+	/* set gammas */
+	p->opva_gamma[0] = 1.0 + G1*G2;
+	p->opva_gamma[1] = 1.0 + G2*G3;
+	p->opva_gamma[2] = 1.0 + G3*G4;
+
+    /* set deltas */
+	p->opva_delta[0] = g;
+	p->opva_delta[1] = 0.5*g;
+	p->opva_delta[2] = 0.5*g;
+
+    /* set epsilons */
+	p->opva_eps[0] = G2;
+	p->opva_eps[1] = G3;
+	p->opva_eps[2] = G4;
+}
+
+int sp_diode_init(sp_data *sp, sp_diode *p)
+{
+    int i;
+    /* initialize the 4 one-pole VA filters */
+
+    for (i = 0; i < 4; i++) {
+        p->opva_alpha[i] = 1.0;
+        p->opva_beta[i] = -1.0;
+        p->opva_gamma[i] = 1.0;
+        p->opva_delta[i] = 0.0;
+        p->opva_eps[i] = 1.0;
+        p->opva_fdbk[i] = 0.0;
+        p->opva_a0[i] = 1.0;
+        p->opva_z1[i] = 0.0;
+
+        p->SG[i] = 0.0;
+    }
+
+	p->gamma = 0.0;
+	p->K = 0.0;
+
+	/* Filter coeffs that are constant */
+	/* set a0s */
+    p->opva_a0[0] = 1.0;
+    p->opva_a0[1] = 0.5;
+    p->opva_a0[2] = 0.5;
+    p->opva_a0[3] = 0.5;
+
+	/* last LPF has no feedback path */
+    p->opva_gamma[3] = 1.0;
+    p->opva_delta[3] = 0.0;
+    p->opva_eps[3] = 0.0;
+    p->opva_fdbk[3] = 0.0;
+
+    /* default cutoff to 1000hz */
+    p->freq = 1000;
+    p->res = 0;
+    /* update filter coefs */
+
+    sp_diode_update(sp, p);
+    return SP_OK;
+}
+
+int sp_diode_compute(sp_data *sp, sp_diode *p, SPFLOAT *in, SPFLOAT *out)
+{
+    int i;
+    SPFLOAT sigma;
+    SPFLOAT un;
+    SPFLOAT tmp = 0.0;
+
+    /* update filter coefficients */
+    p->K = p->res * 17;
+    sp_diode_update(sp, p);
+
+    p->opva_fdbk[2] = sp_diode_opva_fdbk_out(sp, p, 3);
+    p->opva_fdbk[1] = sp_diode_opva_fdbk_out(sp, p, 2);
+    p->opva_fdbk[0] = sp_diode_opva_fdbk_out(sp, p, 1);
+
+    sigma =
+        p->SG[0] * sp_diode_opva_fdbk_out(sp, p, 0) +
+        p->SG[1] * sp_diode_opva_fdbk_out(sp, p, 1) +
+        p->SG[2] * sp_diode_opva_fdbk_out(sp, p, 2) +
+        p->SG[3] * sp_diode_opva_fdbk_out(sp, p, 3);
+
+    un = (*in - p->K * sigma) / (1 + p->K * p->gamma);
+    tmp = un;
+    for (i = 0; i < 4; i++) {
+        tmp = sp_diode_opva_compute(sp, p, tmp, i);
+    }
+    *out = tmp;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/dmetro.c
@@ -1,0 +1,37 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_dmetro_create(sp_dmetro **p)
+{
+    *p = malloc(sizeof(sp_dmetro));
+    return SP_OK;
+}
+
+int sp_dmetro_destroy(sp_dmetro **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_dmetro_init(sp_data *sp, sp_dmetro *p)
+{
+    p->counter = 0;
+    p->time = 1.0;
+    return SP_OK;
+}
+
+int sp_dmetro_compute(sp_data *sp, sp_dmetro *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = 0;
+
+    if (p->counter == 0) {
+        *out = 1.0;
+        p->counter = (int)(sp->sr * p->time) + 1;
+    }
+
+    p->counter--;
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/dtrig.c
@@ -1,0 +1,54 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_dtrig_create(sp_dtrig **p)
+{
+    *p = malloc(sizeof(sp_dtrig));
+    return SP_OK;
+}
+
+int sp_dtrig_destroy(sp_dtrig **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_dtrig_init(sp_data *sp, sp_dtrig *p, sp_ftbl *ft)
+{
+    p->ft = ft;
+    p->counter = 0;
+    p->pos = 0;
+    p->running = 0;
+    p->loop = 0;
+    p->delay = 0;
+    p->scale = 1;
+    return SP_OK;
+}
+
+int sp_dtrig_compute(sp_data *sp, sp_dtrig *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in == 1.0) {
+        p->running = 1.0;
+        p->pos = 0;
+        p->counter = p->delay * sp->sr;
+    }
+    if ((p->pos < p->ft->size) && p->running) {
+        if (p->counter == 0) {
+            p->counter = (uint32_t)(p->scale * p->ft->tbl[p->pos] * sp->sr - 1);
+            *out = 1.0;
+            p->pos++;
+            if (p->loop) {
+                p->pos %= p->ft->size;
+            }
+            return SP_OK;
+        } else {
+            *out = 0;
+            p->counter--;
+            return SP_OK;
+        }
+    }
+    *out = 0;
+    return SP_NOT_OK;
+}
--- /dev/null
+++ b/modules/expon.c
@@ -1,0 +1,68 @@
+/* This code is placed in the public domain. */
+
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_expon_create(sp_expon **p)
+{
+    *p = malloc(sizeof(sp_expon));
+    return SP_OK;
+}
+
+int sp_expon_destroy(sp_expon **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+static void expon_reinit(sp_data *sp, sp_expon *p)
+{
+    SPFLOAT onedsr;
+    p->stime = 0;
+    p->sdur = p->dur * sp->sr;
+    onedsr = 1.0 / sp->sr;
+
+    if ((p->a * p->b) > 0.0) {
+        p->incr = pow((SPFLOAT)(p->b / p->a), onedsr / p->dur);
+    } else {
+        p->incr = 1;
+        p->val = p->a;
+    }
+
+    p->val = p->a;
+}
+
+int sp_expon_init(sp_data *sp, sp_expon *p)
+{
+    p->a = 0.000001;
+    p->b = 1;
+    p->dur = 1;
+    expon_reinit(sp, p);
+    p->init = 1;
+    return SP_OK;
+}
+
+int sp_expon_compute(sp_data *sp, sp_expon *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in) {
+        expon_reinit(sp, p);
+        p->init = 0;
+    }
+
+    if (p->init) {
+        *out = 0;
+        return SP_OK;
+    }
+
+    if (p->stime < p->sdur) {
+        SPFLOAT val = p->val;
+        p->val *= p->incr;
+        p->stime++;
+        *out = val;
+    } else {
+        *out = p->b;
+    }
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/fftwrapper.c
@@ -1,0 +1,130 @@
+/*
+  FFTwrapper.c  -  A wrapper for Fast Fourier Transforms
+  Author: Nasca O. Paul, Tg. Mures, Romania
+
+  De-classified and ported to C code by Paul Batchelor 2015
+
+  License: Public Domain
+
+  This requires FFTW library (http://www.fftw.org) to compile and run.
+*/
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "soundpipe.h"
+#include "kiss_fftr.h"
+
+void FFTwrapper_create(FFTwrapper **fw, int fftsize)
+{
+    FFTwrapper *fwp;
+    *fw = malloc(sizeof(FFTwrapper));
+    fwp = *fw;
+    fwp->fftsize = fftsize;
+
+#ifdef USE_FFTW3
+    fftw_real *tf1 = malloc(fftsize * sizeof(fftw_real));
+    fftw_real *tf2 = malloc(fftsize * sizeof(fftw_real));
+    fwp->tmpfftdata1 = tf1;
+    fwp->tmpfftdata2 = tf2;
+    fwp->planfftw = fftw_plan_r2r_1d(fftsize, fwp->tmpfftdata1,
+            fwp->tmpfftdata1, FFTW_R2HC, FFTW_ESTIMATE);
+    fwp->planfftw_inv = fftw_plan_r2r_1d(fftsize, fwp->tmpfftdata2,
+            fwp->tmpfftdata2, FFTW_HC2R, FFTW_ESTIMATE);
+#else
+    fwp->fft = kiss_fftr_alloc(fftsize, 0, NULL, NULL);
+    fwp->ifft = kiss_fftr_alloc(fftsize, 1, NULL, NULL);
+    fwp->tmp1 = KISS_FFT_MALLOC(sizeof(kiss_fft_cpx) * fftsize);
+    fwp->tmp2 = KISS_FFT_MALLOC(sizeof(kiss_fft_cpx) * fftsize);
+    memset(fwp->tmp1, 0, sizeof(kiss_fft_cpx) * fftsize);
+    memset(fwp->tmp2, 0, sizeof(kiss_fft_cpx) * fftsize);
+#endif
+
+}
+
+void FFTwrapper_destroy(FFTwrapper **fw)
+{
+    FFTwrapper *fwp = *fw;
+#ifdef USE_FFTW3
+    fftw_destroy_plan(fwp->planfftw);
+    fftw_destroy_plan(fwp->planfftw_inv);
+    free(fwp->tmpfftdata1);
+    free(fwp->tmpfftdata2);
+#else
+    kiss_fftr_free(fwp->fft);
+    kiss_fftr_free(fwp->ifft);
+    KISS_FFT_FREE(fwp->tmp1);
+    KISS_FFT_FREE(fwp->tmp2);
+#endif
+    free(*fw);
+}
+
+/* do the Fast Fourier Transform */
+
+void smps2freqs(FFTwrapper *ft, SPFLOAT *smps, FFTFREQS *freqs)
+{
+    int i;
+
+#ifdef USE_FFTW3
+    for (i = 0; i < ft->fftsize; i++) ft->tmpfftdata1[i]=smps[i];
+    fftw_execute(ft->planfftw);
+
+    for (i = 0; i < ft->fftsize/2; i++) {
+        freqs.c[i] = ft->tmpfftdata1[i];
+        if (i != 0) freqs.s[i] = ft->tmpfftdata1[ft->fftsize-i];
+    }
+
+    ft->tmpfftdata2[ft->fftsize/2] = 0.0;
+#else
+    kiss_fftr(ft->fft, smps, ft->tmp1);
+    for (i = 0; i < ft->fftsize/2; i++) {
+        freqs->c[i] = ft->tmp1[i].r;
+        freqs->s[i] = ft->tmp1[i].i;
+    }
+#endif
+}
+
+/*
+ * do the Inverse Fast Fourier Transform
+ */
+void freqs2smps(FFTwrapper *ft, FFTFREQS *freqs, SPFLOAT *smps)
+{
+
+    int i;
+#ifdef USE_FFTW3
+    ft->tmpfftdata2[ft->fftsize/2]=0.0;
+    for (i=0; i<ft->fftsize/2 ;i++) {
+        ft->tmpfftdata2[i]=freqs.c[i];
+        if (i != 0) ft->tmpfftdata2[ft->fftsize-i]=freqs.s[i];
+    }
+    fftw_execute(ft->planfftw_inv);
+    for (i = 0; i < ft->fftsize; i++) smps[i]=ft->tmpfftdata2[i];
+#else
+    for (i = 0; i < ft->fftsize / 2; i++) {
+        ft->tmp2[i].r = freqs->c[i];
+        ft->tmp2[i].i = freqs->s[i];
+    }
+    kiss_fftri(ft->ifft, ft->tmp2, smps);
+#endif
+
+}
+
+void newFFTFREQS(FFTFREQS *f,int size)
+{
+    int i;
+    SPFLOAT *c = malloc(size * sizeof(SPFLOAT));
+    SPFLOAT *s = malloc(size * sizeof(SPFLOAT));
+    f->c = c;
+    f->s = s;
+    for (i = 0; i<size ;i++) {
+        f->c[i]=0.0;
+        f->s[i]=0.0;
+    };
+}
+
+void deleteFFTFREQS(FFTFREQS *f)
+{
+    free(f->c);
+    free(f->s);
+    f->c= f->s = NULL;
+}
--- /dev/null
+++ b/modules/fmpair.c
@@ -1,0 +1,54 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#define SK_FMPAIR_PRIV
+#include "tangled/fmpair.h"
+#include "soundpipe.h"
+
+int sp_fmpair_create(sp_fmpair **p)
+{
+    *p = malloc(sizeof(sp_fmpair));
+    return SP_OK;
+}
+
+int sp_fmpair_destroy(sp_fmpair **p)
+{
+    sp_fmpair *pp;
+    pp = *p;
+    free(pp->fmpair);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_fmpair_init(sp_data *sp, sp_fmpair *p, sp_ftbl *ft)
+{
+    p->fmpair = malloc(sizeof(sk_fmpair));
+    sk_fmpair_init(p->fmpair, sp->sr,
+                   ft->tbl, ft->size, 0,
+                   ft->tbl, ft->size, 0);
+
+    p->mod = 1.0;
+    p->car = 1.0;
+    p->indx = 1.0;
+    p->amp = 0.4;
+    p->freq = 440;
+
+    sk_fmpair_freq(p->fmpair, p->freq);
+    sk_fmpair_modulator(p->fmpair, p->mod);
+    sk_fmpair_carrier(p->fmpair, p->car);
+    sk_fmpair_modindex(p->fmpair, p->indx);
+    return SP_OK;
+}
+
+int sp_fmpair_compute(sp_data *sp, sp_fmpair *p,
+                      SPFLOAT *in, SPFLOAT *out)
+{
+    sk_fmpair_freq(p->fmpair, p->freq);
+    sk_fmpair_modulator(p->fmpair, p->mod);
+    sk_fmpair_carrier(p->fmpair, p->car);
+    sk_fmpair_modindex(p->fmpair, p->indx);
+    *out = sk_fmpair_tick(p->fmpair) * p->amp;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/foo.c
@@ -1,0 +1,36 @@
+/*
+ * Foo
+ *
+ * This is a dummy module. It doesn't do much.
+ * Feel free to use this as a boilerplate template.
+ *
+ */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_foo_create(sp_foo **p)
+{
+    *p = malloc(sizeof(sp_foo));
+    return SP_OK;
+}
+
+int sp_foo_destroy(sp_foo **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_foo_init(sp_data *sp, sp_foo *p)
+{
+    /* Initalize variables here. */
+    p->bar = 123;
+    return SP_OK;
+}
+
+int sp_foo_compute(sp_data *sp, sp_foo *p, SPFLOAT *in, SPFLOAT *out)
+{
+    /* Send the signal's input to the output */
+    *out = *in;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/ftbl.c
@@ -1,0 +1,106 @@
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef NO_LIBSNDFILE
+#include <sndfile.h>
+#endif
+#include "soundpipe.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define tpd360 0.0174532925199433
+
+int sp_ftbl_init(sp_data *sp, sp_ftbl *ft, size_t size)
+{
+    ft->size = size;
+    return SP_OK;
+}
+
+int sp_ftbl_create(sp_data *sp, sp_ftbl **ft, size_t size)
+{
+    sp_ftbl *ftp;
+    *ft = malloc(sizeof(sp_ftbl));
+    ftp = *ft;
+    ftp->tbl = malloc(sizeof(SPFLOAT) * (size + 1));
+    memset(ftp->tbl, 0, sizeof(SPFLOAT) * (size + 1));
+
+    sp_ftbl_init(sp, ftp, size);
+    return SP_OK;
+}
+
+int sp_ftbl_destroy(sp_ftbl **ft)
+{
+    sp_ftbl *ftp = *ft;
+    free(ftp->tbl);
+    free(*ft);
+    return SP_OK;
+}
+
+static char * tokenize(char **next, int *size)
+{
+    char *token;
+    char *str;
+    char *peak;
+
+    if (*size <= 0) return NULL;
+    token = *next;
+    str = *next;
+
+    peak = str + 1;
+
+    while ((*size)--) {
+        if (*str == ' ') {
+            *str = 0;
+            if (*peak != ' ') break;
+        }
+        str = str + 1;
+        peak = str + 1;
+    }
+    *next = peak;
+    return token;
+}
+
+int sp_gen_vals(sp_data *sp, sp_ftbl *ft, const char *string)
+{
+    int size;
+    char *str;
+    char *out;
+    char *ptr;
+    int j;
+
+    size = strlen(string);
+    str = malloc(sizeof(char) * size + 1);
+    strcpy(str, string);
+    ptr = str;
+    j = 0;
+
+    while (size > 0) {
+        out = tokenize(&str, &size);
+        if (ft->size < j + 1) {
+            ft->tbl = realloc(ft->tbl, sizeof(SPFLOAT) * (ft->size + 2));
+            /* zero out new tables */
+            ft->tbl[ft->size] = 0;
+            ft->tbl[ft->size + 1] = 0;
+            ft->size++;
+        }
+        ft->tbl[j] = atof(out);
+        j++;
+    }
+
+    sp_ftbl_init(sp, ft, ft->size);
+    free(ptr);
+    return SP_OK;
+}
+
+int sp_gen_sine(sp_data *sp, sp_ftbl *ft)
+{
+    unsigned long i;
+    SPFLOAT step = 2 * M_PI / ft->size;
+    for (i = 0; i < ft->size; i++) {
+        ft->tbl[i] = sin(i * step);
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/in.c
@@ -1,0 +1,31 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_in_create(sp_in **p)
+{
+    *p = malloc(sizeof(sp_in));
+    return SP_OK;
+}
+
+int sp_in_destroy(sp_in **p)
+{
+    sp_in *pp = *p;
+    fclose(pp->fp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_in_init(sp_data *sp, sp_in *p)
+{
+    p->fp = stdin;
+    return SP_OK;
+}
+
+int sp_in_compute(sp_data *sp, sp_in *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = 0;
+    fread(out, sizeof(SPFLOAT), 1, p->fp);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/incr.c
@@ -1,0 +1,47 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+#ifndef max
+#define max(a, b) ((a > b) ? a : b)
+#endif
+
+#ifndef min
+#define min(a, b) ((a < b) ? a : b)
+#endif
+
+
+int sp_incr_create(sp_incr **p)
+{
+    *p = malloc(sizeof(sp_incr));
+    return SP_OK;
+}
+
+int sp_incr_destroy(sp_incr **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_incr_init(sp_data *sp, sp_incr *p, SPFLOAT val)
+{
+    p->min = 0;
+    p->max = 1;
+    p->step = 0.1;
+    p->val = val;
+    return SP_OK;
+}
+
+int sp_incr_compute(sp_data *sp, sp_incr *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in > 0 ) {
+        p->val += p->step;
+        p->val = max(min(p->val, p->max), p->min);
+    } else if (*in < 0) {
+        p->val -= p->step;
+        p->val = max(min(p->val, p->max), p->min);
+    }
+    *out = p->val;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/jcrev.c
@@ -1,0 +1,327 @@
+/*
+ * JCrev
+ *
+ * This code has been partially generated using Faust.
+ * See the file "jcrev.dsp" to see the original faust code utilized.
+ *
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT SPFLOAT
+#endif
+
+typedef struct {
+
+	float fVec5[4096];
+	float fVec6[4096];
+	float fVec3[2048];
+	float fVec4[2048];
+	float fVec0[512];
+	float fVec1[128];
+	float fVec2[64];
+	float fRec6[2];
+	float fRec4[2];
+	float fRec2[2];
+	float fRec0[2];
+	float fRec1[2];
+	float fRec8[2];
+	float fRec9[2];
+	float fRec10[2];
+	float fRec11[2];
+	float fRec12[2];
+	float fRec13[2];
+	int IOTA;
+	int fSamplingFreq;
+
+} jcrev;
+
+jcrev* newjcrev() {
+	jcrev* dsp = (jcrev*)malloc(sizeof(jcrev));
+	return dsp;
+}
+
+void deletejcrev(jcrev* dsp) {
+	free(dsp);
+}
+
+void instanceInitjcrev(jcrev* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->IOTA = 0;
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 512); i0 = (i0 + 1)) {
+			dsp->fVec0[i0] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec6[i1] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 128); i2 = (i2 + 1)) {
+			dsp->fVec1[i2] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i3;
+		for (i3 = 0; (i3 < 2); i3 = (i3 + 1)) {
+			dsp->fRec4[i3] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i4;
+		for (i4 = 0; (i4 < 64); i4 = (i4 + 1)) {
+			dsp->fVec2[i4] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i5;
+		for (i5 = 0; (i5 < 2); i5 = (i5 + 1)) {
+			dsp->fRec2[i5] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i6;
+		for (i6 = 0; (i6 < 2048); i6 = (i6 + 1)) {
+			dsp->fVec3[i6] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i7;
+		for (i7 = 0; (i7 < 2); i7 = (i7 + 1)) {
+			dsp->fRec0[i7] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i8;
+		for (i8 = 0; (i8 < 2); i8 = (i8 + 1)) {
+			dsp->fRec1[i8] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i9;
+		for (i9 = 0; (i9 < 2048); i9 = (i9 + 1)) {
+			dsp->fVec4[i9] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i10;
+		for (i10 = 0; (i10 < 2); i10 = (i10 + 1)) {
+			dsp->fRec8[i10] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i11;
+		for (i11 = 0; (i11 < 2); i11 = (i11 + 1)) {
+			dsp->fRec9[i11] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i12;
+		for (i12 = 0; (i12 < 4096); i12 = (i12 + 1)) {
+			dsp->fVec5[i12] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i13;
+		for (i13 = 0; (i13 < 2); i13 = (i13 + 1)) {
+			dsp->fRec10[i13] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i14;
+		for (i14 = 0; (i14 < 2); i14 = (i14 + 1)) {
+			dsp->fRec11[i14] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i15;
+		for (i15 = 0; (i15 < 4096); i15 = (i15 + 1)) {
+			dsp->fVec6[i15] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i16;
+		for (i16 = 0; (i16 < 2); i16 = (i16 + 1)) {
+			dsp->fRec12[i16] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i17;
+		for (i17 = 0; (i17 < 2); i17 = (i17 + 1)) {
+			dsp->fRec13[i17] = 0.f;
+
+		}
+
+	}
+
+}
+
+void initjcrev(jcrev* dsp, int samplingFreq) {
+	instanceInitjcrev(dsp, samplingFreq);
+}
+
+void computejcrev(jcrev* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* input0 = inputs[0];
+	FAUSTFLOAT* output0 = outputs[0];
+	FAUSTFLOAT* output1 = outputs[1];
+	FAUSTFLOAT* output2 = outputs[2];
+	FAUSTFLOAT* output3 = outputs[3];
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			float fTemp0 = ((0.7f * dsp->fRec6[1]) + (0.06f * (float)input0[i]));
+			dsp->fVec0[(dsp->IOTA & 511)] = fTemp0;
+			dsp->fRec6[0] = dsp->fVec0[((dsp->IOTA - 346) & 511)];
+			float fRec7 = (0.f - (0.7f * fTemp0));
+			float fTemp1 = (dsp->fRec6[1] + (fRec7 + (0.7f * dsp->fRec4[1])));
+			dsp->fVec1[(dsp->IOTA & 127)] = fTemp1;
+			dsp->fRec4[0] = dsp->fVec1[((dsp->IOTA - 112) & 127)];
+			float fRec5 = (0.f - (0.7f * fTemp1));
+			float fTemp2 = (dsp->fRec4[1] + (fRec5 + (0.7f * dsp->fRec2[1])));
+			dsp->fVec2[(dsp->IOTA & 63)] = fTemp2;
+			dsp->fRec2[0] = dsp->fVec2[((dsp->IOTA - 36) & 63)];
+			float fRec3 = (0.f - (0.7f * fTemp2));
+			float fTemp3 = (dsp->fRec2[1] + (fRec3 + (0.802f * dsp->fRec0[1])));
+			dsp->fVec3[(dsp->IOTA & 2047)] = fTemp3;
+			dsp->fRec0[0] = dsp->fVec3[((dsp->IOTA - 1600) & 2047)];
+			dsp->fRec1[0] = fTemp3;
+			float fTemp4 = (fRec3 + dsp->fRec2[1]);
+			float fTemp5 = (fTemp4 + (0.773f * dsp->fRec8[1]));
+			dsp->fVec4[(dsp->IOTA & 2047)] = fTemp5;
+			dsp->fRec8[0] = dsp->fVec4[((dsp->IOTA - 1866) & 2047)];
+			dsp->fRec9[0] = fTemp5;
+			float fTemp6 = (fTemp4 + (0.753f * dsp->fRec10[1]));
+			dsp->fVec5[(dsp->IOTA & 4095)] = fTemp6;
+			dsp->fRec10[0] = dsp->fVec5[((dsp->IOTA - 2052) & 4095)];
+			dsp->fRec11[0] = fTemp6;
+			float fTemp7 = (fTemp4 + (0.733f * dsp->fRec12[1]));
+			dsp->fVec6[(dsp->IOTA & 4095)] = fTemp7;
+			dsp->fRec12[0] = dsp->fVec6[((dsp->IOTA - 2250) & 4095)];
+			dsp->fRec13[0] = fTemp7;
+			float fTemp8 = (((dsp->fRec1[1] + dsp->fRec9[1]) + dsp->fRec11[1]) + dsp->fRec13[1]);
+			output0[i] = (FAUSTFLOAT)fTemp8;
+			output1[i] = (FAUSTFLOAT)(0.f - fTemp8);
+			float fTemp9 = (dsp->fRec1[1] + dsp->fRec11[1]);
+			float fTemp10 = (dsp->fRec9[1] + dsp->fRec13[1]);
+			output2[i] = (FAUSTFLOAT)(0.f - (fTemp9 - fTemp10));
+			output3[i] = (FAUSTFLOAT)(0.f - (fTemp10 - fTemp9));
+			dsp->IOTA = (dsp->IOTA + 1);
+			dsp->fRec6[1] = dsp->fRec6[0];
+			dsp->fRec4[1] = dsp->fRec4[0];
+			dsp->fRec2[1] = dsp->fRec2[0];
+			dsp->fRec0[1] = dsp->fRec0[0];
+			dsp->fRec1[1] = dsp->fRec1[0];
+			dsp->fRec8[1] = dsp->fRec8[0];
+			dsp->fRec9[1] = dsp->fRec9[0];
+			dsp->fRec10[1] = dsp->fRec10[0];
+			dsp->fRec11[1] = dsp->fRec11[0];
+			dsp->fRec12[1] = dsp->fRec12[0];
+			dsp->fRec13[1] = dsp->fRec13[0];
+
+		}
+
+	}
+
+}
+
+
+int sp_jcrev_create(sp_jcrev **p)
+{
+    *p = malloc(sizeof(sp_jcrev));
+    return SP_OK;
+}
+
+int sp_jcrev_destroy(sp_jcrev **p)
+{
+    sp_jcrev *pp = *p;
+    jcrev *dsp = pp->ud;
+    deletejcrev(dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_jcrev_init(sp_data *sp, sp_jcrev *p)
+{
+    jcrev *dsp = newjcrev();
+    initjcrev(dsp, sp->sr);
+    p->ud = dsp;
+    return SP_OK;
+}
+
+int sp_jcrev_compute(sp_data *sp, sp_jcrev *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    jcrev *dsp = p->ud;
+    SPFLOAT out1 = 0, out2 = 0, out3 = 0, out4 = 0;
+    SPFLOAT *faust_out[] = {&out1, &out2, &out3, &out4};
+    computejcrev(dsp, 1, &in, faust_out);
+
+    /* As you can see, only 1 out of the 4 channels are being used */
+    *out = out1;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/line.c
@@ -1,0 +1,59 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_line_create(sp_line **p)
+{
+    *p = malloc(sizeof(sp_line));
+    return SP_OK;
+}
+
+int sp_line_destroy(sp_line **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+static void line_reinit(sp_data *sp, sp_line *p)
+{
+    SPFLOAT onedsr = 1.0 / sp->sr;
+    p->incr = (SPFLOAT)((p->b - p->a) / (p->dur)) * onedsr;
+    p->val = p->a;
+    p->stime = 0;
+    p->sdur = sp->sr * p->dur;
+}
+
+int sp_line_init(sp_data *sp, sp_line *p)
+{
+    p->a = 0;
+    p->dur = 0.5;
+    p->b = 1;
+    line_reinit(sp, p);
+    p->init = 1;
+    return SP_OK;
+}
+
+int sp_line_compute(sp_data *sp, sp_line *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in != 0 ) {
+        line_reinit(sp, p);
+        p->init = 0;
+    }
+
+    if (p->init) {
+        *out = 0;
+        return SP_OK;
+    }
+
+    if (p->stime < p->sdur) {
+        SPFLOAT val = p->val;
+        p->val += p->incr;
+        p->stime++;
+        *out = val;
+    } else {
+        *out = p->b;
+    }
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/loadwav.c
@@ -1,0 +1,27 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+#include "lib/dr_wav/sp_dr_wav.h"
+
+int sp_ftbl_loadwav(sp_data *sp, sp_ftbl **ft, const char *filename)
+{
+    drwav *wav;
+    size_t size;
+    SPFLOAT *tbl;
+    sp_ftbl *ftp;
+
+    wav = calloc(1, sp_drwav_size());
+    if (!sp_drwav_init_file(wav, filename)) return SP_NOT_OK;
+
+    size = sp_drwav_sampcount(wav);
+    *ft = malloc(sizeof(sp_ftbl));
+    ftp = *ft;
+    ftp->tbl = malloc(sizeof(SPFLOAT) * (size + 1));
+    tbl = ftp->tbl;
+    sp_ftbl_init(sp, ftp, size);
+    sp_drwav_read_f32(wav, size, tbl);
+    sp_drwav_uninit(wav);
+    free(wav);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/lpc.c
@@ -1,0 +1,104 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "openlpc.h"
+
+int sp_lpc_create(sp_lpc **lpc)
+{
+    *lpc = malloc(sizeof(sp_lpc));
+    return SP_OK;
+}
+
+int sp_lpc_destroy(sp_lpc **lpc)
+{
+    sp_lpc *plpc;
+    plpc = *lpc;
+    sp_auxdata_free(&plpc->m_e);
+    sp_auxdata_free(&plpc->m_d);
+    sp_auxdata_free(&plpc->m_out);
+    sp_auxdata_free(&plpc->m_in);
+    free(*lpc);
+    return SP_OK;
+}
+
+int sp_lpc_init(sp_data *sp, sp_lpc *lpc, int framesize)
+{
+    int i;
+    lpc->counter = 0;
+    lpc->clock = 0;
+    lpc->block = 4;
+    lpc->samp = 0;
+    lpc->mode = 0;
+    lpc->framesize = framesize;
+    openlpc_sr(sp->sr / lpc->block);
+
+    sp_auxdata_alloc(&lpc->m_d, openlpc_get_decoder_state_size());
+    sp_auxdata_alloc(&lpc->m_e, openlpc_get_encoder_state_size());
+    lpc->d = lpc->m_d.ptr;
+    lpc->e = lpc->m_e.ptr;
+
+    sp_auxdata_alloc(&lpc->m_in, sizeof(short) * framesize);
+    sp_auxdata_alloc(&lpc->m_out, sizeof(short) * framesize);
+
+    lpc->out = lpc->m_out.ptr;
+    lpc->in = lpc->m_in.ptr;
+
+    init_openlpc_decoder_state(lpc->d, framesize);
+    init_openlpc_encoder_state(lpc->e, framesize);
+
+    for (i = 0; i < framesize; i++) {
+        lpc->in[i] = 0;
+        lpc->out[i] = 0;
+        if(i < 7) lpc->data[i] = 0;
+    }
+    return SP_OK;
+}
+
+int sp_lpc_compute(sp_data *sp, sp_lpc *lpc, SPFLOAT *in, SPFLOAT *out)
+{
+    int i;
+
+    if (lpc->clock == 0) {
+        if (lpc->counter == 0) {
+            if (lpc->mode == 0) {
+                openlpc_encode(lpc->in, lpc->data, lpc->e);
+            } else {
+                for (i = 0; i < 7; i++) {
+                    lpc->y[i] =
+                        lpc->smooth*lpc->y[i] +
+                        (1-lpc->smooth)*lpc->ft->tbl[i];
+                    lpc->data[i] = 255 * lpc->y[i];
+                }
+            }
+            openlpc_decode(sp, lpc->data, lpc->out, lpc->d);
+        }
+
+        if (lpc->mode == 0) lpc->in[lpc->counter] = *in * 32767;
+        lpc->samp = lpc->out[lpc->counter] / 32767.0;
+
+        lpc->counter = (lpc->counter + 1) % lpc->framesize;
+    }
+
+
+    lpc->clock = (lpc->clock + 1) % lpc->block;
+    *out = lpc->samp;
+
+    return SP_OK;
+}
+
+int sp_lpc_synth(sp_data *sp, sp_lpc *lpc, sp_ftbl *ft)
+{
+    int i;
+    int sr;
+    sr = sp->sr;
+
+    sr = sr / 4;
+    sr = sr / lpc->framesize;
+    lpc->ft = ft;
+    lpc->mode = 1;
+    for (i = 0; i < 7; i++) lpc->y[i] = 0;
+    lpc->smooth = exp(-1.0 / (0.01 * sr));
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/maygate.c
@@ -1,0 +1,45 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_maygate_create(sp_maygate **p)
+{
+    *p = malloc(sizeof(sp_maygate));
+    return SP_OK;
+}
+
+int sp_maygate_destroy(sp_maygate **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_maygate_init(sp_data *sp, sp_maygate *p)
+{
+    p->prob = 0.0;
+    p->gate = 0;
+    p->mode = 0;
+    return SP_OK;
+}
+
+int sp_maygate_compute(sp_data *sp, sp_maygate *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (*in == 0) {
+        if (p->mode) {
+            *out = 0;
+        } else {
+            *out = p->gate;
+        }
+        return SP_OK;
+    }
+
+    if ((1.0 * sp_rand(sp) / SP_RANDMAX) <= p->prob) {
+        *out = 1;
+        p->gate = 1;
+    } else {
+        *out = 0;
+        p->gate = 0;
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/metro.c
@@ -1,0 +1,62 @@
+/*
+ * Metro
+ *
+ * Metro produces a signal steady sequence of impulses,
+ * which is typically used as a clock signal to for other
+ * modules.
+ *
+ * Metro is very similar to the "metro" object in puredata,
+ * except that the rate parameter unit is supplied in Hz,
+ * not ms.
+ *
+ * This code has been placed in the public domain.
+ */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_metro_create(sp_metro **p)
+{
+    *p = malloc(sizeof(sp_metro));
+    return SP_OK;
+}
+
+int sp_metro_destroy(sp_metro **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_metro_init(sp_data *sp, sp_metro *p)
+{
+    p->freq = 2.0;
+    p->phs = 0;
+    p->init = 1;
+    p->onedsr = 1.0 / sp->sr;
+    return SP_OK;
+}
+
+int sp_metro_compute(sp_data *sp, sp_metro *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT phs;
+
+    phs = p->phs;
+
+    *out = 0;
+
+    if (p->init) {
+        *out = 1.0;
+        p->init = 0;
+    } else {
+        phs += p->freq * p->onedsr;
+
+        if (phs >= 1) {
+            *out = 1.0;
+            phs -= 1.0;
+        }
+    }
+
+    p->phs = phs;
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/modalres.c
@@ -1,0 +1,44 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#define SK_MODALRES_PRIV
+#include "tangled/modalres.h"
+#include "soundpipe.h"
+
+int sp_modalres_create(sp_modalres **p)
+{
+    *p = malloc(sizeof(sp_modalres));
+    return SP_OK;
+}
+
+int sp_modalres_destroy(sp_modalres **p)
+{
+    sp_modalres *pp;
+    pp = *p;
+    free(pp->modalres);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_modalres_init(sp_data *sp, sp_modalres *p)
+{
+    p->modalres = malloc(sizeof(sk_modalres));
+    sk_modalres_init(p->modalres, sp->sr);
+    p->freq = 500;
+    p->q = 50;
+
+    sk_modalres_freq(p->modalres, p->freq);
+    sk_modalres_q(p->modalres, p->q);
+    return SP_OK;
+}
+
+int sp_modalres_compute(sp_data *sp, sp_modalres *p,
+                         SPFLOAT *in, SPFLOAT *out)
+{
+    sk_modalres_freq(p->modalres, p->freq);
+    sk_modalres_q(p->modalres, p->q);
+    *out = sk_modalres_tick(p->modalres, *in);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/noise.c
@@ -1,0 +1,31 @@
+/* This code is placed in the public domain. */
+
+#include <math.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_noise_create(sp_noise **ns)
+{
+    *ns = malloc(sizeof(sp_noise));
+    return SP_OK;
+}
+
+int sp_noise_init(sp_data *sp, sp_noise *ns)
+{
+    ns->amp = 1.0;
+    return SP_OK;
+}
+
+int sp_noise_compute(sp_data *sp, sp_noise *ns, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = ((sp_rand(sp) % SP_RANDMAX) / (SP_RANDMAX * 1.0));
+    *out = (*out * 2) - 1;
+    *out *= ns->amp;
+    return SP_OK;
+}
+
+int sp_noise_destroy(sp_noise **ns)
+{
+    free(*ns);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/nsmp.c
@@ -1,0 +1,244 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "ini.h"
+
+int nano_dict_add(nano_dict *dict, const char *name)
+{
+    nano_entry *entry = malloc(sizeof(nano_entry));
+    entry->size = 0;
+    entry->speed = 1;
+    entry->pos = 0;
+    strcpy(entry->name, name);
+    dict->last->next = entry;
+    dict->last = entry;
+    dict->nval++;
+    return SP_OK;
+}
+
+int nano_ini_handler(void *user, const char *section, const char *name,
+        const char *value)
+{
+    nanosamp *ss = user;
+    nano_dict *dict = &ss->dict;
+    const char *entry_name = dict->last->name;
+
+    if (dict->init) {
+        nano_dict_add(dict, section);
+        dict->init = 0;
+    } else if(strncmp(entry_name, section, 50) != 0) {
+        nano_dict_add(dict, section);
+    }
+
+    dict->last->speed = 1.0;
+
+    if (strcmp(name, "pos") == 0) {
+        dict->last->pos = (uint32_t)(atof(value) * ss->sr);
+    } else if (strcmp(name, "size") == 0) {
+        dict->last->size = (uint32_t)(atof(value) * ss->sr);
+    } else if (strcmp(name, "speed") == 0) {
+        dict->last->speed = atof(value);
+    }
+
+    return SP_OK;
+}
+
+int nano_create(nanosamp **smp, const char *ini, int sr)
+{
+    nanosamp *psmp;
+    *smp = malloc(sizeof(nanosamp));
+    psmp = *smp;
+    strcpy(psmp->ini, ini);
+    psmp->dict.last = &psmp->dict.root;
+    psmp->dict.nval = 0;
+    psmp->dict.init = 1;
+    psmp->selected = 0;
+    psmp->curpos = 0;
+    psmp->sr = sr;
+
+    if (ini_parse(psmp->ini, nano_ini_handler, psmp) < 0) {
+        printf("Can't load file %s\n", psmp->ini);
+        return SP_NOT_OK;
+    }
+
+    return SP_OK;
+}
+
+int nano_select_from_index(nanosamp *smp, uint32_t pos)
+{
+    pos %= smp->dict.nval;
+    smp->selected = 1;
+    smp->sample = smp->index[pos];
+    smp->curpos = 0;
+    return SP_OK;
+}
+
+uint32_t nano_keyword_to_index(nanosamp *smp, const char *keyword)
+{
+    uint32_t i;
+    for (i = 0; i < smp->dict.nval; i++) {
+        if (strcmp(keyword, smp->index[i]->name)) {
+            return i;
+        }
+    }
+    return 0;
+}
+
+int nano_select(nanosamp *smp, const char *keyword)
+{
+    uint32_t i;
+    nano_dict *dict = &smp->dict;
+    nano_entry *entry = dict->root.next;
+
+    smp->curpos = 0;
+    smp->selected = 0;
+
+    for (i = 0; i < dict->nval; i++) {
+        if (strncmp(keyword, entry->name, 50) == 0) {
+            smp->selected = 1;
+            smp->sample = entry;
+            smp->curpos = 0;
+            break;
+        } else {
+            entry = entry->next;
+        }
+    }
+
+    if (smp->selected == 1) return SP_OK;
+    else return SP_NOT_OK;
+}
+
+
+int nano_compute(sp_data *sp, nanosamp *smp, SPFLOAT *out)
+{
+    if (!smp->selected) {
+        *out = 0;
+        return SP_NOT_OK;
+    }
+
+    if (smp->curpos < (SPFLOAT)smp->sample->size) {
+        SPFLOAT x1 = 0 , x2 = 0, frac = 0, tmp = 0;
+        uint32_t index = 0;
+        SPFLOAT *tbl = smp->ft->tbl;
+
+        tmp = (smp->curpos + smp->sample->pos);
+        index = floorf(tmp);
+        frac = fabs(tmp - index);
+
+        if (index >= smp->ft->size) {
+            index = smp->ft->size - 1;
+        }
+
+        x1 = tbl[index];
+        x2 = tbl[index + 1];
+        *out = x1 + (x2 - x1) * frac;
+        smp->curpos += smp->sample->speed;
+    } else {
+        smp->selected = 0;
+        *out = 0;
+    }
+
+    return SP_OK;
+}
+
+int nano_dict_destroy(nano_dict *dict)
+{
+    int i;
+    nano_entry *entry, *next;
+    entry = dict->root.next;
+
+    for (i = 0; i < dict->nval; i++) {
+        next = entry->next;
+        free(entry);
+        entry = next;
+    }
+
+    return SP_OK;
+}
+
+int nano_destroy(nanosamp **smp)
+{
+    nanosamp *psmp = *smp;
+    nano_dict_destroy(&psmp->dict);
+    free(*smp);
+    return SP_OK;
+}
+
+int nano_create_index(nanosamp *smp)
+{
+    int i;
+    nano_entry *entry, *next;
+    nano_dict *dict = &smp->dict;
+    smp->index = malloc(dict->nval * sizeof(nano_entry *));
+    entry = dict->root.next;
+
+    for (i = 0; i < dict->nval; i++) {
+        next = entry->next;
+        smp->index[i] = entry;
+        entry = next;
+    }
+    return SP_OK;
+}
+
+int nano_destroy_index(nanosamp *smp)
+{
+    free(smp->index);
+    return SP_OK;
+}
+
+int sp_nsmp_create(sp_nsmp **p)
+{
+    *p = malloc(sizeof(sp_nsmp));
+    return SP_OK;
+}
+
+int sp_nsmp_destroy(sp_nsmp **p)
+{
+    sp_nsmp *pp = *p;
+    nano_destroy_index(pp->smp);
+    nano_destroy(&pp->smp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_nsmp_init(sp_data *sp, sp_nsmp *p, sp_ftbl *ft, int sr, const char *ini)
+{
+    if (nano_create(&p->smp, ini, sr) == SP_NOT_OK) {
+        nano_destroy(&p->smp);
+        return SP_NOT_OK;
+    }
+    nano_create_index(p->smp);
+    p->smp->sr = sr;
+    p->index= 0;
+    p->triggered = 0;
+    p->smp->ft = ft;
+    return SP_OK;
+}
+
+int sp_nsmp_compute(sp_data *sp, sp_nsmp *p, SPFLOAT *trig, SPFLOAT *out)
+{
+    if (*trig != 0) {
+       p->triggered = 1;
+       nano_select_from_index(p->smp, p->index);
+    }
+
+    if (p->triggered == 1) {
+        nano_compute(sp, p->smp, out);
+    } else {
+        *out = 0;
+    }
+
+    return SP_OK;
+}
+
+int sp_nsmp_print_index(sp_data *sp, sp_nsmp *p)
+{
+    uint32_t i;
+    for (i = 0; i < p->smp->dict.nval; i++) {
+        printf("%d: key = %s\n", i, p->smp->index[i]->name);
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/osc.c
@@ -1,0 +1,40 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#define SK_OSC_PRIV
+#include "tangled/osc.h"
+#include "soundpipe.h"
+
+int sp_osc_create(sp_osc **osc)
+{
+    *osc = malloc(sizeof(sp_osc));
+    return SP_OK;
+}
+
+int sp_osc_destroy(sp_osc **osc)
+{
+    sp_osc *o;
+    o = *osc;
+    free(o->osc);
+    free(*osc);
+    return SP_OK;
+}
+
+int sp_osc_init(sp_data *sp, sp_osc *osc, sp_ftbl *ft, SPFLOAT iphs)
+{
+    osc->freq = 440.0;
+    osc->amp = 0.2;
+    osc->osc = malloc(sizeof(sk_osc));
+    sk_osc_init(osc->osc, sp->sr, ft->tbl, ft->size, iphs);
+    return SP_OK;
+}
+
+int sp_osc_compute(sp_data *sp, sp_osc *osc, SPFLOAT *in, SPFLOAT *out)
+{
+    sk_osc_freq(osc->osc, osc->freq);
+    sk_osc_amp(osc->osc, osc->amp);
+    *out = sk_osc_tick(osc->osc);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/oscmorph.c
@@ -1,0 +1,90 @@
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+int sp_oscmorph_create(sp_oscmorph **p)
+{
+    *p = malloc(sizeof(sp_oscmorph));
+    return SP_OK;
+}
+
+int sp_oscmorph_destroy(sp_oscmorph **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_oscmorph_init(sp_data *sp, sp_oscmorph *osc, sp_ftbl **ft, int nft, SPFLOAT iphs)
+{
+    int i;
+    osc->freq = 440.0;
+    osc->amp = 0.2;
+    osc->tbl = ft;
+    osc->iphs = fabs(iphs);
+    osc->inc = 0;
+    osc->lphs = ((int32_t)(osc->iphs * SP_FT_MAXLEN)) & SP_FT_PHMASK;
+    osc->wtpos = 0.0;
+    osc->nft = nft;
+    uint32_t prev = (uint32_t)ft[0]->size;
+    for (i = 0; i < nft; i++) {
+        if (prev != ft[i]->size) {
+            fprintf(stderr, "sp_oscmorph: size mismatch\n");
+            return SP_NOT_OK;
+        }
+        prev = (uint32_t)ft[i]->size;
+    }
+    return SP_OK;
+}
+
+int sp_oscmorph_compute(sp_data *sp, sp_oscmorph *osc, SPFLOAT *in, SPFLOAT *out)
+{
+    sp_ftbl *ftp1;
+    SPFLOAT amp, cps, fract, v1, v2;
+    SPFLOAT *ft1, *ft2;
+    int32_t phs, lobits, pos;
+    SPFLOAT sicvt = osc->tbl[0]->sicvt;
+
+    /* Use only the fractional part of the position or 1 */
+    if (osc->wtpos > 1.0) {
+        osc->wtpos -= (int)osc->wtpos;
+    }
+    SPFLOAT findex = osc->wtpos * (osc->nft - 1);
+    int index = floor(findex);
+    SPFLOAT wtfrac = findex - index;
+
+    lobits = osc->tbl[0]->lobits;
+    amp = osc->amp;
+    cps = osc->freq;
+    phs = osc->lphs;
+    ftp1 = osc->tbl[index];
+    ft1 = osc->tbl[index]->tbl;
+
+    if (index >= osc->nft - 1) {
+        ft2 = ft1;
+    } else {
+        ft2 = osc->tbl[index + 1]->tbl;
+    }
+
+    osc->inc = (int32_t)lrintf(cps * sicvt);
+
+    fract = ((phs) & ftp1->lomask) * ftp1->lodiv;
+
+    pos = phs >> lobits;
+
+    v1 = (1 - wtfrac) *
+        *(ft1 + pos) +
+        wtfrac *
+        *(ft2 + pos);
+    v2 = (1 - wtfrac) *
+        *(ft1 + ((pos + 1) % ftp1->size))+
+        wtfrac *
+        *(ft2 + ((pos + 1) % ftp1->size));
+
+    *out = (v1 + (v2 - v1) * fract) * amp;
+
+    phs += osc->inc;
+    phs &= SP_FT_PHMASK;
+
+    osc->lphs = phs;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/padsynth.c
@@ -1,0 +1,127 @@
+/*
+    Example implementation of the PADsynth basic algorithm
+    By: Nasca O. Paul, Tg. Mures, Romania
+
+    Ported to pure C by Paul Batchelor
+
+    This implementation and the algorithm are released under Public Domain
+    Feel free to use it into your projects or your products ;-)
+
+    This implementation is tested under GCC/Linux, but it's
+    very easy to port to other compiler/OS.
+*/
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+#ifndef M_PI
+#define M_PI		3.14159265358979323846
+#endif
+
+int sp_gen_padsynth(sp_data *sp, sp_ftbl *ps, sp_ftbl *amps,
+        SPFLOAT f, SPFLOAT bw)
+{
+
+    int i, nh;
+    int N = (int) ps->size;
+    int number_harmonics = (int) amps->size;
+    SPFLOAT *A = amps->tbl;
+    SPFLOAT *smp = ps->tbl;
+
+    SPFLOAT *freq_amp = malloc((N / 2) * sizeof(SPFLOAT));
+    SPFLOAT *freq_phase = malloc((N / 2) * sizeof(SPFLOAT));
+
+
+    N = (int) ps->size;
+    number_harmonics = (int) amps->size;
+    A = amps->tbl;
+    smp = ps->tbl;
+
+    freq_amp = malloc((N / 2) * sizeof(SPFLOAT));
+    freq_phase = malloc((N / 2) * sizeof(SPFLOAT));
+
+    for (i = 0; i < N/2; i++) freq_amp[i]=0.0;
+
+    for (nh=1; nh < number_harmonics; nh++) {
+        SPFLOAT bw_Hz;
+        SPFLOAT bwi;
+        SPFLOAT fi;
+        bw_Hz = (pow(2.0, bw/1200.0) - 1.0) * f * nh;
+        bwi = bw_Hz/(2.0*ps->size);
+        fi = f*nh/ps->size;
+        for (i = 0; i < N/2 ; i++) {
+            SPFLOAT hprofile;
+            hprofile = sp_padsynth_profile((i / (SPFLOAT) N) - fi, bwi);
+            freq_amp[i] += hprofile*A[nh];
+        }
+    }
+
+    for (i = 0; i < N/2; i++) {
+        freq_phase[i] = (sp_rand(sp) / (SP_RANDMAX + 1.0)) * 2.0 * M_PI;
+    };
+
+    sp_padsynth_ifft(N,freq_amp,freq_phase,smp);
+    sp_padsynth_normalize(N,smp);
+
+    free(freq_amp);
+    free(freq_phase);
+    return SP_OK;
+}
+
+/* This is the profile of one harmonic
+   In this case is a Gaussian distribution (e^(-x^2))
+   The amplitude is divided by the bandwidth to ensure that the harmonic
+   keeps the same amplitude regardless of the bandwidth */
+
+SPFLOAT sp_padsynth_profile(SPFLOAT fi, SPFLOAT bwi)
+{
+    SPFLOAT x =fi/bwi;
+    x *= x;
+
+    /*
+     * this avoids computing the e^(-x^2) where
+     * it's results are very close to zero
+     */
+
+    if (x>14.71280603) return 0.0;
+
+    return exp(-x)/bwi;
+}
+
+int sp_padsynth_ifft(int N, SPFLOAT *freq_amp,
+        SPFLOAT *freq_phase, SPFLOAT *smp)
+{
+    int i;
+    FFTwrapper *fft;
+    FFTFREQS fftfreqs;
+
+    FFTwrapper_create(&fft, N);
+    newFFTFREQS(&fftfreqs,N/2);
+
+    for (i = 0; i < N/2; i++) {
+        fftfreqs.c[i] = freq_amp[i]*cos(freq_phase[i]);
+        fftfreqs.s[i] = freq_amp[i]*sin(freq_phase[i]);
+    };
+
+    freqs2smps(fft, &fftfreqs,smp);
+    deleteFFTFREQS(&fftfreqs);
+    FFTwrapper_destroy(&fft);
+    return SP_OK;
+}
+
+/*
+    Simple normalization function. It normalizes the sound to 1/sqrt(2)
+*/
+
+int sp_padsynth_normalize(int N, SPFLOAT *smp)
+{
+    int i;
+    SPFLOAT max=0.0;
+    for (i = 0; i < N;i++) {
+        if (fabs(smp[i]) > max) max = fabs(smp[i]);
+    }
+    if (max < 1e-5) max = 1e-5;
+    for (i = 0; i < N; i++) smp[i] /= max*1.4142;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/paulstretch.c
@@ -1,0 +1,156 @@
+/*
+ * PaulStretch
+ *
+ * An implementation of the PaulStretch algorithm by Paul Nasca Octavian.
+ * This code is based off the Python Numpy/Scipy implementation of
+ * PaulStretch, found here: https://github.com/paulnasca/paulstretch_python
+ *
+ * This implementation has been placed in the public domain.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "kiss_fftr.h"
+
+#ifndef M_PI
+#define M_PI		3.14159265358979323846
+#endif
+
+static void compute_block(sp_data *sp, sp_paulstretch *p) {
+    uint32_t istart_pos = floor(p->start_pos);
+    uint32_t pos;
+    uint32_t i;
+    uint32_t windowsize = p->windowsize;
+    uint32_t half_windowsize = p->half_windowsize;
+    SPFLOAT *buf = p->buf;
+    SPFLOAT *hinv_buf = p->hinv_buf;
+    SPFLOAT *old_windowed_buf= p->old_windowed_buf;
+    SPFLOAT *tbl = p->ft->tbl;
+    SPFLOAT *window = p->window;
+    SPFLOAT *output= p->output;
+
+    for (i = 0; i < windowsize; i++) {
+        /* Loop through buffer */
+        pos = (istart_pos + i);
+
+        if (p->wrap) {
+            pos %= p->ft->size;
+        }
+
+        if (pos < p->ft->size) {
+            buf[i] = tbl[pos] * window[i];
+        } else {
+            buf[i] = 0;
+        }
+    }
+
+    kiss_fftr(p->fft, buf, p->tmp1);
+
+    for (i = 0; i < windowsize / 2; i++) {
+        SPFLOAT mag = sqrt(p->tmp1[i].r*p->tmp1[i].r + p->tmp1[i].i*p->tmp1[i].i);
+        SPFLOAT ph = ((SPFLOAT)sp_rand(sp) / SP_RANDMAX) * 2 * M_PI;
+        p->tmp1[i].r = mag * cos(ph);
+        p->tmp1[i].i = mag * sin(ph);
+    }
+
+    kiss_fftri(p->ifft, p->tmp1, buf);
+
+    for (i = 0; i < windowsize; i++) {
+        buf[i] *= window[i];
+        if (i < half_windowsize) {
+            output[i] = (SPFLOAT)(buf[i] + old_windowed_buf[half_windowsize + i]) / windowsize;
+            output[i] *= hinv_buf[i];
+        }
+        old_windowed_buf[i] = buf[i];
+    }
+    p->start_pos += p->displace_pos;
+}
+
+int sp_paulstretch_create(sp_paulstretch **p)
+{
+    *p = malloc(sizeof(sp_paulstretch));
+    return SP_OK;
+}
+
+int sp_paulstretch_destroy(sp_paulstretch **p)
+{
+    sp_paulstretch *pp = *p;
+    sp_auxdata_free(&pp->m_window);
+    sp_auxdata_free(&pp->m_old_windowed_buf);
+    sp_auxdata_free(&pp->m_hinv_buf);
+    sp_auxdata_free(&pp->m_buf);
+    sp_auxdata_free(&pp->m_output);
+    kiss_fftr_free(pp->fft);
+    kiss_fftr_free(pp->ifft);
+    KISS_FFT_FREE(pp->tmp1);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_paulstretch_init(sp_data *sp, sp_paulstretch *p, sp_ftbl *ft, SPFLOAT windowsize, SPFLOAT stretch)
+{
+    uint32_t i;
+    SPFLOAT hinv_sqrt2;
+    kiss_fft_cpx *tmp1;
+
+    p->ft = ft;
+    p->windowsize = (uint32_t)(sp->sr * windowsize);
+    p->stretch = stretch;
+
+    if (p->windowsize < 16) p->windowsize = 16;
+
+    p->half_windowsize = p->windowsize / 2;
+    p->displace_pos = (p->windowsize * 0.5) / p->stretch;
+
+    sp_auxdata_alloc(&p->m_window, sizeof(SPFLOAT) * p->windowsize);
+    p->window = p->m_window.ptr;
+
+    sp_auxdata_alloc(&p->m_old_windowed_buf, sizeof(SPFLOAT) * p->windowsize);
+    p->old_windowed_buf = p->m_old_windowed_buf.ptr;
+
+    sp_auxdata_alloc(&p->m_hinv_buf, sizeof(SPFLOAT) * p->half_windowsize);
+    p->hinv_buf = p->m_hinv_buf.ptr;
+
+    sp_auxdata_alloc(&p->m_buf, sizeof(SPFLOAT) * p->windowsize);
+    p->buf = p->m_buf.ptr;
+
+    sp_auxdata_alloc(&p->m_output, sizeof(SPFLOAT) * p->half_windowsize);
+    p->output = p->m_output.ptr;
+
+    /* Create Hann window */
+    for (i = 0; i < p->windowsize; i++) {
+        p->window[i] = 0.5 - cos(i * 2.0 * M_PI / (p->windowsize - 1)) * 0.5;
+    }
+    /* creatve inverse hann window */
+    hinv_sqrt2 = (1 + sqrt(0.5)) * 0.5;
+    for (i = 0; i < p->half_windowsize; i++) {
+        p->hinv_buf[i] = hinv_sqrt2 - (1.0 - hinv_sqrt2) * cos(i * 2.0 * M_PI / p->half_windowsize);
+    }
+
+    p->start_pos = 0.0;
+    p->counter = 0;
+
+    /* set up kissfft */
+    p->fft = kiss_fftr_alloc(p->windowsize, 0, NULL, NULL);
+    p->ifft = kiss_fftr_alloc(p->windowsize, 1, NULL, NULL);
+    tmp1 = malloc(sizeof(kiss_fft_cpx) * p->windowsize);
+    memset(tmp1, 0, sizeof(SPFLOAT) * p->windowsize);
+    p->tmp1 = tmp1;
+
+    /* turn on wrap mode by default */
+    p->wrap = 1;
+    return SP_OK;
+}
+
+int sp_paulstretch_compute(sp_data *sp, sp_paulstretch *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (p->counter == 0) compute_block(sp, p);
+
+    *out = p->output[p->counter];
+    p->counter = (p->counter + 1) % p->half_windowsize;
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/peakeq.c
@@ -1,0 +1,47 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#define SK_PEAKEQ_PRIV
+#include "tangled/peakeq.h"
+#include "soundpipe.h"
+
+int sp_peakeq_create(sp_peakeq **p)
+{
+    *p = malloc(sizeof(sp_peakeq));
+    return SP_OK;
+}
+
+int sp_peakeq_destroy(sp_peakeq **p)
+{
+    sp_peakeq *pp;
+    pp = *p;
+    free(pp->peakeq);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_peakeq_init(sp_data *sp, sp_peakeq *p)
+{
+    p->peakeq = malloc(sizeof(sk_peakeq));
+    sk_peakeq_init(p->peakeq, sp->sr);
+    p->freq = 1000;
+    p->bw = 125;
+    p->gain  = 2;
+
+    sk_peakeq_freq(p->peakeq, p->freq);
+    sk_peakeq_bandwidth(p->peakeq, p->bw);
+    sk_peakeq_gain(p->peakeq, p->gain);
+    return SP_OK;
+}
+
+int sp_peakeq_compute(sp_data *sp, sp_peakeq *p,
+                         SPFLOAT *in, SPFLOAT *out)
+{
+    sk_peakeq_freq(p->peakeq, p->freq);
+    sk_peakeq_bandwidth(p->peakeq, p->bw);
+    sk_peakeq_gain(p->peakeq, p->gain);
+    *out = sk_peakeq_tick(p->peakeq, *in);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/peaklim.c
@@ -1,0 +1,80 @@
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef dB
+/* if below -100dB, set to -100dB to prevent taking log of zero */
+#define dB(x) 20.0 * ((x) > 0.00001 ? log10(x) : log10(0.00001))
+#endif
+
+#ifndef dB2lin
+#define dB2lin(x)           pow( 10.0, (x) / 20.0 )
+#endif
+
+int sp_peaklim_create(sp_peaklim **p)
+{
+    *p = malloc(sizeof(sp_peaklim));
+    return SP_OK;
+}
+
+int sp_peaklim_destroy(sp_peaklim **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_peaklim_init(sp_data *sp, sp_peaklim *p)
+{
+    p->a1_r = 0;
+    p->b0_r = 1;
+    p->a1_a = 0;
+    p->b0_a = 1;
+    p->atk = 0.1;
+    p->rel = 0.1;
+    p->patk = -100;
+    p->prel = -100;
+    p->level = 0;
+    return SP_OK;
+}
+
+int sp_peaklim_compute(sp_data *sp, sp_peaklim *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    SPFLOAT db_gain = 0;
+    SPFLOAT gain = 0;
+
+    /* change coefficients, if needed */
+
+    if (p->patk != p->atk) {
+        p->patk = p->atk;
+		p->a1_a = exp( -1.0 / ( p->rel * sp->sr ) );
+		p->b0_a = 1 - p->a1_a;
+    }
+
+    if (p->prel != p->rel) {
+        p->prel = p->rel;
+		p->a1_r = exp( -1.0 / ( p->rel * sp->sr ) );
+		p->b0_r = 1 - p->a1_r;
+    }
+
+
+    if ( fabs(*in) > p->level)
+        p->level += p->b0_a * ( fabs(*in) - p->level);
+    else
+        p->level += p->b0_r * ( fabs(*in) - p->level);
+
+    db_gain = min(0.0, dB(dB2lin(p->thresh)/p->level));
+    gain = dB2lin(db_gain);
+
+    *out = *in * gain;
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/phaser.c
@@ -1,0 +1,367 @@
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT float
+#endif
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+static float faustpower2_f(float value) {
+	return (value * value);
+
+}
+static float faustpower3_f(float value) {
+	return ((value * value) * value);
+
+}
+static float faustpower4_f(float value) {
+	return (((value * value) * value) * value);
+
+}
+
+typedef struct {
+
+	float fRec4[3];
+	float fRec3[3];
+	float fRec2[3];
+	float fRec1[3];
+	float fRec11[3];
+	float fRec10[3];
+	float fRec9[3];
+	float fRec8[3];
+	int iVec0[2];
+	float fRec5[2];
+	float fRec6[2];
+	float fRec0[2];
+	float fRec7[2];
+	FAUSTFLOAT fHslider0;
+	FAUSTFLOAT fCheckbox0;
+	FAUSTFLOAT fHslider1;
+	int fSamplingFreq;
+	int iConst0;
+	float fConst1;
+	FAUSTFLOAT fHslider2;
+	FAUSTFLOAT fHslider3;
+	FAUSTFLOAT fHslider4;
+	FAUSTFLOAT fHslider5;
+	float fConst2;
+	FAUSTFLOAT fHslider6;
+	FAUSTFLOAT fHslider7;
+	FAUSTFLOAT fCheckbox1;
+
+} phaser;
+
+phaser* newphaser() {
+	phaser* dsp = (phaser*)malloc(sizeof(phaser));
+	return dsp;
+}
+
+void deletephaser(phaser* dsp) {
+	free(dsp);
+}
+
+void instanceInitphaser(phaser* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->fHslider0 = (FAUSTFLOAT)0.;
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 2); i0 = (i0 + 1)) {
+			dsp->iVec0[i0] = 0;
+
+		}
+
+	}
+	dsp->fCheckbox0 = (FAUSTFLOAT)0.;
+	dsp->fHslider1 = (FAUSTFLOAT)1.;
+	dsp->iConst0 = min(192000, max(1, dsp->fSamplingFreq));
+	dsp->fConst1 = (1.f / (float)dsp->iConst0);
+	dsp->fHslider2 = (FAUSTFLOAT)1000.;
+	dsp->fHslider3 = (FAUSTFLOAT)1.5;
+	dsp->fHslider4 = (FAUSTFLOAT)100.;
+	dsp->fHslider5 = (FAUSTFLOAT)800.;
+	dsp->fConst2 = (0.10472f / (float)dsp->iConst0);
+	dsp->fHslider6 = (FAUSTFLOAT)30.;
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec5[i1] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i2;
+		for (i2 = 0; (i2 < 2); i2 = (i2 + 1)) {
+			dsp->fRec6[i2] = 0.f;
+
+		}
+
+	}
+	dsp->fHslider7 = (FAUSTFLOAT)0.;
+	/* C99 loop */
+	{
+		int i3;
+		for (i3 = 0; (i3 < 3); i3 = (i3 + 1)) {
+			dsp->fRec4[i3] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i4;
+		for (i4 = 0; (i4 < 3); i4 = (i4 + 1)) {
+			dsp->fRec3[i4] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i5;
+		for (i5 = 0; (i5 < 3); i5 = (i5 + 1)) {
+			dsp->fRec2[i5] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i6;
+		for (i6 = 0; (i6 < 3); i6 = (i6 + 1)) {
+			dsp->fRec1[i6] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i7;
+		for (i7 = 0; (i7 < 2); i7 = (i7 + 1)) {
+			dsp->fRec0[i7] = 0.f;
+
+		}
+
+	}
+	dsp->fCheckbox1 = (FAUSTFLOAT)0.;
+	/* C99 loop */
+	{
+		int i8;
+		for (i8 = 0; (i8 < 3); i8 = (i8 + 1)) {
+			dsp->fRec11[i8] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i9;
+		for (i9 = 0; (i9 < 3); i9 = (i9 + 1)) {
+			dsp->fRec10[i9] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i10;
+		for (i10 = 0; (i10 < 3); i10 = (i10 + 1)) {
+			dsp->fRec9[i10] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i11;
+		for (i11 = 0; (i11 < 3); i11 = (i11 + 1)) {
+			dsp->fRec8[i11] = 0.f;
+
+		}
+
+	}
+	/* C99 loop */
+	{
+		int i12;
+		for (i12 = 0; (i12 < 2); i12 = (i12 + 1)) {
+			dsp->fRec7[i12] = 0.f;
+
+		}
+
+	}
+
+}
+
+void initphaser(phaser* dsp, int samplingFreq) {
+	instanceInitphaser(dsp, samplingFreq);
+}
+
+void buildUserInterfacephaser(phaser* dsp, UIGlue* interface) {
+	interface->addHorizontalSlider(interface->uiInterface, "MaxNotch1Freq", &dsp->fHslider5, 800.f, 20.f, 10000.f, 1.f);
+	interface->addHorizontalSlider(interface->uiInterface, "MinNotch1Freq", &dsp->fHslider4, 100.f, 20.f, 5000.f, 1.f);
+	interface->addHorizontalSlider(interface->uiInterface, "Notch width", &dsp->fHslider2, 1000.f, 10.f, 5000.f, 1.f);
+	interface->addHorizontalSlider(interface->uiInterface, "NotchFreq", &dsp->fHslider3, 1.5f, 1.1f, 4.f, 0.01f);
+	interface->addCheckButton(interface->uiInterface, "VibratoMode", &dsp->fCheckbox0);
+	interface->addHorizontalSlider(interface->uiInterface, "depth", &dsp->fHslider1, 1.f, 0.f, 1.f, 0.01f);
+	interface->addHorizontalSlider(interface->uiInterface, "feedback gain", &dsp->fHslider7, 0.f, 0.f, 1.f, 0.01f);
+	interface->addCheckButton(interface->uiInterface, "invert", &dsp->fCheckbox1);
+	interface->addHorizontalSlider(interface->uiInterface, "level", &dsp->fHslider0, 0.f, -60.f, 10.f, 0.1f);
+	interface->addHorizontalSlider(interface->uiInterface, "lfobpm", &dsp->fHslider6, 30.f, 24.f, 360.f, 1.f);
+}
+
+void computephaser(phaser* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* input0 = inputs[0];
+	FAUSTFLOAT* input1 = inputs[1];
+	FAUSTFLOAT* output0 = outputs[0];
+	FAUSTFLOAT* output1 = outputs[1];
+	float fSlow0 = pow(10.f, (0.05f * (float)dsp->fHslider0));
+	float fSlow1 = (0.5f * ((int)(float)dsp->fCheckbox0?2.f:(float)dsp->fHslider1));
+	float fSlow2 = (1.f - fSlow1);
+	float fSlow3 = exp((dsp->fConst1 * (0.f - (3.14159f * (float)dsp->fHslider2))));
+	float fSlow4 = faustpower2_f(fSlow3);
+	float fSlow5 = (0.f - (2.f * fSlow3));
+	float fSlow6 = (float)dsp->fHslider3;
+	float fSlow7 = (dsp->fConst1 * fSlow6);
+	float fSlow8 = (float)dsp->fHslider4;
+	float fSlow9 = (6.28319f * fSlow8);
+	float fSlow10 = (0.5f * ((6.28319f * max(fSlow8, (float)dsp->fHslider5)) - fSlow9));
+	float fSlow11 = (dsp->fConst2 * (float)dsp->fHslider6);
+	float fSlow12 = sin(fSlow11);
+	float fSlow13 = cos(fSlow11);
+	float fSlow14 = (0.f - fSlow12);
+	float fSlow15 = (float)dsp->fHslider7;
+	float fSlow16 = (dsp->fConst1 * faustpower2_f(fSlow6));
+	float fSlow17 = (dsp->fConst1 * faustpower3_f(fSlow6));
+	float fSlow18 = (dsp->fConst1 * faustpower4_f(fSlow6));
+	float fSlow19 = ((int)(float)dsp->fCheckbox1?(0.f - fSlow1):fSlow1);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			dsp->iVec0[0] = 1;
+			float fTemp0 = (float)input0[i];
+			dsp->fRec5[0] = ((fSlow12 * dsp->fRec6[1]) + (fSlow13 * dsp->fRec5[1]));
+			dsp->fRec6[0] = ((1.f + ((fSlow13 * dsp->fRec6[1]) + (fSlow14 * dsp->fRec5[1]))) - (float)dsp->iVec0[1]);
+			float fTemp1 = ((fSlow10 * (1.f - dsp->fRec5[0])) + fSlow9);
+			float fTemp2 = (dsp->fRec4[1] * cos((fSlow7 * fTemp1)));
+			dsp->fRec4[0] = (0.f - (((fSlow5 * fTemp2) + (fSlow4 * dsp->fRec4[2])) - ((fSlow0 * fTemp0) + (fSlow15 * dsp->fRec0[1]))));
+			float fTemp3 = (dsp->fRec3[1] * cos((fSlow16 * fTemp1)));
+			dsp->fRec3[0] = ((fSlow5 * (fTemp2 - fTemp3)) + (dsp->fRec4[2] + (fSlow4 * (dsp->fRec4[0] - dsp->fRec3[2]))));
+			float fTemp4 = (dsp->fRec2[1] * cos((fSlow17 * fTemp1)));
+			dsp->fRec2[0] = ((fSlow5 * (fTemp3 - fTemp4)) + (dsp->fRec3[2] + (fSlow4 * (dsp->fRec3[0] - dsp->fRec2[2]))));
+			float fTemp5 = (dsp->fRec1[1] * cos((fSlow18 * fTemp1)));
+			dsp->fRec1[0] = ((fSlow5 * (fTemp4 - fTemp5)) + (dsp->fRec2[2] + (fSlow4 * (dsp->fRec2[0] - dsp->fRec1[2]))));
+			dsp->fRec0[0] = ((fSlow4 * dsp->fRec1[0]) + ((fSlow5 * fTemp5) + dsp->fRec1[2]));
+			output0[i] = (FAUSTFLOAT)((fSlow0 * (fSlow2 * fTemp0)) + (dsp->fRec0[0] * fSlow19));
+			float fTemp6 = (float)input1[i];
+			float fTemp7 = ((fSlow10 * (1.f - dsp->fRec6[0])) + fSlow9);
+			float fTemp8 = (dsp->fRec11[1] * cos((fSlow7 * fTemp7)));
+			dsp->fRec11[0] = (0.f - (((fSlow5 * fTemp8) + (fSlow4 * dsp->fRec11[2])) - ((fSlow0 * fTemp6) + (fSlow15 * dsp->fRec7[1]))));
+			float fTemp9 = (dsp->fRec10[1] * cos((fSlow16 * fTemp7)));
+			dsp->fRec10[0] = ((fSlow5 * (fTemp8 - fTemp9)) + (dsp->fRec11[2] + (fSlow4 * (dsp->fRec11[0] - dsp->fRec10[2]))));
+			float fTemp10 = (dsp->fRec9[1] * cos((fSlow17 * fTemp7)));
+			dsp->fRec9[0] = ((fSlow5 * (fTemp9 - fTemp10)) + (dsp->fRec10[2] + (fSlow4 * (dsp->fRec10[0] - dsp->fRec9[2]))));
+			float fTemp11 = (dsp->fRec8[1] * cos((fSlow18 * fTemp7)));
+			dsp->fRec8[0] = ((fSlow5 * (fTemp10 - fTemp11)) + (dsp->fRec9[2] + (fSlow4 * (dsp->fRec9[0] - dsp->fRec8[2]))));
+			dsp->fRec7[0] = ((fSlow4 * dsp->fRec8[0]) + ((fSlow5 * fTemp11) + dsp->fRec8[2]));
+			output1[i] = (FAUSTFLOAT)((fSlow0 * (fSlow2 * fTemp6)) + (dsp->fRec7[0] * fSlow19));
+			dsp->iVec0[1] = dsp->iVec0[0];
+			dsp->fRec5[1] = dsp->fRec5[0];
+			dsp->fRec6[1] = dsp->fRec6[0];
+			dsp->fRec4[2] = dsp->fRec4[1];
+			dsp->fRec4[1] = dsp->fRec4[0];
+			dsp->fRec3[2] = dsp->fRec3[1];
+			dsp->fRec3[1] = dsp->fRec3[0];
+			dsp->fRec2[2] = dsp->fRec2[1];
+			dsp->fRec2[1] = dsp->fRec2[0];
+			dsp->fRec1[2] = dsp->fRec1[1];
+			dsp->fRec1[1] = dsp->fRec1[0];
+			dsp->fRec0[1] = dsp->fRec0[0];
+			dsp->fRec11[2] = dsp->fRec11[1];
+			dsp->fRec11[1] = dsp->fRec11[0];
+			dsp->fRec10[2] = dsp->fRec10[1];
+			dsp->fRec10[1] = dsp->fRec10[0];
+			dsp->fRec9[2] = dsp->fRec9[1];
+			dsp->fRec9[1] = dsp->fRec9[0];
+			dsp->fRec8[2] = dsp->fRec8[1];
+			dsp->fRec8[1] = dsp->fRec8[0];
+			dsp->fRec7[1] = dsp->fRec7[0];
+
+		}
+
+	}
+
+}
+
+static void addHorizontalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_phaser *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+static void addCheckButton (void* ui_interface, const char* label, FAUSTFLOAT* zone)
+{
+    sp_phaser *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_phaser_create(sp_phaser **p)
+{
+    *p = malloc(sizeof(sp_phaser));
+    return SP_OK;
+}
+
+int sp_phaser_destroy(sp_phaser **p)
+{
+    sp_phaser *pp = *p;
+    phaser *dsp = pp->faust;
+    deletephaser (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_phaser_init(sp_data *sp, sp_phaser *p)
+{
+    phaser *dsp = newphaser();
+    UIGlue UI;
+    p->argpos = 0;
+    UI.addHorizontalSlider= addHorizontalSlider;
+    UI.addCheckButton = addCheckButton;
+    UI.uiInterface = p;
+    buildUserInterfacephaser(dsp, &UI);
+    initphaser(dsp, sp->sr);
+
+
+    p->MaxNotch1Freq = p->args[0];
+    p->MinNotch1Freq = p->args[1];
+    p->Notch_width = p->args[2];
+    p->NotchFreq = p->args[3];
+    p->VibratoMode = p->args[4];
+    p->depth = p->args[5];
+    p->feedback_gain = p->args[6];
+    p->invert = p->args[7];
+    p->level = p->args[8];
+    p->lfobpm = p->args[9];
+
+    p->faust = dsp;
+    return SP_OK;
+}
+
+int sp_phaser_compute(sp_data *sp, sp_phaser *p,
+	SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2)
+{
+    phaser *dsp = p->faust;
+    SPFLOAT *faust_out[] = {out1, out2};
+    SPFLOAT *faust_in[] = {in1, in2};
+    computephaser(dsp, 1, faust_in, faust_out);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/phasewarp.c
@@ -1,0 +1,31 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#include "tangled/phasewarp.h"
+#include "soundpipe.h"
+
+int sp_phasewarp_create(sp_phasewarp **p)
+{
+    *p = malloc(sizeof(sp_phasewarp));
+    return SP_OK;
+}
+
+int sp_phasewarp_destroy(sp_phasewarp **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_phasewarp_init(sp_data *sp, sp_phasewarp *p)
+{
+    return SP_OK;
+}
+
+int sp_phasewarp_compute(sp_data *sp, sp_phasewarp *p,
+                         SPFLOAT *in, SPFLOAT *out)
+{
+    *out = sk_phasewarp_tick(*in, p->amount);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/phasor.c
@@ -1,0 +1,52 @@
+/*
+ * Phasor
+ *
+ * A phasor produces a non-bandlimited sawtooth wave,
+ * normalized to be in range 0-1. Phasors are most
+ * frequently used to create table-lookup oscillators.
+ */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_phasor_create(sp_phasor **p)
+{
+    *p = malloc(sizeof(sp_phasor));
+    return SP_OK;
+}
+
+int sp_phasor_destroy(sp_phasor **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_phasor_init(sp_data *sp, sp_phasor *p, SPFLOAT iphs)
+{
+    p->freq = 440;
+    p->phs = iphs;
+    p->onedsr = 1.0 / sp->sr;
+    return SP_OK;
+}
+
+int sp_phasor_compute(sp_data *sp, sp_phasor *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT phs;
+    SPFLOAT incr;
+
+    phs = p->phs;
+    incr = p->freq * p->onedsr;
+
+    *out = phs;
+
+    phs += incr;
+
+    if (phs >= 1.0) {
+        phs -= 1.0;
+    } else if (phs < 0.0) {
+        phs += 1.0;
+    }
+
+    p->phs = phs;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/pinknoise.c
@@ -1,0 +1,71 @@
+/*
+ * Pinknoise
+ *
+ * This code has been extracted the pink noise synthesizer from Protrekkr
+ * It has been modified to work as a Soundpipe module.
+ *
+ * Original Author(s): McCartney, Juan Antonio Arguelles
+ * Location: release/distrib/replay/lib/replay.cpp
+ *
+ */
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+
+static uint32_t ctz[64] =
+{
+    6, 0, 1, 0, 2, 0, 1, 0,
+    3, 0, 1, 0, 2, 0, 1, 0,
+    4, 0, 1, 0, 2, 0, 1, 0,
+    3, 0, 1, 0, 2, 0, 1, 0,
+    5, 0, 1, 0, 2, 0, 1, 0,
+    3, 0, 1, 0, 2, 0, 1, 0,
+    4, 0, 1, 0, 2, 0, 1, 0,
+    3, 0, 1, 0, 2, 0, 1, 0,
+};
+
+int sp_pinknoise_create(sp_pinknoise **p)
+{
+    *p = malloc(sizeof(sp_pinknoise));
+    return SP_OK;
+}
+
+int sp_pinknoise_destroy(sp_pinknoise **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_pinknoise_init(sp_data *sp, sp_pinknoise *p)
+{
+    int i;
+
+    p->amp = 1.0;
+    p->seed = sp_rand(sp);
+    p->total = 0;
+    p->counter = 0;
+
+    for (i = 0; i < 7; i++) p->dice[i] = 0;
+
+    return SP_OK;
+}
+
+int sp_pinknoise_compute(sp_data *sp, sp_pinknoise *p, SPFLOAT *in, SPFLOAT *out)
+{
+    short tmp;
+    uint32_t k;
+    k = ctz[p->counter & 63];
+    p->prevrand = p->dice[k];
+    p->seed = 1664525 * p->seed + 1013904223;
+    p->newrand = p->seed >> 3;
+    p->dice[k] = p->newrand;
+    p->total += (p->newrand - p->prevrand);
+    p->seed = 1103515245 * p->seed + 12345;
+    p->newrand = p->seed >> 3;
+    tmp = (short) ((((p->total + p->newrand) * (1.0f / (3 << 29)) - 1) - .25f) * 16384.0f);
+
+    *out = ((SPFLOAT) tmp / 32767) * p->amp;
+    p->counter = (p->counter + 1) % 0xFFFFFFFF;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/prop.c
@@ -1,0 +1,573 @@
+/*
+ * Tinyprop
+ * By Paul Batchelor
+ *
+ * A tiny C implementation of prop, a proportional rhythmic notation system
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+typedef struct {
+    uint32_t size;
+    prop_list **ar;
+} prop_slice;
+
+static int prop_create(prop_data **pd);
+static int prop_parse(prop_data *pd, const char *str);
+static prop_event prop_next(sp_data *sp, prop_data *pd);
+static float prop_time(prop_data *pd, prop_event evt);
+static int prop_destroy(prop_data **pd);
+
+static int prop_val_free(prop_val val);
+static int prop_list_init(prop_list *lst);
+static int prop_list_destroy(prop_list *lst);
+static int prop_list_append(prop_list *lst, prop_val val);
+static void prop_list_reset(prop_list *lst);
+static int prop_list_copy(prop_list *src, prop_list **dst);
+
+static void mode_insert_event(prop_data *pd, char type);
+static void mode_insert_slice(prop_data *pd);
+static void mode_list_start(prop_data *pd);
+static void mode_list_end(prop_data *pd);
+static void prop_slice_encap(prop_data *pd);
+static void prop_slice_append(prop_data *pd);
+static void reset(prop_data *pd);
+static void back_to_top(prop_data *pd);
+
+enum {
+PTYPE_SLICE,
+PTYPE_LIST,
+PTYPE_EVENT,
+PTYPE_OFF,
+PTYPE_ON,
+PTYPE_MAYBE,
+PMODE_INSERT,
+PMODE_SETDIV,
+PMODE_SETMUL,
+PMODE_UNSETMUL,
+PMODE_INIT,
+PSTATUS_NOTOK,
+PSTATUS_OK,
+PTYPE_NULL
+};
+
+int sp_prop_create(sp_prop **p)
+{
+    *p = malloc(sizeof(sp_prop));
+    return SP_OK;
+}
+
+int sp_prop_destroy(sp_prop **p)
+{
+    sp_prop *pp = *p;
+    prop_destroy(&pp->prp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_prop_init(sp_data *sp, sp_prop *p, const char *str)
+{
+    p->count = 0;
+
+    prop_create(&p->prp);
+
+    if (prop_parse(p->prp, str) == PSTATUS_NOTOK) {
+        fprintf(stderr,"There was an error parsing the string.\n");
+        return SP_NOT_OK;
+    }
+
+    p->bpm = 60;
+    p->lbpm = 60;
+    return SP_OK;
+}
+
+int sp_prop_compute(sp_data *sp, sp_prop *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (p->count == 0) {
+        if (p->bpm != p->lbpm) {
+            p->prp->scale = (SPFLOAT) 60.0 / p->bpm;
+            p->lbpm = p->bpm;
+        }
+
+        p->evt = prop_next(sp, p->prp);
+        p->count = prop_time(p->prp, p->evt) * sp->sr;
+
+        switch (p->evt.type) {
+            case PTYPE_ON:
+                *out = 1.0;
+                break;
+            case PTYPE_MAYBE:
+                if (((SPFLOAT) sp_rand(sp) / SP_RANDMAX) > 0.5) *out = 1.0;
+                else *out = 0.0;
+                break;
+            default:
+                *out = 0.0;
+                break;
+        }
+        return SP_OK;
+    }
+
+    *out = 0;
+    p->count--;
+
+    return SP_OK;
+}
+
+static int stack_push(prop_stack *ps, uint32_t val)
+{
+    if (ps->pos++ < 16) {
+        ps->stack[ps->pos] = val;
+    }
+    return SP_OK;
+}
+
+static void stack_init(prop_stack *ps)
+{
+    int n;
+    ps->pos = -1;
+    for (n = 0; n < 16; n++) ps->stack[n] = 1;
+}
+
+static uint32_t stack_pop(prop_stack *ps)
+{
+    if (ps->pos >= 0) {
+        return ps->stack[ps->pos--];
+    }
+    return 1;
+}
+
+static void mode_insert_event(prop_data *pd, char type)
+{
+    prop_val val;
+    prop_event *evt;
+#ifdef DEBUG_PROP
+    if (type == PTYPE_ON) {
+        printf("mode_insert: PTYPE_ON\n");
+    } else {
+        printf("mode_insert: PTYPE_OFF\n");
+    }
+    printf("\tval/mul = %d, pos = %d, cons = %d, div = %d\n",
+            pd->mul, pd->num, pd->cons_mul, pd->div);
+#endif
+
+    val.type = PTYPE_EVENT;
+    evt = malloc(sizeof(prop_event));
+    evt->type = type;
+    evt->val = pd->mul;
+    evt->cons = pd->cons_mul;
+    val.ud = evt;
+    prop_list_append(pd->main, val);
+}
+
+static void mode_setdiv(prop_data *pd, char n)
+{
+    if (pd->tmp == 0 && n == 0) n = 1;
+    pd->tmp *= 10;
+    pd->tmp += n;
+}
+
+static void mode_setmul(prop_data *pd)
+{
+    pd->mul *= pd->tmp;
+    pd->div = pd->tmp;
+    stack_push(&pd->mstack, pd->tmp);
+    pd->tmp = 0;
+}
+
+static void mode_unsetmul(prop_data *pd)
+{
+    uint32_t div = stack_pop(&pd->mstack);
+#ifdef DEBUG_PROP
+    printf("mul / div = %d / %d\n", pd->mul, div);
+#endif
+    pd->mul /= div;
+}
+
+static void mode_setcons(prop_data *pd)
+{
+    pd->cons_mul *= pd->tmp;
+    pd->cons_div = pd->tmp;
+    stack_push(&pd->cstack, pd->tmp);
+    pd->tmp = 0;
+}
+
+static void mode_unsetcons(prop_data *pd)
+{
+    uint32_t div = stack_pop(&pd->cstack);
+#ifdef DEBUG_PROP
+    printf("mul / div = %d / %d\n", pd->cons_mul, div);
+#endif
+    pd->cons_mul /= div;
+}
+
+static int prop_create(prop_data **pd)
+{
+    prop_data *pdp;
+    *pd = malloc(sizeof(prop_data));
+    pdp = *pd;
+
+    pdp->mul = 1;
+    pdp->div = 0;
+    pdp->scale = 1;
+    pdp->cons_mul = 1;
+    pdp->cons_div = 0;
+    pdp->mode = PMODE_INIT;
+    pdp->pos = 1;
+    pdp->main = &pdp->top;
+    pdp->main->lvl = 0;
+    pdp->tmp = 0;
+
+    stack_init(&pdp->mstack);
+    stack_init(&pdp->cstack);
+    prop_list_init(pdp->main);
+
+    return PSTATUS_OK;
+}
+
+static int prop_parse(prop_data *pd, const char *str)
+{
+    char c;
+
+    while (*str != 0) {
+        c = str[0];
+
+        switch(c) {
+            case '+':
+                mode_insert_event(pd, PTYPE_ON);
+                break;
+            case '?':
+                mode_insert_event(pd, PTYPE_MAYBE);
+                break;
+            case '-':
+                mode_insert_event(pd, PTYPE_OFF);
+                break;
+
+            case '0':
+                mode_setdiv(pd, 0);
+                break;
+            case '1':
+                mode_setdiv(pd, 1);
+                break;
+            case '2':
+                mode_setdiv(pd, 2);
+                break;
+            case '3':
+                mode_setdiv(pd, 3);
+                break;
+            case '4':
+                mode_setdiv(pd, 4);
+                break;
+            case '5':
+                mode_setdiv(pd, 5);
+                break;
+            case '6':
+                mode_setdiv(pd, 6);
+                break;
+            case '7':
+                mode_setdiv(pd, 7);
+                break;
+            case '8':
+                mode_setdiv(pd, 8);
+                break;
+            case '9':
+                mode_setdiv(pd, 9);
+                break;
+            case '(':
+                mode_setmul(pd);
+                break;
+            case ')':
+                mode_unsetmul(pd);
+                break;
+            case '[':
+                mode_setcons(pd);
+                break;
+            case ']':
+                mode_unsetcons(pd);
+                break;
+            case '|':
+                mode_insert_slice(pd);
+                break;
+            case '{':
+                mode_list_start(pd);
+                break;
+            case '}':
+                mode_list_end(pd);
+                break;
+            case ' ': break;
+            case '\n': break;
+            case '\t': break;
+
+            default:
+                return PSTATUS_NOTOK;
+        }
+        pd->pos++;
+        str++;
+    }
+    prop_list_reset(&pd->top);
+    pd->main = &pd->top;
+    return PSTATUS_OK;
+}
+
+prop_val prop_list_iterate(prop_list *lst)
+{
+    prop_val val;
+    if (lst->pos >= lst->size) {
+        prop_list_reset(lst);
+    }
+    val = lst->last->val;
+    lst->last = lst->last->next;
+    lst->pos++;
+    return val;
+}
+
+static void back_to_top(prop_data *pd)
+{
+    prop_list *lst = pd->main;
+    prop_list_reset(lst);
+    pd->main = lst->top;
+    reset(pd);
+}
+
+static void reset(prop_data *pd)
+{
+    prop_list *lst = pd->main;
+    if (lst->pos >= lst->size) {
+        back_to_top(pd);
+    }
+}
+
+prop_event prop_next(sp_data *sp, prop_data *pd)
+{
+/*
+    prop_list *lst = pd->main;
+
+    if(lst->pos >= lst->size) {
+        //prop_list_reset(lst);
+        pd->main = lst->top;
+    }
+*/
+    prop_list *lst;
+    prop_event *p;
+    prop_val val;
+
+    reset(pd);
+    lst = pd->main;
+    val = lst->last->val;
+
+    lst->last = lst->last->next;
+    lst->pos++;
+
+    switch (val.type) {
+        case PTYPE_SLICE: {
+            prop_slice *slice = (prop_slice *)val.ud;
+
+            uint32_t pos = floor(
+                ((SPFLOAT)sp_rand(sp) / SP_RANDMAX)
+                * slice->size);
+
+            pd->main = slice->ar[pos];
+            prop_list_reset(pd->main);
+            return prop_next(sp, pd);
+            break;
+        }
+        case PTYPE_LIST: {
+            prop_list *lst = (prop_list *)val.ud;
+            pd->main = lst;
+            prop_list_reset(pd->main);
+            return prop_next(sp, pd);
+            break;
+        }
+        default:
+            break;
+    }
+    p = (prop_event *)val.ud;
+    return *p;
+}
+
+static float prop_time(prop_data *pd, prop_event evt)
+{
+    float val = evt.cons * (pd->scale / evt.val);
+    return val;
+}
+
+static int prop_destroy(prop_data **pd)
+{
+    prop_data *pdp = *pd;
+
+    prop_list_destroy(&pdp->top);
+
+    free(*pd);
+    return PSTATUS_OK;
+}
+
+static int prop_list_init(prop_list *lst)
+{
+    lst->last = &lst->root;
+    lst->size = 0;
+    lst->pos = 0;
+    lst->root.val.type = PTYPE_NULL;
+    lst->top = lst;
+    return PSTATUS_OK;
+}
+
+static int prop_list_append(prop_list *lst, prop_val val)
+{
+    prop_entry *new;
+    new = malloc(sizeof(prop_entry));
+    new->val = val;
+    lst->last->next = new;
+    lst->last = new;
+    lst->size++;
+    return PSTATUS_OK;
+}
+
+static int prop_slice_free(prop_slice *slice)
+{
+    uint32_t i;
+    for (i = 0; i < slice->size; i++) {
+        prop_list_destroy(slice->ar[i]);
+        free(slice->ar[i]);
+    }
+    free(slice->ar);
+    return PSTATUS_OK;
+}
+
+static int prop_val_free(prop_val val)
+{
+    switch (val.type) {
+        case PTYPE_SLICE:
+            prop_slice_free((prop_slice *)val.ud);
+            free(val.ud);
+            break;
+        case PTYPE_LIST:
+            prop_list_destroy((prop_list *)val.ud);
+            free(val.ud);
+            break;
+        default:
+            free(val.ud);
+            break;
+    }
+    return PSTATUS_OK;
+}
+
+static int prop_list_destroy(prop_list *lst)
+{
+    prop_entry *entry = lst->root.next;
+    prop_entry *next;
+    uint32_t i;
+
+    for (i = 0; i < lst->size; i++) {
+        next = entry->next;
+        prop_val_free(entry->val);
+        free(entry);
+        entry = next;
+    }
+    return PSTATUS_OK;
+}
+
+static void prop_list_reset(prop_list *lst)
+{
+    lst->last = lst->root.next;
+    lst->pos = 0;
+}
+
+static void mode_insert_slice(prop_data *pd)
+{
+    prop_entry *entry;
+
+    entry = pd->main->top->last;
+    if (entry->val.type != PTYPE_SLICE) {
+        prop_slice_encap(pd);
+    } else {
+        prop_slice_append(pd);
+    }
+}
+
+static void prop_slice_encap(prop_data *pd)
+{
+    prop_val val;
+    prop_list *lst, *new;
+    prop_list *top;
+    prop_slice *slice;
+
+    top = pd->main->top;
+    val.type = PTYPE_SLICE;
+    slice = malloc(sizeof(prop_slice));
+    val.ud = slice;
+    prop_list_copy(pd->main, &lst);
+    new = malloc(sizeof(prop_list));
+    new->lvl = pd->main->lvl;
+    slice->size = 2;
+    slice->ar =
+        (prop_list **)malloc(sizeof(prop_list *) * slice->size);
+    slice->ar[0] = lst;
+    /* reinit main list */
+    prop_list_init(pd->main);
+    prop_list_append(pd->main, val);
+    slice->ar[1] = new;
+    prop_list_init(slice->ar[1]);
+    pd->main = slice->ar[1];
+
+    slice->ar[0]->top = top;
+    slice->ar[1]->top = top;
+}
+
+static void prop_slice_append(prop_data *pd)
+{
+    prop_entry *entry;
+    prop_slice *slice;
+    prop_list *new;
+
+    entry = pd->main->top->last;
+    slice = (prop_slice *)entry->val.ud;
+    new = malloc(sizeof(prop_list));
+    prop_list_init(new);
+    slice->size++;
+    slice->ar = (prop_list **)
+        realloc(slice->ar, sizeof(prop_list *) * slice->size);
+    slice->ar[slice->size - 1] = new;
+    new->top = pd->main->top;
+    pd->main = new;
+}
+
+static int prop_list_copy(prop_list *src, prop_list **dst)
+{
+    prop_list *pdst;
+    *dst = malloc(sizeof(prop_list));
+    pdst = *dst;
+    pdst->root = src->root;
+    pdst->last = src->last;
+    pdst->size = src->size;
+    pdst->pos = src->pos;
+    pdst->lvl = src->lvl;
+    return PSTATUS_OK;
+}
+
+static void mode_list_start(prop_data *pd)
+{
+    prop_val val;
+    prop_list *new;
+    val.type = PTYPE_LIST;
+    new = malloc(sizeof(prop_list));
+    prop_list_init(new);
+    new->lvl = pd->main->lvl + 1;
+    val.ud = new;
+    prop_list_append(pd->main, val);
+    new->top = pd->main;
+    pd->main = new;
+}
+
+static void mode_list_end(prop_data *pd)
+{
+    pd->main = pd->main->top;
+}
+
+int sp_prop_reset(sp_data *sp, sp_prop *p)
+{
+    back_to_top(p->prp);
+    p->count = 0;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/pshift.c
@@ -1,0 +1,153 @@
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+#include "CUI.h"
+
+#define max(a,b) ((a < b) ? b : a)
+#define min(a,b) ((a < b) ? a : b)
+
+#ifndef FAUSTFLOAT
+#define FAUSTFLOAT SPFLOAT
+#endif
+
+
+float powf(float dummy0, float dummy1);
+float fmodf(float dummy0, float dummy1);
+
+typedef struct {
+	float fVec0[65536];
+	float fRec0[2];
+	int IOTA;
+	FAUSTFLOAT fHslider0;
+	FAUSTFLOAT fHslider1;
+	FAUSTFLOAT fHslider2;
+	int fSamplingFreq;
+} pshift;
+
+static pshift* newpshift() {
+	pshift* dsp = (pshift*)malloc(sizeof(pshift));
+	return dsp;
+}
+
+static void deletepshift(pshift* dsp) {
+	free(dsp);
+}
+
+static void instanceInitpshift(pshift* dsp, int samplingFreq) {
+	dsp->fSamplingFreq = samplingFreq;
+	dsp->IOTA = 0;
+	/* C99 loop */
+	{
+		int i0;
+		for (i0 = 0; (i0 < 65536); i0 = (i0 + 1)) {
+			dsp->fVec0[i0] = 0.f;
+
+		}
+
+	}
+	dsp->fHslider0 = (FAUSTFLOAT)1000.;
+	dsp->fHslider1 = (FAUSTFLOAT)0.;
+	/* C99 loop */
+	{
+		int i1;
+		for (i1 = 0; (i1 < 2); i1 = (i1 + 1)) {
+			dsp->fRec0[i1] = 0.f;
+
+		}
+
+	}
+	dsp->fHslider2 = (FAUSTFLOAT)10.;
+}
+
+static void initpshift(pshift* dsp, int samplingFreq) {
+	instanceInitpshift(dsp, samplingFreq);
+}
+
+static void buildUserInterfacepshift(pshift* dsp, UIGlue* interface) {
+	interface->addHorizontalSlider(interface->uiInterface, "shift", &dsp->fHslider1, 0.f, -24.f, 24.f, 0.1f);
+	interface->addHorizontalSlider(interface->uiInterface, "window", &dsp->fHslider0, 1000.f, 50.f, 10000.f, 1.f);
+	interface->addHorizontalSlider(interface->uiInterface, "xfade", &dsp->fHslider2, 10.f, 1.f, 10000.f, 1.f);
+}
+
+static void computepshift(pshift* dsp, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
+	FAUSTFLOAT* input0 = inputs[0];
+	FAUSTFLOAT* output0 = outputs[0];
+	float fSlow0 = (float)dsp->fHslider0;
+	float fSlow1 = ((1.f + fSlow0) - powf(2.f, (0.0833333f * (float)dsp->fHslider1)));
+	float fSlow2 = (1.f / (float)dsp->fHslider2);
+	float fSlow3 = (fSlow0 - 1.f);
+	/* C99 loop */
+	{
+		int i;
+		for (i = 0; (i < count); i = (i + 1)) {
+			float fTemp0 = (float)input0[i];
+			dsp->fVec0[(dsp->IOTA & 65535)] = fTemp0;
+			dsp->fRec0[0] = fmodf((dsp->fRec0[1] + fSlow1), fSlow0);
+			int iTemp1 = (int)dsp->fRec0[0];
+			int iTemp2 = (1 + iTemp1);
+			float fTemp3 = min((fSlow2 * dsp->fRec0[0]), 1.f);
+			float fTemp4 = (dsp->fRec0[0] + fSlow0);
+			int iTemp5 = (int)fTemp4;
+			output0[i] = (FAUSTFLOAT)((((dsp->fVec0[((dsp->IOTA - (iTemp1 & 65535)) & 65535)] * ((float)iTemp2 - dsp->fRec0[0])) + ((dsp->fRec0[0] - (float)iTemp1) * dsp->fVec0[((dsp->IOTA - (iTemp2 & 65535)) & 65535)])) * fTemp3) + (((dsp->fVec0[((dsp->IOTA - (iTemp5 & 65535)) & 65535)] * (0.f - ((dsp->fRec0[0] + fSlow3) - (float)iTemp5))) + ((fTemp4 - (float)iTemp5) * dsp->fVec0[((dsp->IOTA - ((1 + iTemp5) & 65535)) & 65535)])) * (1.f - fTemp3)));
+			dsp->IOTA = (dsp->IOTA + 1);
+			dsp->fRec0[1] = dsp->fRec0[0];
+
+		}
+
+	}
+
+}
+
+static void addHorizontalSlider(void* ui_interface, const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
+{
+    sp_pshift *p = ui_interface;
+    p->args[p->argpos] = zone;
+    p->argpos++;
+}
+
+int sp_pshift_create(sp_pshift **p)
+{
+    *p = malloc(sizeof(sp_pshift));
+    return SP_OK;
+}
+
+int sp_pshift_destroy(sp_pshift **p)
+{
+    sp_pshift *pp = *p;
+    pshift *dsp = pp->faust;
+    deletepshift (dsp);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_pshift_init(sp_data *sp, sp_pshift *p)
+{
+    pshift *dsp = newpshift();
+    UIGlue UI;
+    p->argpos = 0;
+    UI.addHorizontalSlider= addHorizontalSlider;
+    UI.uiInterface = p;
+    buildUserInterfacepshift(dsp, &UI);
+    initpshift(dsp, sp->sr);
+
+
+    p->shift = p->args[0];
+    p->window = p->args[1];
+    p->xfade = p->args[2];
+
+    p->faust = dsp;
+    return SP_OK;
+}
+
+int sp_pshift_compute(sp_data *sp, sp_pshift *p, SPFLOAT *in, SPFLOAT *out)
+{
+
+    pshift *dsp = p->faust;
+    SPFLOAT out1 = 0;
+    SPFLOAT *faust_out[] = {&out1};
+    SPFLOAT *faust_in[] = {in};
+    computepshift(dsp, 1, faust_in, faust_out);
+
+    *out = out1;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/randh.c
@@ -1,0 +1,46 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_randh_create(sp_randh **p)
+{
+    *p = malloc(sizeof(sp_randh));
+    return SP_OK;
+}
+
+int sp_randh_destroy(sp_randh **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_randh_init(sp_data *sp, sp_randh *p)
+{
+    p->counter = 0;
+    p->freq = 10;
+    p->dur = (sp->sr / p->freq);
+    p->min = 0;
+    p->max = 1;
+    p->val = 0;
+    return SP_OK;
+}
+
+int sp_randh_compute(sp_data *sp, sp_randh *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (p->counter == 0) {
+        p->val = p->min + ((SPFLOAT) sp_rand(sp) / SP_RANDMAX) * (p->max - p->min);
+
+        if (p->freq == 0) {
+            p->dur = 1;
+        } else {
+            p->dur = (sp->sr / p->freq) + 1;
+        }
+
+        *out = p->val;
+    } else {
+        *out = p->val;
+    }
+    p->counter = (p->counter + 1) % p->dur;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/randmt.c
@@ -1,0 +1,133 @@
+/*
+   A C-program for MT19937, with initialisation improved 2002/1/26.
+   Coded by Takuji Nishimura and Makoto Matsumoto.
+
+   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+   All rights reserved.
+
+   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 names of its contributors may not be used to endorse or promote
+        products derived from this software without specific prior written
+        permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+
+   Any feedback is very welcome.
+   http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+   email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+#include "soundpipe.h"
+#define N           (624)
+#define M           (397)
+#define MATRIX_A    0x9908B0DFU     /* constant vector a */
+#define UPPER_MASK  0x80000000U     /* most significant w-r bits */
+#define LOWER_MASK  0x7FFFFFFFU     /* least significant r bits */
+
+static void MT_update_state(uint32_t *mt)
+{
+    /* mag01[x] = x * MATRIX_A  for x=0,1 */
+    const uint32_t  mag01[2] = { (uint32_t) 0, (uint32_t) MATRIX_A };
+    int       i;
+    uint32_t  y;
+
+    for (i = 0; i < (N - M); i++) {
+      y = (mt[i] & UPPER_MASK) | (mt[i + 1] & LOWER_MASK);
+      mt[i] = mt[i + M] ^ (y >> 1) ^ mag01[y & (uint32_t) 1];
+    }
+    for ( ; i < (N - 1); i++) {
+      y = (mt[i] & UPPER_MASK) | (mt[i + 1] & LOWER_MASK);
+      mt[i] = mt[i + (M - N)] ^ (y >> 1) ^ mag01[y & (uint32_t) 1];
+    }
+    y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
+    mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & (uint32_t) 1];
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+
+uint32_t sp_randmt_compute(sp_randmt *p)
+{
+    int       i = p->mti;
+    uint32_t  y;
+
+    if (i >= N) {                   /* generate N words at one time */
+      MT_update_state(&(p->mt[0]));
+      i = 0;
+    }
+    y = p->mt[i];
+    p->mti = i + 1;
+    /* Tempering */
+    y ^= (y >> 11);
+    y ^= (y << 7) & (uint32_t) 0x9D2C5680U;
+    y ^= (y << 15) & (uint32_t) 0xEFC60000U;
+    y ^= (y >> 18);
+
+    return y;
+}
+
+void sp_randmt_seed(sp_randmt *p,
+    const uint32_t *initKey, uint32_t keyLength)
+{
+    int       i, j, k;
+    uint32_t  x;
+
+    /* if array is NULL, use length parameter as simple 32 bit seed */
+    x = (initKey == NULL ? keyLength : (uint32_t) 19650218);
+    p->mt[0] = x;
+    for (i = 1; i < N; i++) {
+      /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+      /* In the previous versions, MSBs of the seed affect   */
+      /* only MSBs of the array mt[].                        */
+      /* 2002/01/09 modified by Makoto Matsumoto             */
+      x = ((uint32_t) 1812433253 * (x ^ (x >> 30)) + (uint32_t) i);
+      p->mt[i] = x;
+    }
+    p->mti = N;
+    if (initKey == NULL)
+      return;
+    i = 0; j = 0;
+    k = (N > (int) keyLength ? N : (int) keyLength);
+    for ( ; k; k--) {
+      x = p->mt[i++];
+      p->mt[i] = (p->mt[i] ^ ((x ^ (x >> 30)) * (uint32_t) 1664525))
+                 + initKey[j] + (uint32_t) j;   /* non linear */
+      if (i == (N - 1)) {
+        p->mt[0] = p->mt[N - 1];
+        i = 0;
+      }
+      if (++j >= (int) keyLength)
+        j = 0;
+    }
+    for (k = (N - 1); k; k--) {
+      x = p->mt[i++];
+      p->mt[i] = (p->mt[i] ^ ((x ^ (x >> 30)) * (uint32_t) 1566083941))
+                 - (uint32_t) i;                /* non linear */
+      if (i == (N - 1)) {
+        p->mt[0] = p->mt[N - 1];
+        i = 0;
+      }
+    }
+    /* MSB is 1; assuring non-zero initial array */
+    p->mt[0] = (uint32_t) 0x80000000U;
+}
--- /dev/null
+++ b/modules/random.c
@@ -1,0 +1,33 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_random_create(sp_random **p)
+{
+    *p = malloc(sizeof(sp_random));
+    return SP_OK;
+}
+
+int sp_random_destroy(sp_random **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_random_init(sp_data *sp, sp_random *p)
+{
+    p->min = -1;
+    p->max = 1;
+    return SP_OK;
+}
+
+int sp_random_compute(sp_data *sp, sp_random *p, SPFLOAT *in, SPFLOAT *out)
+{
+    /* Send the signal's input to the output */
+    SPFLOAT rnd = ((sp_rand(sp) % RAND_MAX) / (RAND_MAX * 1.0));
+    rnd *= (p->max - p->min);
+    rnd += p->min;
+    *out = rnd;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/reverse.c
@@ -1,0 +1,37 @@
+/* This code is placed in the public domain. */
+
+#include <string.h>
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_reverse_create(sp_reverse **p)
+{
+    *p = malloc(sizeof(sp_reverse));
+    return SP_OK;
+}
+
+int sp_reverse_destroy(sp_reverse **p)
+{
+    sp_reverse *pp = *p;
+    sp_auxdata_free(&pp->buf);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_reverse_init(sp_data *sp, sp_reverse *p, SPFLOAT delay)
+{
+    size_t size = delay * sp->sr * sizeof(SPFLOAT) * 2;
+    p->bufpos = 0;
+    sp_auxdata_alloc(&p->buf, size);
+    p->bufsize = (uint32_t)p->buf.size / sizeof(SPFLOAT);
+    return SP_OK;
+}
+
+int sp_reverse_compute(sp_data *sp, sp_reverse *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT *buf = (SPFLOAT *)p->buf.ptr;
+    *out = buf[p->bufpos];
+    buf[(p->bufsize - 1) - p->bufpos] = *in;
+    p->bufpos = (p->bufpos + 1) % p->bufsize;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/rline.c
@@ -1,0 +1,47 @@
+/* this file is placed in the public domain */
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#define SK_RLINE_PRIV
+#include "tangled/rline.h"
+#include "soundpipe.h"
+
+int sp_rline_create(sp_rline **p)
+{
+    *p = malloc(sizeof(sp_rline));
+    return SP_OK;
+}
+
+int sp_rline_destroy(sp_rline **p)
+{
+    sp_rline *pp;
+    pp = *p;
+    free(pp->rline);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_rline_init(sp_data *sp, sp_rline *p)
+{
+    p->rline = malloc(sizeof(sk_rline));
+    sk_rline_init(p->rline, sp->sr, sp_rand(sp));
+    p->min = 0;
+    p->max = 1;
+    p->cps = 3;
+
+    sk_rline_min(p->rline, p->min);
+    sk_rline_max(p->rline, p->max);
+    sk_rline_rate(p->rline, p->cps);
+    return SP_OK;
+}
+
+int sp_rline_compute(sp_data *sp, sp_rline *p,
+                         SPFLOAT *in, SPFLOAT *out)
+{
+    sk_rline_min(p->rline, p->min);
+    sk_rline_max(p->rline, p->max);
+    sk_rline_rate(p->rline, p->cps);
+    *out = sk_rline_tick(p->rline);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/rpt.c
@@ -1,0 +1,92 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+static int sp_rpt_set(sp_rpt *p, SPFLOAT bpm, int div, int rep);
+
+int sp_rpt_create(sp_rpt **p)
+{
+    *p = malloc(sizeof(sp_rpt));
+    return SP_OK;
+}
+
+int sp_rpt_destroy(sp_rpt **p)
+{
+    sp_rpt *pp = *p;
+    sp_auxdata_free(&pp->aux);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_rpt_init(sp_data *sp, sp_rpt *p, SPFLOAT maxdur)
+{
+    sp_auxdata_alloc(&p->aux, sizeof(SPFLOAT) * (uint32_t)maxdur * sp->sr);
+    p->playpos = 0;
+    p->bufpos = 0;
+    p->running = 0;
+    p->reps = 4;
+    p->count = p->reps;
+    p->size = (int)p->aux.size;
+    p->sr = sp->sr;
+    p->bpm = 130;
+    p->div = 4;
+    p->rep = 4;
+    p->rc = SP_OK;
+    return SP_OK;
+}
+
+int sp_rpt_compute(sp_data *sp, sp_rpt *p, SPFLOAT *trig,
+        SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT *buf = (SPFLOAT *)p->aux.ptr;
+
+    if (p->rc == SP_NOT_OK) {
+        *out = 0;
+        return SP_NOT_OK;
+    }
+
+    if (*trig > 0) {
+        p->rc = sp_rpt_set(p, p->bpm, p->div, p->rep);
+        p->running = 1;
+        p->playpos = 0;
+        p->bufpos = 0;
+        p->count = p->reps + 1;
+    }
+
+    if (p->bufpos * sizeof(SPFLOAT) < p->aux.size) {
+        p->rc = sp_rpt_set(p, p->bpm, p->div, p->rep);
+        buf[p->bufpos] = *in;
+        p->bufpos++;
+    } else {
+        p->running = 0;
+    }
+
+    if(p->running && p->count > 0) {
+        if (p->playpos == 0) {
+            p->count--;
+        }
+        *out = buf[p->playpos];
+        p->playpos = (p->playpos + 1) % p->size;
+    } else {
+        *out = *in;
+    }
+
+    return SP_OK;
+}
+
+static int sp_rpt_set(sp_rpt *p, SPFLOAT bpm, int div, int rep)
+{
+    uint32_t size = (p->sr * (60.0 / bpm)) / (SPFLOAT) div;
+    p->reps = rep;
+    if (size * sizeof(SPFLOAT) > p->aux.size) {
+        fprintf(stderr, "Error: not enough memory allocated for buffer.\n");
+        return SP_NOT_OK;
+    } else if(size <= 0) {
+        fprintf(stderr, "Error: Size cannot be zero.\n");
+        return SP_NOT_OK;
+    } else {
+        p->size = size;
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/samphold.c
@@ -1,0 +1,31 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_samphold_create(sp_samphold **p)
+{
+    *p = malloc(sizeof(sp_samphold));
+    return SP_OK;
+}
+
+int sp_samphold_destroy(sp_samphold **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_samphold_init(sp_data *sp, sp_samphold *p)
+{
+    p->val = 0;
+    return SP_OK;
+}
+
+int sp_samphold_compute(sp_data *sp, sp_samphold *p, SPFLOAT *trig, SPFLOAT *in, SPFLOAT *out)
+{
+    if(*trig != 0) {
+        p->val = *in;
+    }
+    *out = p->val;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/saturator.c
@@ -1,0 +1,109 @@
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+#ifndef M_PI
+#define M_PI    3.14159265358979323846
+#endif
+
+static void bilinear_transform(SPFLOAT acoefs[], SPFLOAT dcoefs[], SPFLOAT fs)
+{
+    SPFLOAT b0, b1, b2, a0, a1, a2;
+    SPFLOAT bz0, bz1, bz2, az0, az1, az2;
+
+    b0 = acoefs[0]; b1 = acoefs[1]; b2 = acoefs[2];
+    a0 = acoefs[3]; a1 = acoefs[4]; a2 = acoefs[5];
+
+    bz0 = 1.0; bz1 = 0.0; bz2 = 0.0;
+    az0 = 1.0; az1 = 0.0; az2 = 0.0;
+
+    az0 = a2*4*fs*fs + a1*2*fs + a0;
+
+    bz2 = (b2*4*fs*fs - b1*2*fs + b0) / az0;
+    bz1 = (-b2*8*fs*fs + 2*b0) / az0;
+    bz0 = (b2*4*fs*fs+ b1*2*fs + b0) / az0;
+    az2 = (a2*4*fs*fs - a1*2*fs + a0) / az0;
+    az1 = (-a2*8*fs*fs + 2*a0) / az0;
+
+    dcoefs[0] = bz0; dcoefs[1] = bz1; dcoefs[2] = bz2;
+    dcoefs[3] = az1; dcoefs[4] = az2;
+}
+
+int sp_saturator_create(sp_saturator **p)
+{
+    *p = malloc(sizeof(sp_saturator));
+    return SP_OK;
+}
+
+int sp_saturator_destroy(sp_saturator **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_saturator_init(sp_data *sp, sp_saturator *p)
+{
+    int i, j;
+    const SPFLOAT aacoefs[6][7] =
+    {
+        {2.60687e-05, 2.98697e-05, 2.60687e-05, -1.31885, 0.437162, 0.0, 0.0},
+        {1, -0.800256, 1, -1.38301, 0.496576, 0.0, 0.0},
+        {1, -1.42083, 1, -1.48787, 0.594413, 0.0, 0.0},
+        {1, -1.6374, 1, -1.60688, 0.707142, 0.0, 0.0},
+        {1, -1.7261, 1, -1.7253, 0.822156, 0.0, 0.0},
+        {1, -1.75999, 1, -1.84111, 0.938811, 0.0, 0.0}
+    };
+
+    SPFLOAT wc_dc = 5*2*M_PI;
+    SPFLOAT scoeffs[6] = {  0, 1, 0, wc_dc, 1, 0 };
+    SPFLOAT zcoeffs[5];
+    p->drive = 1;
+    p->dcoffset = 0;
+
+    for(i = 0; i < 6; i++){
+        for(j = 0; j < 7; j++){
+            p->aa[i][j] =  aacoefs[i][j];
+            p->ai[i][j] =  aacoefs[i][j];
+        }
+    }
+    bilinear_transform(scoeffs, zcoeffs, sp->sr*8);
+    for(i = 0; i < 2; i++){
+        for(j = 0; j < 5; j++)
+            p->dcblocker[i][j] = zcoeffs[j];
+        p->dcblocker[i][5] = 0.0;
+        p->dcblocker[i][6] = 0.0;
+    }
+        return SP_OK;
+}
+
+static int quad_compute(SPFLOAT p[7],  SPFLOAT *input, SPFLOAT* output)
+{
+    SPFLOAT in = *input;
+    *output = p[5] + in * p[0];
+    p[5] = p[6] + in * p[1] - *output*p[3];
+    p[6] = in * p[2] - *output*p[4];
+    return SP_OK;
+}
+
+
+int sp_saturator_compute(sp_data *sp, sp_saturator *p, SPFLOAT *in, SPFLOAT *out)
+{
+    int i, j;
+    SPFLOAT fsignal, usignal, dsignal;
+
+    fsignal = p->drive * *in;
+    for(i = 0; i < 8; i++){
+        usignal = (i == 0) ? 8 *fsignal : 0.0;
+        for(j = 0; j < 6; j++)
+            quad_compute(p->ai[j], &usignal, &usignal);
+
+        dsignal = (usignal + p->dcoffset) / (1.0 + fabs(usignal + p->dcoffset));
+
+        quad_compute(p->dcblocker[0], &dsignal, &dsignal);
+        quad_compute(p->dcblocker[1], &dsignal, &dsignal);
+
+        for(j = 0; j < 6; j++)
+            quad_compute(p->aa[j], &dsignal, out);
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/scale.c
@@ -1,0 +1,29 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_scale_create(sp_scale **p)
+{
+    *p = malloc(sizeof(sp_scale));
+    return SP_OK;
+}
+
+int sp_scale_destroy(sp_scale **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_scale_init(sp_data *sp, sp_scale *p)
+{
+    p->min = -1;
+    p->max = 1;
+    return SP_OK;
+}
+
+int sp_scale_compute(sp_data *sp, sp_scale *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out =  *in * (p->max - p->min) + p->min;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/scrambler.c
@@ -1,0 +1,65 @@
+/* This code is placed in the public domain. */
+
+#include <math.h>
+#include "soundpipe.h"
+#include "kiss_fftr.h"
+
+#ifndef M_PI
+#define M_PI		3.14159265358979323846
+#endif
+
+int sp_gen_scrambler(sp_data *sp, sp_ftbl *src, sp_ftbl **dest)
+{
+
+    uint32_t size;
+    sp_ftbl *dst;
+    kiss_fftr_cfg fft, ifft;
+    kiss_fft_cpx *tmp;
+    uint32_t i;
+    SPFLOAT mag, phs;
+    SPFLOAT max;
+    SPFLOAT val;
+
+    size = (src->size % 2 == 0) ? src->size : src->size - 1;
+    sp_ftbl_create(sp, &dst, size);
+
+    /* set up kissfft */
+    fft = kiss_fftr_alloc(size, 0, NULL, NULL);
+    ifft = kiss_fftr_alloc(size, 1, NULL, NULL);
+    tmp = malloc(sizeof(kiss_fft_cpx) * size);
+    memset(tmp, 0, sizeof(SPFLOAT) * size);
+    kiss_fftr(fft, src->tbl, tmp);
+
+    for(i = 0; i < size / 2; i++) {
+        mag = sqrt(tmp[i].r * tmp[i].r + tmp[i].i * tmp[i].i) / size;
+        phs = ((SPFLOAT)sp_rand(sp) / SP_RANDMAX) * 2 * M_PI;
+        tmp[i].r = mag * cos(phs);
+        tmp[i].i = mag * sin(phs);
+    }
+
+    tmp[0].r = 0;
+    tmp[0].i = 0;
+    tmp[size / 2 - 1].r = 0;
+    tmp[size / 2 - 1].i = 0;
+
+    kiss_fftri(ifft, tmp, dst->tbl);
+    max = -1;
+    val = 0;
+    for(i = 0; i < size; i++) {
+        val = fabs(dst->tbl[i]);
+        if(val > max) {
+            max = val;
+        }
+    }
+
+    for(i = 0; i < size; i++) {
+       dst->tbl[i] /= max;
+    }
+
+    kiss_fftr_free(fft);
+    kiss_fftr_free(ifft);
+    KISS_FFT_FREE(tmp);
+
+    *dest = dst;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/sdelay.c
@@ -1,0 +1,43 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_sdelay_create(sp_sdelay **p)
+{
+    sp_sdelay *pp;
+    *p = malloc(sizeof(sp_sdelay));
+    pp = *p;
+    pp->size = 0;
+    return SP_OK;
+}
+
+int sp_sdelay_destroy(sp_sdelay **p)
+{
+    sp_sdelay *pp = *p;
+
+    if(pp->size > 0) {
+        free(pp->buf);
+    }
+
+    free(*p);
+    return SP_OK;
+}
+
+int sp_sdelay_init(sp_data *sp, sp_sdelay *p, int size)
+{
+    int n;
+    p->size = size;
+    p->buf = malloc(size * sizeof(SPFLOAT));
+    for(n = 0; n < p->size; n++) p->buf[n] = 0;
+    p->pos = 0;
+    return SP_OK;
+}
+
+int sp_sdelay_compute(sp_data *sp, sp_sdelay *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = p->buf[p->pos];
+    p->buf[p->pos] = *in;
+    p->pos = (p->pos + 1) % p->size;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/slice.c
@@ -1,0 +1,48 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_slice_create(sp_slice **p)
+{
+    *p = malloc(sizeof(sp_slice));
+    return SP_OK;
+}
+
+int sp_slice_destroy(sp_slice **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_slice_init(sp_data *sp, sp_slice *p, sp_ftbl *vals, sp_ftbl *buf)
+{
+    p->vals = vals;
+    p->buf = buf;
+    p->pos = 0;
+    p->nextpos = 0;
+    p->id = 0;
+    return SP_OK;
+}
+
+int sp_slice_compute(sp_data *sp, sp_slice *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = 0;
+    if(*in != 0) {
+        if(p->id < p->vals->size) {
+            p->pos = p->vals->tbl[p->id];
+            if(p->id == p->vals->size - 1) {
+                p->nextpos = p->buf->size;
+            } else {
+                p->nextpos = p->vals->tbl[p->id + 1];
+            }
+        }
+    }
+
+    if(p->pos < p->nextpos) {
+        *out = p->buf->tbl[p->pos];
+        p->pos++;
+    }
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/smoothdelay.c
@@ -1,0 +1,122 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+#ifndef max
+#define max(a, b) ((a > b) ? a : b)
+#endif
+
+#ifndef min
+#define min(a, b) ((a < b) ? a : b)
+#endif
+
+
+int sp_smoothdelay_create(sp_smoothdelay **p)
+{
+    *p = malloc(sizeof(sp_smoothdelay));
+    return SP_OK;
+}
+
+int sp_smoothdelay_destroy(sp_smoothdelay **p)
+{
+    sp_smoothdelay *pp = *p;
+    sp_auxdata_free(&pp->buf1);
+    sp_auxdata_free(&pp->buf2);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_smoothdelay_init(sp_data *sp, sp_smoothdelay *p,
+        SPFLOAT maxdel, uint32_t interp)
+{
+    uint32_t n = (int32_t)(maxdel * sp->sr)+1;
+    p->sr = sp->sr;
+    p->del = maxdel * 0.5;
+    p->pdel = -1;
+    p->maxdel = maxdel;
+    p->feedback = 0;
+    p->maxbuf = n - 1;
+    p->maxcount = interp;
+
+    sp_auxdata_alloc(&p->buf1, n * sizeof(SPFLOAT));
+    p->bufpos1 = 0;
+    p->deltime1 = (uint32_t) (p->del * sp->sr);
+
+    sp_auxdata_alloc(&p->buf2, n * sizeof(SPFLOAT));
+    p->bufpos2 = 0;
+    p->deltime2 = p->deltime1;
+
+    p->counter = 0;
+    p->curbuf = 0;
+    return SP_OK;
+}
+
+static SPFLOAT delay_sig(SPFLOAT *buf,
+        uint32_t *bufpos,
+        uint32_t deltime,
+        SPFLOAT fdbk,
+        SPFLOAT in)
+{
+    SPFLOAT delay = buf[*bufpos];
+    SPFLOAT sig = (delay * fdbk) + in;
+    buf[*bufpos] = sig;
+    *bufpos = (*bufpos + 1) % deltime;
+    return delay;
+}
+
+int sp_smoothdelay_compute(sp_data *sp, sp_smoothdelay *p, SPFLOAT *in, SPFLOAT *out)
+{
+    SPFLOAT *buf1;
+    SPFLOAT *buf2;
+    SPFLOAT it;
+    SPFLOAT del1;
+    SPFLOAT del2;
+
+    *out = 0;
+    if (p->del != p->pdel && p->counter == 0) {
+        uint32_t dels = min((uint32_t)(p->del * sp->sr), p->maxbuf);
+
+        /* initial delay time sets time for both buffers */
+
+        if(p->pdel < 0) {
+            p->deltime1 = dels;
+            p->deltime2 = dels;
+        }
+
+        p->pdel = p->del;
+
+        if(dels == 0) dels = 1;
+
+        if(p->curbuf == 0) {
+            p->curbuf = 1;
+            p->deltime2 = dels;
+        } else {
+            p->curbuf = 0;
+            p->deltime1 = dels;
+        }
+        p->counter = p->maxcount;
+    }
+
+
+
+    buf1 = (SPFLOAT *)p->buf1.ptr;
+    buf2 = (SPFLOAT *)p->buf2.ptr;
+    it = (SPFLOAT)p->counter / p->maxcount;
+    if (p->counter != 0) p->counter--;
+
+    del1 = delay_sig(buf1, &p->bufpos1,
+            p->deltime1, p->feedback, *in);
+
+    del2 = delay_sig(buf2, &p->bufpos2,
+            p->deltime2, p->feedback, *in);
+
+    if (p->curbuf == 0) {
+        /* 1 to 2 */
+        *out = (del1 * it) + (del2 * (1 - it));
+    } else {
+        /* 2 to 1 */
+        *out = (del2 * it) + (del1 * (1 - it));
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/smoother.c
@@ -1,0 +1,66 @@
+/*
+ * Smoother
+ *
+ * Smoother is a one-pole smoothing filter, typically used on
+ * control signals to create a "smootheramenteau" effect.
+ *
+ * This filter design uses the difference equation:
+ *
+ * y(n) = b0*x(n) - a1*y(n - 1)
+ *
+ * Where a1 is (0.5^(1/(t * sr))), and b0 is (1 - a1).
+ *
+ * More information on one-pole smoothers can be found here:
+ * https://ccrma.stanford.edu/~jos/filters/One_Pole.html
+ */
+
+
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "soundpipe.h"
+
+int sp_smoother_create(sp_smoother **p)
+{
+    *p = malloc(sizeof(sp_smoother));
+    return SP_OK;
+}
+
+int sp_smoother_destroy(sp_smoother **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_smoother_init(sp_data *sp, sp_smoother *p)
+{
+    p->y0 = 0;
+    p->b0 = 0;
+    p->a1 = 0;
+    p->psmooth = -100.0;
+    p->smooth = 0.01;
+
+    /* using this constant shaves off a multiply operation */
+    p->onedsr = 1.0/sp->sr;
+    return SP_OK;
+}
+
+int sp_smoother_compute(sp_data *sp, sp_smoother *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if (p->psmooth != p->smooth) {
+        p->a1 = pow(0.5, p->onedsr/p->smooth);
+        p->b0 = 1.0 - p->a1;
+        p->psmooth = p->smooth;
+    }
+
+    p->y0 = p->b0 * (*in) + p->a1 * p->y0;
+    *out = p->y0;
+    return SP_OK;
+}
+
+int sp_smoother_reset(sp_data *sp, sp_smoother *p, SPFLOAT *in)
+{
+    p->y0 = *in;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/spa.c
@@ -1,0 +1,57 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+#define SPA_BUFSIZE 4096
+
+int sp_spa_create(sp_spa **p)
+{
+    *p = malloc(sizeof(sp_spa));
+    return SP_OK;
+}
+
+int sp_spa_destroy(sp_spa **p)
+{
+    sp_spa *pp = *p;
+    sp_auxdata_free(&pp->aux);
+    spa_close(&pp->spa);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_spa_init(sp_data *sp, sp_spa *p, const char *filename)
+{
+    if(spa_open(sp, &p->spa, filename, SPA_READ) != SP_OK) {
+        return SP_NOT_OK;
+    }
+
+    p->pos = 0;
+
+    p->bufsize = SPA_BUFSIZE;
+    sp_auxdata_alloc(&p->aux, sizeof(SPFLOAT) * p->bufsize);
+
+    p->buf = p->aux.ptr;
+
+    return SP_OK;
+}
+
+int sp_spa_compute(sp_data *sp, sp_spa *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if(p->bufsize == 0) {
+        *out = 0.0;
+        return SP_OK;
+    }
+
+    if(p->pos == 0) {
+        p->bufsize = spa_read_buf(sp, &p->spa, p->buf, SPA_BUFSIZE);
+        if(p->bufsize == 0) {
+            *out = 0.0;
+            return SP_OK;
+        }
+    }
+
+    *out = p->buf[p->pos];
+    p->pos = (p->pos + 1) % p->bufsize;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/sparec.c
@@ -1,0 +1,59 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+#define SPA_BUFSIZE 4096
+
+int sp_sparec_create(sp_sparec **p)
+{
+    *p = malloc(sizeof(sp_sparec));
+    return SP_OK;
+}
+
+int sp_sparec_destroy(sp_sparec **p)
+{
+    sp_sparec *pp = *p;
+    sp_auxdata_free(&pp->aux);
+    spa_close(&pp->spa);
+    free(*p);
+    return SP_OK;
+}
+
+int sp_sparec_init(sp_data *sp, sp_sparec *p, const char *filename)
+{
+    if(spa_open(sp, &p->spa, filename, SPA_WRITE) != SP_OK) {
+        return SP_NOT_OK;
+    }
+
+    p->pos = SPA_BUFSIZE;
+
+    p->bufsize = SPA_BUFSIZE;
+    sp_auxdata_alloc(&p->aux, sizeof(SPFLOAT) * p->bufsize);
+
+    p->buf = p->aux.ptr;
+    return SP_OK;
+}
+
+int sp_sparec_compute(sp_data *sp, sp_sparec *p, SPFLOAT *in, SPFLOAT *out)
+{
+    if(p->pos == 0) {
+        p->pos = p->bufsize;
+        spa_write_buf(sp, &p->spa, p->buf, p->bufsize);
+    }
+
+    p->buf[p->bufsize - p->pos] = *in;
+
+    p->pos--;
+    *out = *in;
+    return SP_OK;
+}
+
+/* call this to close sparec. will write the rest of the buffer */
+int sp_sparec_close(sp_data *sp, sp_sparec *p)
+{
+    if(p->pos < p->bufsize - 1) {
+        spa_write_buf(sp, &p->spa, p->buf, p->bufsize - p->pos);
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/switch.c
@@ -1,0 +1,38 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_switch_create(sp_switch **p)
+{
+    *p = malloc(sizeof(sp_switch));
+    return SP_OK;
+}
+
+int sp_switch_destroy(sp_switch **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_switch_init(sp_data *sp, sp_switch *p)
+{
+    p->mode = 0;
+    return SP_OK;
+}
+
+int sp_switch_compute(sp_data *sp, sp_switch *p, SPFLOAT *trig,
+    SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out)
+{
+    if (*trig) {
+        p->mode = p->mode == 0 ? 1 : 0;
+    }
+
+    if(p->mode == 0) {
+        *out = *in1;
+    } else {
+        *out = *in2;
+    }
+
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/tadsr.c
@@ -1,0 +1,152 @@
+/*
+ * TADSR
+ *
+ * This module uses modified code from Perry Cook's ADSR STK module.
+ *
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "soundpipe.h"
+
+enum{ ATTACK, DECAY, SUSTAIN, RELEASE, CLEAR, KEY_ON, KEY_OFF };
+
+static void make_Envelope(sp_tadsr *e)
+{
+    e->target = 0.0;
+    e->value = 0.0;
+    e->rate = 0.001;
+    e->state = 1;
+}
+
+static void make_ADSR(sp_tadsr *a)
+{
+    make_Envelope(a);
+    a->target = 0.0;
+    a->value = 0.0;
+    a->attackRate = 0.001;
+    a->decayRate = 0.001;
+    a->sustainLevel = 0.0;
+    a->releaseRate = 0.01;
+    a->state = CLEAR;
+}
+
+static void ADSR_keyOn(sp_tadsr *a)
+{
+    a->target = 1.0;
+    a->rate = a->attackRate;
+    a->state = ATTACK;
+}
+
+static void ADSR_keyOff(sp_tadsr *a)
+{
+    a->target = 0.0;
+    a->rate = a->releaseRate;
+    a->state = RELEASE;
+}
+
+static void ADSR_setSustainLevel(sp_data *sp, sp_tadsr *a, SPFLOAT aLevel)
+{
+   a->sustainLevel = aLevel;
+}
+
+static void ADSR_setAttackTime(sp_data *sp, sp_tadsr *a, SPFLOAT aTime)
+{
+    a->attackRate = 1.0 / (aTime * sp->sr);
+}
+
+static void ADSR_setDecayTime(sp_data *sp, sp_tadsr *a, SPFLOAT aTime)
+{
+    a->decayRate = 1.0 / (aTime * sp->sr);
+}
+
+static void ADSR_setReleaseTime(sp_data *sp, sp_tadsr *a, SPFLOAT aTime)
+{
+    a->releaseRate = 1.0 / (aTime * sp->sr);
+}
+
+static void ADSR_setAllTimes(sp_data *sp, sp_tadsr *a, SPFLOAT attTime, SPFLOAT decTime,
+                      SPFLOAT susLevel, SPFLOAT relTime)
+{
+    ADSR_setAttackTime(sp, a, attTime);
+    ADSR_setDecayTime(sp, a, decTime);
+    ADSR_setSustainLevel(sp, a, susLevel);
+    ADSR_setReleaseTime(sp, a, relTime);
+}
+
+static SPFLOAT ADSR_tick(sp_tadsr *a)
+{
+    SPFLOAT out = 0;
+    if (a->state == ATTACK) {
+        a->value += a->rate;
+        if (a->value >= a->target) {
+            a->value = a->target;
+            a->rate = a->decayRate;
+            a->target = a->sustainLevel;
+            a->state = DECAY;
+        }
+        out = a->value;
+    } else if (a->state == DECAY) {
+        a->value -= a->decayRate;
+        out = a->value;
+        if (a->value <= a->sustainLevel) {
+            a->value = a->sustainLevel;
+            out = a->sustainLevel;
+            a->rate = 0.0;
+            a->state = SUSTAIN;
+        }
+    } else if (a->state == RELEASE)  {
+        a->value -= a->releaseRate;
+        if (a->value <= 0.0) {
+            a->value = 0.0;
+            a->state = CLEAR;
+        }
+        out = a->value;
+    } else if (a->state == SUSTAIN)  {
+        out = a->sustainLevel;
+    }
+    return out;
+}
+
+int sp_tadsr_create(sp_tadsr **p)
+{
+    *p = malloc(sizeof(sp_tadsr));
+    return SP_OK;
+}
+
+int sp_tadsr_destroy(sp_tadsr **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_tadsr_init(sp_data *sp, sp_tadsr *p)
+{
+    make_ADSR(p);
+    p->atk = 0.5;
+    p->dec = 0.5;
+    p->sus = 0.0;
+    p->rel = 0.5;
+    p->mode = KEY_OFF;
+    return SP_OK;
+}
+
+int sp_tadsr_compute(sp_data *sp, sp_tadsr *p, SPFLOAT *trig, SPFLOAT *out)
+{
+    if(*trig != 0) {
+
+        if(*trig == 2) {
+            ADSR_keyOff(p);
+            p->mode = KEY_OFF;
+        }else if(p->mode == KEY_OFF) {
+            ADSR_setAllTimes(sp, p, p->atk, p->dec, p->sus, p->rel);
+            ADSR_keyOn(p);
+            p->mode = KEY_ON;
+        } else {
+            ADSR_keyOff(p);
+            p->mode = KEY_OFF;
+        }
+    }
+    *out = ADSR_tick(p);
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/talkbox.c
@@ -1,0 +1,206 @@
+/*
+ * Talkbox
+ *
+ * This module is ported from the mdaTalkbox plugin by Paul Kellet
+ * (maxim digital audio).
+ *
+ * More information on his plugins and the original code can be found here:
+ *
+ * http://mda.smartelectronix.com/
+ *
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "soundpipe.h"
+
+#ifndef TWO_PI
+#define TWO_PI 6.28318530717958647692528676655901
+#endif
+
+#define ORD_MAX 50
+
+static void lpc_durbin(SPFLOAT *r, int p, float *k, float *g)
+{
+  int i, j;
+  SPFLOAT a[ORD_MAX], at[ORD_MAX], e=r[0];
+
+  for(i=0; i<=p; i++) a[i] = at[i] = 0.0f;
+
+  for(i=1; i<=p; i++)
+  {
+    k[i] = -r[i];
+
+    for(j=1; j<i; j++)
+    {
+      at[j] = a[j];
+      k[i] -= a[j] * r[i-j];
+    }
+    if(fabs(e) < 1.0e-20f) { e = 0.0f;  break; }
+    k[i] /= e;
+
+    a[i] = k[i];
+    for(j=1; j<i; j++) a[j] = at[j] + k[i] * at[i-j];
+
+    e *= 1.0f - k[i] * k[i];
+  }
+
+  if(e < 1.0e-20f) e = 0.0f;
+  *g = (float)sqrt(e);
+}
+
+static void lpc(float *buf, float *car, uint32_t n, uint32_t o)
+{
+    SPFLOAT z[ORD_MAX], r[ORD_MAX], k[ORD_MAX], G, x;
+    uint32_t i, j, nn=n;
+    SPFLOAT min;
+
+    /* buf[] is already emphasized and windowed */
+    for(j=0; j<=o; j++, nn--) {
+        z[j] = r[j] = 0.0f;
+        for(i=0; i<nn; i++) r[j] += buf[i] * buf[i+j]; /* autocorrelation */
+    }
+
+    r[0] *= 1.001f;  /* stability fix */
+
+    min = 0.00001f;
+    if(r[0] < min) { for(i=0; i<n; i++) buf[i] = 0.0f; return; }
+
+    lpc_durbin(r, o, k, &G);  /* calc reflection coeffs */
+
+    for(i=1; i<=o; i++) {
+        if(k[i] > 0.995f) k[i] = 0.995f; else if(k[i] < -0.995f) k[i] = -.995f;
+    }
+
+    for(i=0; i<n; i++) {
+        x = G * car[i];
+        /* lattice filter */
+        for(j=o; j>0; j--) {
+            x -= k[j] * z[j-1];
+            z[j] = z[j-1] + k[j] * x;
+        }
+        buf[i] = z[0] = x;  /* output buf[] will be windowed elsewhere */
+    }
+}
+
+int sp_talkbox_create(sp_talkbox **p)
+{
+    *p = malloc(sizeof(sp_talkbox));
+    return SP_OK;
+}
+
+int sp_talkbox_destroy(sp_talkbox **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_talkbox_init(sp_data *sp, sp_talkbox *p)
+{
+    uint32_t n;
+    p->quality = 1.f;
+    p->N = 1;
+    p->K = 0;
+
+    n = (uint32_t)(0.01633f * sp->sr);
+    if(n > SP_TALKBOX_BUFMAX) n = SP_TALKBOX_BUFMAX;
+
+    /* calculate hanning window */
+    if(n != p->N) {
+        SPFLOAT dp;
+        SPFLOAT pos;
+        p->N = n;
+        dp = TWO_PI / (SPFLOAT)p->N;
+        pos = 0.0f;
+        for(n=0; n < p->N; n++) {
+            p->window[n] = 0.5f - 0.5f * (SPFLOAT)cos(pos);
+            pos += dp;
+        }
+    }
+
+    /* zero out variables and buffers */
+    p->pos = p->K = 0;
+    p->emphasis = 0.0f;
+    p->FX = 0;
+
+    p->u0 = p->u1 = p->u2 = p->u3 = p->u4 = 0.0f;
+    p->d0 = p->d1 = p->d2 = p->d3 = p->d4 = 0.0f;
+
+    memset(p->buf0, 0, SP_TALKBOX_BUFMAX * sizeof(SPFLOAT));
+    memset(p->buf1, 0, SP_TALKBOX_BUFMAX * sizeof(SPFLOAT));
+    memset(p->car0, 0, SP_TALKBOX_BUFMAX * sizeof(SPFLOAT));
+    memset(p->car1, 0, SP_TALKBOX_BUFMAX * sizeof(SPFLOAT));
+    return SP_OK;
+}
+
+int sp_talkbox_compute(sp_data *sp, sp_talkbox *t, SPFLOAT *src, SPFLOAT *exc, SPFLOAT *out)
+{
+    int32_t p0=t->pos, p1 = (t->pos + t->N/2) % t->N;
+    SPFLOAT e=t->emphasis, w, o, x, fx=t->FX;
+    SPFLOAT p, q, h0=0.3f, h1=0.77f;
+    SPFLOAT den;
+
+    t->O = (uint32_t)((0.0001f + 0.0004f * t->quality) * sp->sr);
+
+    o = *src;
+    x = *exc;
+
+    p = t->d0 + h0 * x;
+    t->d0 = t->d1;
+    t->d1 = x - h0 * p;
+
+    q = t->d2 + h1 * t->d4;
+    t->d2 = t->d3;
+    t->d3 = t->d4 - h1 * q;
+
+    t->d4 = x;
+
+    x = p + q;
+
+    if(t->K++) {
+        t->K = 0;
+
+        /* carrier input */
+        t->car0[p0] = t->car1[p1] = x;
+
+        /* 6dB/oct pre-emphasis */
+        x = o - e;  e = o;
+
+        /* 50% overlapping hanning windows */
+        w = t->window[p0]; fx = t->buf0[p0] * w;  t->buf0[p0] = x * w;
+        if(++p0 >= t->N) { lpc(t->buf0, t->car0, t->N, t->O);  p0 = 0; }
+
+        w = 1.0f - w;  fx += t->buf1[p1] * w;  t->buf1[p1] = x * w;
+        if(++p1 >= t->N) { lpc(t->buf1, t->car1, t->N, t->O);  p1 = 0; }
+    }
+
+    p = t->u0 + h0 * fx;
+    t->u0 = t->u1;
+    t->u1 = fx - h0 * p;
+
+    q = t->u2 + h1 * t->u4;
+    t->u2 = t->u3;
+    t->u3 = t->u4 - h1 * q;
+
+    t->u4 = fx;
+    x = p + q;
+
+    o = x * 0.5;
+    *out = o;
+    t->emphasis = e;
+    t->pos = p0;
+    t->FX = fx;
+
+    den = 1.0e-10f;
+    /* anti-denormal */
+    if(fabs(t->d0) < den) t->d0 = 0.0f;
+    if(fabs(t->d1) < den) t->d1 = 0.0f;
+    if(fabs(t->d2) < den) t->d2 = 0.0f;
+    if(fabs(t->d3) < den) t->d3 = 0.0f;
+    if(fabs(t->u0) < den) t->u0 = 0.0f;
+    if(fabs(t->u1) < den) t->u1 = 0.0f;
+    if(fabs(t->u2) < den) t->u2 = 0.0f;
+    if(fabs(t->u3) < den) t->u3 = 0.0f;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/tblrec.c
@@ -1,0 +1,46 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include <string.h>
+#include "soundpipe.h"
+
+int sp_tblrec_create(sp_tblrec **p)
+{
+    *p = malloc(sizeof(sp_tblrec));
+    return SP_OK;
+}
+
+int sp_tblrec_destroy(sp_tblrec **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_tblrec_init(sp_data *sp, sp_tblrec *p, sp_ftbl *ft)
+{
+    p->index = 0;
+    p->record = 0;
+    p->ft = ft;
+    return SP_OK;
+}
+
+int sp_tblrec_compute(sp_data *sp, sp_tblrec *p, SPFLOAT *in, SPFLOAT *trig, SPFLOAT *out)
+{
+    if(*trig != 0) {
+        if(p->record == 1) {
+            p->record = 0;
+        } else {
+            p->record = 1;
+            p->index = 0;
+            memset(p->ft->tbl, 0, sizeof(SPFLOAT) * p->ft->size);
+        }
+    }
+
+    if(p->record) {
+        p->ft->tbl[p->index] = *in;
+        p->index = (p->index + 1) % p->ft->size;
+    }
+
+    *out = *in;
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/tdiv.c
@@ -1,0 +1,35 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_tdiv_create(sp_tdiv **p)
+{
+    *p = malloc(sizeof(sp_tdiv));
+    return SP_OK;
+}
+
+int sp_tdiv_destroy(sp_tdiv **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+int sp_tdiv_init(sp_data *sp, sp_tdiv *p)
+{
+    p->num = 2;
+    p->counter = 0;
+    p->offset = 0;
+    return SP_OK;
+}
+
+int sp_tdiv_compute(sp_data *sp, sp_tdiv *p, SPFLOAT *in, SPFLOAT *out)
+{
+    *out = 0.0;
+    if(*in != 0) {
+        if(p->counter == p->offset) *out = 1.0;
+        else *out = 0.0;
+        p->counter = (p->counter + 1) % p->num;
+    }
+    return SP_OK;
+}
--- /dev/null
+++ b/modules/tenv.c
@@ -1,0 +1,92 @@
+/* This code is placed in the public domain. */
+
+#include <stdlib.h>
+#include "soundpipe.h"
+
+int sp_tenv_create(sp_tenv **p)
+{
+    *p = malloc(sizeof(sp_tenv));
+    return SP_OK;
+}
+
+int sp_tenv_destroy(sp_tenv **p)
+{
+    free(*p);
+    return SP_OK;
+}
+
+static void sp_tenv_reinit(void *ud)
+{
+    sp_tenv *env = ud;
+    env->pos = 0;
+    env->atk_end = env->sr * env->atk;
+    env->rel_start = env->sr * (env->atk + env->hold);
+    env->atk_slp = 1.0 / env->atk_end;
+    env->rel_slp = -1.0 / (env->sr * env->rel);
+    env->totaldur = env->sr * (env->atk + env->hold + env->rel);
+}
+
+static void sp_tenv_comp(void *ud, SPFLOAT *out)
+{
+    sp_tenv *env = ud;
+    SPFLOAT sig = 0;
+    uint32_t pos = env->pos;
+    *out = 0.0;
+
+    if (pos < env->atk_end) {
+        sig = env->last + env->atk_slp;
+    } else if (pos < env->rel_start) {
+        sig = 1.0;
+    } else if (pos < env->totaldur) {
+        sig = env->last + env->rel_slp;
+    } else{
+        sig = 0.0;
+    }
+</