/**************************************************************************
 * Copyright 2009-2015 Olivier Belanger                                   *
 *                                                                        *
 * This file is part of pyo, a python module to help digital signal       *
 * processing script creation.                                            *
 *                                                                        *
 * pyo is free software: you can redistribute it and/or modify            *
 * it under the terms of the GNU Lesser General Public License as         *
 * published by the Free Software Foundation, either version 3 of the     *
 * License, or (at your option) any later version.                        *
 *                                                                        *
 * pyo is distributed in the hope that it will be useful,                 *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 * GNU Lesser General Public License for more details.                    *
 *                                                                        *
 * You should have received a copy of the GNU Lesser General Public       *
 * License along with pyo.  If not, see <http://www.gnu.org/licenses/>.   *
 *************************************************************************/

#include <Python.h>
#include "structmember.h"
#include <math.h>
#include "pyomodule.h"
#include "streammodule.h"
#include "servermodule.h"
#include "dummymodule.h"

#define NUM_REFS  13

static const MYFLT randomScaling = 0.5;
static const MYFLT reverbParams[8][4] =
{
    { 2473.0, 0.0010, 3.100, 2503.0 },
    { 2767.0, 0.0011, 3.500, 2749.0 },
    { 3217.0, 0.0017, 1.110, 3187.0 },
    { 3557.0, 0.0006, 3.973, 3583.0 },
    { 3907.0, 0.0010, 2.341, 3929.0 },
    { 4127.0, 0.0011, 1.897, 4093.0 },
    { 2143.0, 0.0017, 0.891, 2131.0 },
    { 1933.0, 0.0006, 3.221, 1951.0 }
};

static const MYFLT first_ref_delays[NUM_REFS] = {283, 467, 587, 677, 757, 911, 1117, 1223, 1307, 1429, 1553, 1613, 1783};

typedef struct
{
    pyo_audio_HEAD
    PyObject *input;
    Stream *input_stream;
    PyObject *feedback;
    Stream *feedback_stream;
    PyObject *cutoff;
    Stream *cutoff_stream;
    PyObject *mix;
    Stream *mix_stream;
    void (*mix_func_ptr)();
    int modebuffer[5];
    MYFLT total_signal;
    MYFLT delays[8];
    long size[8];
    int in_count[8];
    MYFLT *buffer[8];
    // lowpass
    MYFLT damp;
    MYFLT lastFreq;
    // sample memories
    MYFLT lastSamples[8];
    // jitters
    MYFLT rnd[8];
    MYFLT rnd_value[8];
    MYFLT rnd_oldValue[8];
    MYFLT rnd_diff[8];
    MYFLT rnd_time[8];
    MYFLT rnd_timeInc[8];
    MYFLT rnd_range[8];
    MYFLT rnd_halfRange[8];
} WGVerb;

static void
WGVerb_process_ii(WGVerb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt;
    int i, j, ind;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT feed = PyFloat_AS_DOUBLE(self->feedback);
    MYFLT freq = PyFloat_AS_DOUBLE(self->cutoff);

    if (feed < 0)
        feed = 0;
    else if (feed > 1)
        feed = 1;

    if (freq != self->lastFreq)
    {
        self->lastFreq = freq;
        self->damp = 2.0 - MYCOS(TWOPI * freq / self->sr);
        self->damp = (self->damp - MYSQRT(self->damp * self->damp - 1.0));
    }

    for (i = 0; i < self->bufsize; i++)
    {
        inval = in[i];
        junction = self->total_signal * .25;
        self->total_signal = 0.0;

        for (j = 0; j < 8; j++)
        {
            self->rnd_time[j] += self->rnd_timeInc[j];

            if (self->rnd_time[j] < 0.0)
                self->rnd_time[j] += 1.0;
            else if (self->rnd_time[j] >= 1.0)
            {
                self->rnd_time[j] -= 1.0;
                self->rnd_oldValue[j] = self->rnd_value[j];
                self->rnd_value[j] = self->rnd_range[j] * RANDOM_UNIFORM - self->rnd_halfRange[j];
                self->rnd_diff[j] = self->rnd_value[j] - self->rnd_oldValue[j];
            }

            self->rnd[j] = self->rnd_oldValue[j] + self->rnd_diff[j] * self->rnd_time[j];

            xind = self->in_count[j] - (self->delays[j] + self->rnd[j]);

            if (xind < 0)
                xind += self->size[j];

            ind = (int)xind;
            frac = xind - ind;
            x = self->buffer[j][ind];
            x1 = self->buffer[j][ind + 1];
            val = x + (x1 - x) * frac;
            val *= feed;
            filt = (self->lastSamples[j] - val) * self->damp + val;
            self->total_signal += filt;

            self->buffer[j][self->in_count[j]] = inval + junction - self->lastSamples[j];
            self->lastSamples[j] = filt;

            if(self->in_count[j] == 0)
                self->buffer[j][self->size[j]] = self->buffer[j][0];

            self->in_count[j]++;

            if (self->in_count[j] >= self->size[j])
                self->in_count[j] = 0;
        }

        self->data[i] = self->total_signal * 0.25;
    }
}

static void
WGVerb_process_ai(WGVerb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt, feed;
    int i, j, ind;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT *feedback = Stream_getData((Stream *)self->feedback_stream);
    MYFLT freq = PyFloat_AS_DOUBLE(self->cutoff);

    if (freq != self->lastFreq)
    {
        self->lastFreq = freq;
        self->damp = 2.0 - MYCOS(TWOPI * freq / self->sr);
        self->damp = (self->damp - MYSQRT(self->damp * self->damp - 1.0));
    }

    for (i = 0; i < self->bufsize; i++)
    {
        inval = in[i];
        feed = feedback[i];

        if (feed < 0)
            feed = 0;
        else if (feed > 1)
            feed = 1;

        junction = self->total_signal * .25;
        self->total_signal = 0.0;

        for (j = 0; j < 8; j++)
        {
            self->rnd_time[j] += self->rnd_timeInc[j];

            if (self->rnd_time[j] < 0.0)
                self->rnd_time[j] += 1.0;
            else if (self->rnd_time[j] >= 1.0)
            {
                self->rnd_time[j] -= 1.0;
                self->rnd_oldValue[j] = self->rnd_value[j];
                self->rnd_value[j] = self->rnd_range[j] * RANDOM_UNIFORM - self->rnd_halfRange[j];
                self->rnd_diff[j] = self->rnd_value[j] - self->rnd_oldValue[j];
            }

            self->rnd[j] = self->rnd_oldValue[j] + self->rnd_diff[j] * self->rnd_time[j];

            xind = self->in_count[j] - (self->delays[j] + self->rnd[j]);

            if (xind < 0)
                xind += self->size[j];

            ind = (int)xind;
            frac = xind - ind;
            x = self->buffer[j][ind];
            x1 = self->buffer[j][ind + 1];
            val = x + (x1 - x) * frac;
            val *= feed;
            filt = (self->lastSamples[j] - val) * self->damp + val;
            self->total_signal += filt;

            self->buffer[j][self->in_count[j]] = inval + junction - self->lastSamples[j];
            self->lastSamples[j] = filt;

            if(self->in_count[j] == 0)
                self->buffer[j][self->size[j]] = self->buffer[j][0];

            self->in_count[j]++;

            if (self->in_count[j] >= self->size[j])
                self->in_count[j] = 0;
        }

        self->data[i] = self->total_signal * 0.25;
    }
}

static void
WGVerb_process_ia(WGVerb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt, freq;
    int i, j, ind;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT feed = PyFloat_AS_DOUBLE(self->feedback);
    MYFLT *cutoff = Stream_getData((Stream *)self->cutoff_stream);

    if (feed < 0)
        feed = 0;
    else if (feed > 1)
        feed = 1;

    for (i = 0; i < self->bufsize; i++)
    {
        inval = in[i];
        freq = cutoff[i];

        if (freq != self->lastFreq)
        {
            self->lastFreq = freq;
            self->damp = 2.0 - MYCOS(TWOPI * freq / self->sr);
            self->damp = (self->damp - MYSQRT(self->damp * self->damp - 1.0));
        }

        junction = self->total_signal * .25;
        self->total_signal = 0.0;

        for (j = 0; j < 8; j++)
        {
            self->rnd_time[j] += self->rnd_timeInc[j];

            if (self->rnd_time[j] < 0.0)
                self->rnd_time[j] += 1.0;
            else if (self->rnd_time[j] >= 1.0)
            {
                self->rnd_time[j] -= 1.0;
                self->rnd_oldValue[j] = self->rnd_value[j];
                self->rnd_value[j] = self->rnd_range[j] * RANDOM_UNIFORM - self->rnd_halfRange[j];
                self->rnd_diff[j] = self->rnd_value[j] - self->rnd_oldValue[j];
            }

            self->rnd[j] = self->rnd_oldValue[j] + self->rnd_diff[j] * self->rnd_time[j];

            xind = self->in_count[j] - (self->delays[j] + self->rnd[j]);

            if (xind < 0)
                xind += self->size[j];

            ind = (int)xind;
            frac = xind - ind;
            x = self->buffer[j][ind];
            x1 = self->buffer[j][ind + 1];
            val = x + (x1 - x) * frac;
            val *= feed;
            filt = (self->lastSamples[j] - val) * self->damp + val;
            self->total_signal += filt;

            self->buffer[j][self->in_count[j]] = inval + junction - self->lastSamples[j];
            self->lastSamples[j] = filt;

            if(self->in_count[j] == 0)
                self->buffer[j][self->size[j]] = self->buffer[j][0];

            self->in_count[j]++;

            if (self->in_count[j] >= self->size[j])
                self->in_count[j] = 0;
        }

        self->data[i] = self->total_signal * 0.25;
    }
}

static void
WGVerb_process_aa(WGVerb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt, feed, freq;
    int i, j, ind;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT *feedback = Stream_getData((Stream *)self->feedback_stream);
    MYFLT *cutoff = Stream_getData((Stream *)self->cutoff_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inval = in[i];
        feed = feedback[i];
        freq = cutoff[i];

        if (feed < 0)
            feed = 0;
        else if (feed > 1)
            feed = 1;

        if (freq != self->lastFreq)
        {
            self->lastFreq = freq;
            self->damp = 2.0 - MYCOS(TWOPI * freq / self->sr);
            self->damp = (self->damp - MYSQRT(self->damp * self->damp - 1.0));
        }

        junction = self->total_signal * .25;
        self->total_signal = 0.0;

        for (j = 0; j < 8; j++)
        {
            self->rnd_time[j] += self->rnd_timeInc[j];

            if (self->rnd_time[j] < 0.0)
                self->rnd_time[j] += 1.0;
            else if (self->rnd_time[j] >= 1.0)
            {
                self->rnd_time[j] -= 1.0;
                self->rnd_oldValue[j] = self->rnd_value[j];
                self->rnd_value[j] = self->rnd_range[j] * RANDOM_UNIFORM - self->rnd_halfRange[j];
                self->rnd_diff[j] = self->rnd_value[j] - self->rnd_oldValue[j];
            }

            self->rnd[j] = self->rnd_oldValue[j] + self->rnd_diff[j] * self->rnd_time[j];

            xind = self->in_count[j] - (self->delays[j] + self->rnd[j]);

            if (xind < 0)
                xind += self->size[j];

            ind = (int)xind;
            frac = xind - ind;
            x = self->buffer[j][ind];
            x1 = self->buffer[j][ind + 1];
            val = x + (x1 - x) * frac;
            val *= feed;
            filt = (self->lastSamples[j] - val) * self->damp + val;
            self->total_signal += filt;

            self->buffer[j][self->in_count[j]] = inval + junction - self->lastSamples[j];
            self->lastSamples[j] = filt;

            if(self->in_count[j] == 0)
                self->buffer[j][self->size[j]] = self->buffer[j][0];

            self->in_count[j]++;

            if (self->in_count[j] >= self->size[j])
                self->in_count[j] = 0;
        }

        self->data[i] = self->total_signal * 0.25;
    }
}

static void
WGVerb_mix_i(WGVerb *self)
{
    int i;
    MYFLT val;

    MYFLT mix = PyFloat_AS_DOUBLE(self->mix);
    MYFLT *in = Stream_getData((Stream *)self->input_stream);

    if (mix < 0.0)
        mix = 0.0;
    else if (mix > 1.0)
        mix = 1.0;

    for (i = 0; i < self->bufsize; i++)
    {
        val = in[i] * (1.0 - mix) + self->data[i] * mix;
        self->data[i] = val;
    }
}

static void
WGVerb_mix_a(WGVerb *self)
{
    int i;
    MYFLT mix, val;

    MYFLT *mi = Stream_getData((Stream *)self->mix_stream);
    MYFLT *in = Stream_getData((Stream *)self->input_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        mix = mi[i];

        if (mix < 0.0)
            mix = 0.0;
        else if (mix > 1.0)
            mix = 1.0;

        val = in[i] * (1.0 - mix) + self->data[i] * mix;
        self->data[i] = val;
    }
}

static void WGVerb_postprocessing_ii(WGVerb *self) { POST_PROCESSING_II };
static void WGVerb_postprocessing_ai(WGVerb *self) { POST_PROCESSING_AI };
static void WGVerb_postprocessing_ia(WGVerb *self) { POST_PROCESSING_IA };
static void WGVerb_postprocessing_aa(WGVerb *self) { POST_PROCESSING_AA };
static void WGVerb_postprocessing_ireva(WGVerb *self) { POST_PROCESSING_IREVA };
static void WGVerb_postprocessing_areva(WGVerb *self) { POST_PROCESSING_AREVA };
static void WGVerb_postprocessing_revai(WGVerb *self) { POST_PROCESSING_REVAI };
static void WGVerb_postprocessing_revaa(WGVerb *self) { POST_PROCESSING_REVAA };
static void WGVerb_postprocessing_revareva(WGVerb *self) { POST_PROCESSING_REVAREVA };

static void
WGVerb_setProcMode(WGVerb *self)
{
    int procmode, muladdmode, mixmode;
    procmode = self->modebuffer[2] + self->modebuffer[3] * 10;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    mixmode = self->modebuffer[4];

    switch (procmode)
    {
        case 0:
            self->proc_func_ptr = WGVerb_process_ii;
            break;

        case 1:
            self->proc_func_ptr = WGVerb_process_ai;
            break;

        case 10:
            self->proc_func_ptr = WGVerb_process_ia;
            break;

        case 11:
            self->proc_func_ptr = WGVerb_process_aa;
            break;
    }

    switch (mixmode)
    {
        case 0:
            self->mix_func_ptr = WGVerb_mix_i;
            break;

        case 1:
            self->mix_func_ptr = WGVerb_mix_a;
            break;
    }

    switch (muladdmode)
    {
        case 0:
            self->muladd_func_ptr = WGVerb_postprocessing_ii;
            break;

        case 1:
            self->muladd_func_ptr = WGVerb_postprocessing_ai;
            break;

        case 2:
            self->muladd_func_ptr = WGVerb_postprocessing_revai;
            break;

        case 10:
            self->muladd_func_ptr = WGVerb_postprocessing_ia;
            break;

        case 11:
            self->muladd_func_ptr = WGVerb_postprocessing_aa;
            break;

        case 12:
            self->muladd_func_ptr = WGVerb_postprocessing_revaa;
            break;

        case 20:
            self->muladd_func_ptr = WGVerb_postprocessing_ireva;
            break;

        case 21:
            self->muladd_func_ptr = WGVerb_postprocessing_areva;
            break;

        case 22:
            self->muladd_func_ptr = WGVerb_postprocessing_revareva;
            break;
    }
}

static void
WGVerb_compute_next_data_frame(WGVerb *self)
{
    (*self->proc_func_ptr)(self);
    (*self->mix_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
WGVerb_traverse(WGVerb *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->input);
    Py_VISIT(self->feedback);
    Py_VISIT(self->cutoff);
    Py_VISIT(self->mix);
    return 0;
}

static int
WGVerb_clear(WGVerb *self)
{
    pyo_CLEAR
    Py_CLEAR(self->input);
    Py_CLEAR(self->feedback);
    Py_CLEAR(self->cutoff);
    Py_CLEAR(self->mix);
    return 0;
}

static void
WGVerb_dealloc(WGVerb* self)
{
    int i;
    pyo_DEALLOC

    for (i = 0; i < 8; i++)
    {
        PyMem_RawFree(self->buffer[i]);
    }

    WGVerb_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
WGVerb_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i, j;
    PyObject *inputtmp, *input_streamtmp, *feedbacktmp = NULL, *cutofftmp = NULL, *mixtmp = NULL, *multmp = NULL, *addtmp = NULL;
    WGVerb *self;
    self = (WGVerb *)type->tp_alloc(type, 0);

    self->feedback = PyFloat_FromDouble(0.5);
    self->cutoff = PyFloat_FromDouble(5000.0);
    self->mix = PyFloat_FromDouble(0.5);
    self->lastFreq = self->damp = 0.0;

    self->total_signal = 0.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;
    self->modebuffer[4] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, WGVerb_compute_next_data_frame);
    self->mode_func_ptr = WGVerb_setProcMode;

    for (i = 0; i < 8; i++)
    {
        self->in_count[i] = 0;
        self->lastSamples[i] = 0.0;
        self->rnd[i] = self->rnd_value[i] = self->rnd_oldValue[i] = self->rnd_diff[i] = 0.0;
        self->rnd_time[i] = 1.0;
        self->rnd_timeInc[i] = reverbParams[i][2] * randomScaling / self->sr;
        self->rnd_range[i] = reverbParams[i][1] * randomScaling * self->sr;
        self->rnd_halfRange[i] = self->rnd_range[i] * 0.5;
        self->delays[i] = reverbParams[i][0] * (self->sr / 44100.0);
    }

    static char *kwlist[] = {"input", "feedback", "cutoff", "mix", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOOO", kwlist, &inputtmp, &feedbacktmp, &cutofftmp, &mixtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    INIT_INPUT_STREAM

    if (feedbacktmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFeedback", "O", feedbacktmp);
    }

    if (cutofftmp)
    {
        PyObject_CallMethod((PyObject *)self, "setCutoff", "O", cutofftmp);
    }

    if (mixtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMix", "O", mixtmp);
    }

    if (multmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMul", "O", multmp);
    }

    if (addtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setAdd", "O", addtmp);
    }

    PyObject_CallMethod(self->server, "addStream", "O", self->stream);

    for (i = 0; i < 8; i++)
    {
        self->size[i] = reverbParams[i][0] * (self->sr / 44100.0) + (int)(reverbParams[i][1] * self->sr + 0.5);
        self->buffer[i] = (MYFLT *)PyMem_RawRealloc(self->buffer[i], (self->size[i] + 1) * sizeof(MYFLT));

        for (j = 0; j < (self->size[i] + 1); j++)
        {
            self->buffer[i][j] = 0.;
        }
    }

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * WGVerb_getServer(WGVerb* self) { GET_SERVER };
static PyObject * WGVerb_getStream(WGVerb* self) { GET_STREAM };
static PyObject * WGVerb_setMul(WGVerb *self, PyObject *arg) { SET_MUL };
static PyObject * WGVerb_setAdd(WGVerb *self, PyObject *arg) { SET_ADD };
static PyObject * WGVerb_setSub(WGVerb *self, PyObject *arg) { SET_SUB };
static PyObject * WGVerb_setDiv(WGVerb *self, PyObject *arg) { SET_DIV };

static PyObject * WGVerb_play(WGVerb *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * WGVerb_out(WGVerb *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * WGVerb_stop(WGVerb *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * WGVerb_multiply(WGVerb *self, PyObject *arg) { MULTIPLY };
static PyObject * WGVerb_inplace_multiply(WGVerb *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * WGVerb_add(WGVerb *self, PyObject *arg) { ADD };
static PyObject * WGVerb_inplace_add(WGVerb *self, PyObject *arg) { INPLACE_ADD };
static PyObject * WGVerb_sub(WGVerb *self, PyObject *arg) { SUB };
static PyObject * WGVerb_inplace_sub(WGVerb *self, PyObject *arg) { INPLACE_SUB };
static PyObject * WGVerb_div(WGVerb *self, PyObject *arg) { DIV };
static PyObject * WGVerb_inplace_div(WGVerb *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
WGVerb_reset(WGVerb *self)
{
    int i, j;

    for (i = 0; i < 8; i++)
    {
        self->in_count[i] = 0;
        self->lastSamples[i] = 0.0;

        for (j = 0; j < (self->size[i] + 1); j++)
        {
            self->buffer[i][j] = 0.;
        }
    }

    self->total_signal = 0.0;

    Py_RETURN_NONE;
}

static PyObject * WGVerb_setFeedback(WGVerb *self, PyObject *arg) { SET_PARAM(self->feedback, self->feedback_stream, 2); }
static PyObject * WGVerb_setCutoff(WGVerb *self, PyObject *arg) { SET_PARAM(self->cutoff, self->cutoff_stream, 3); }
static PyObject * WGVerb_setMix(WGVerb *self, PyObject *arg) {SET_PARAM(self->mix, self->mix_stream, 4); }

static PyMemberDef WGVerb_members[] =
{
    {"server", T_OBJECT_EX, offsetof(WGVerb, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(WGVerb, stream), 0, "Stream object."},
    {"input", T_OBJECT_EX, offsetof(WGVerb, input), 0, "Input sound object."},
    {"feedback", T_OBJECT_EX, offsetof(WGVerb, feedback), 0, "Feedback value."},
    {"cutoff", T_OBJECT_EX, offsetof(WGVerb, cutoff), 0, "WGVerb lowpass filter cutoff."},
    {"mix", T_OBJECT_EX, offsetof(WGVerb, mix), 0, "Balance between dry and wet signals."},
    {"mul", T_OBJECT_EX, offsetof(WGVerb, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(WGVerb, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef WGVerb_methods[] =
{
    {"getServer", (PyCFunction)WGVerb_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)WGVerb_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)WGVerb_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)WGVerb_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)WGVerb_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"reset", (PyCFunction)WGVerb_reset, METH_NOARGS, "Reset the delay line."},
    {"setFeedback", (PyCFunction)WGVerb_setFeedback, METH_O, "Sets feedback value between 0 -> 1."},
    {"setCutoff", (PyCFunction)WGVerb_setCutoff, METH_O, "Sets lowpass filter cutoff."},
    {"setMix", (PyCFunction)WGVerb_setMix, METH_O, "Sets balance between dry and wet signals."},
    {"setMul", (PyCFunction)WGVerb_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)WGVerb_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)WGVerb_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)WGVerb_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods WGVerb_as_number =
{
    (binaryfunc)WGVerb_add,                      /*nb_add*/
    (binaryfunc)WGVerb_sub,                 /*nb_subtract*/
    (binaryfunc)WGVerb_multiply,                 /*nb_multiply*/
    0,                /*nb_remainder*/
    0,                   /*nb_divmod*/
    0,                   /*nb_power*/
    0,                  /*nb_neg*/
    0,                /*nb_pos*/
    0,                  /*(unaryfunc)array_abs,*/
    0,                    /*nb_nonzero*/
    0,                    /*nb_invert*/
    0,               /*nb_lshift*/
    0,              /*nb_rshift*/
    0,              /*nb_and*/
    0,              /*nb_xor*/
    0,               /*nb_or*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    (binaryfunc)WGVerb_inplace_add,              /*inplace_add*/
    (binaryfunc)WGVerb_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)WGVerb_inplace_multiply,         /*inplace_multiply*/
    0,        /*inplace_remainder*/
    0,           /*inplace_power*/
    0,       /*inplace_lshift*/
    0,      /*inplace_rshift*/
    0,      /*inplace_and*/
    0,      /*inplace_xor*/
    0,       /*inplace_or*/
    0,             /*nb_floor_divide*/
    (binaryfunc)WGVerb_div,                       /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    (binaryfunc)WGVerb_inplace_div,                       /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject WGVerbType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.WGVerb_base",         /*tp_name*/
    sizeof(WGVerb),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)WGVerb_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_as_async (tp_compare in Python 2)*/
    0,                         /*tp_repr*/
    &WGVerb_as_number,             /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
    "WGVerb objects. Waveguide-based reverberation network.",           /* tp_doc */
    (traverseproc)WGVerb_traverse,   /* tp_traverse */
    (inquiry)WGVerb_clear,           /* tp_clear */
    0,                     /* tp_richcompare */
    0,                     /* tp_weaklistoffset */
    0,                     /* tp_iter */
    0,                     /* tp_iternext */
    WGVerb_methods,             /* tp_methods */
    WGVerb_members,             /* tp_members */
    0,                      /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,      /* tp_init */
    0,                         /* tp_alloc */
    WGVerb_new,                 /* tp_new */
};

/***************/
/**** STRev ****/
/***************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *input;
    Stream *input_stream;
    PyObject *inpos;
    Stream *inpos_stream;
    PyObject *revtime;
    Stream *revtime_stream;
    PyObject *cutoff;
    Stream *cutoff_stream;
    PyObject *mix;
    Stream *mix_stream;
    void (*mix_func_ptr)();
    int modebuffer[4];
    MYFLT firstRefGain;
    MYFLT total_signal[2];
    MYFLT delays[2][8];
    long size[2][8];
    int in_count[2][8];
    MYFLT *buffer[2][8];
    MYFLT *ref_buffer[NUM_REFS];
    int ref_size[NUM_REFS];
    int ref_in_count[NUM_REFS];
    MYFLT avg_time;
    MYFLT srfac;
    // lowpass
    MYFLT damp[2];
    MYFLT lastFreq;
    MYFLT nyquist;
    MYFLT lastInpos;
    // sample memories
    MYFLT lastSamples[2][8];
    // jitters
    MYFLT rnd[2][8];
    MYFLT rnd_value[2][8];
    MYFLT rnd_oldValue[2][8];
    MYFLT rnd_diff[2][8];
    MYFLT rnd_time[2][8];
    MYFLT rnd_timeInc[2][8];
    MYFLT rnd_range[2][8];
    MYFLT rnd_halfRange[2][8];
    MYFLT *buffer_streams;
    MYFLT *input_buffer[2];
} STReverb;

static void
STReverb_process_ii(STReverb *self)
{
    int i, j, k, k2, ind, half;
    MYFLT val, x, x1, xind, frac, junction, inval, filt, amp1, amp2, b, f, sum_ref, feed, step, invp;
    MYFLT ref_amp_l[NUM_REFS];
    MYFLT ref_amp_r[NUM_REFS];
    MYFLT ref_buf[2];

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT inpos = PyFloat_AS_DOUBLE(self->inpos);

    if (self->modebuffer[1] == 0)
        feed = PyFloat_AS_DOUBLE(self->revtime);
    else
        feed = Stream_getData((Stream *)self->revtime_stream)[0];

    MYFLT freq = PyFloat_AS_DOUBLE(self->cutoff);

    if (inpos < 0.0)
        inpos = 0.0;
    else if (inpos > 1.0)
        inpos = 1.0;

    if (feed < 0.01)
        feed = 0.01;

    feed = MYPOW(100.0, -self->avg_time / feed);

    if (freq < 20.0)
        freq = 20.0;
    else if (freq > self->nyquist)
        freq = self->nyquist;

    if (freq != self->lastFreq || inpos != self->lastInpos)
    {
        self->lastFreq = freq;
        self->lastInpos = inpos;
        f = ((1.0 - inpos) * 0.3 + 0.7) * freq;
        b = 2.0 - MYCOS(TWOPI * f / self->sr);
        self->damp[0] = (b - MYSQRT(b * b - 1.0));
        f = (inpos * 0.3 + 0.7) * freq;
        b = 2.0 - MYCOS(TWOPI * f / self->sr);
        self->damp[1] = (b - MYSQRT(b * b - 1.0));
    }

    /* position of the source and first reflexions */
    amp1 = 1.0 - inpos;
    amp2 = inpos;
    half = (NUM_REFS - 1) / 2;

    if (inpos <= 0.5)
    {
        step = (0.5 - inpos) / half;
        ref_amp_l[half] = ref_amp_r[half] = 0.5;

        for (k = 0; k < half; k++)
        {
            k2 = NUM_REFS - 1 - k;
            ref_amp_r[k] = ref_amp_l[k2] = inpos + step * k;
            ref_amp_l[k] = ref_amp_r[k2] = 1.0 - ref_amp_r[k];
            ref_amp_r[k2] *= inpos + 0.5;
        }
    }
    else
    {
        invp = 1.0 - inpos;
        step = (0.5 - invp) / half;
        ref_amp_l[half] = ref_amp_r[half] = 0.5;

        for (k = 0; k < half; k++)
        {
            k2 = NUM_REFS - 1 - k;
            ref_amp_l[k] = ref_amp_r[k2] = invp + step * k;
            ref_amp_r[k] = ref_amp_l[k2] = 1.0 - ref_amp_l[k];
            ref_amp_l[k2] *= invp + 0.5;
        }
    }

    for (i = 0; i < self->bufsize; i++)
    {
        self->input_buffer[0][i] = in[i] * amp1;
        self->input_buffer[1][i] = in[i] * amp2;
        ref_buf[0] = ref_buf[1] = 0.0;

        for (k = 0; k < NUM_REFS; k++)
        {
            sum_ref = self->ref_buffer[k][self->ref_in_count[k]];
            self->ref_buffer[k][self->ref_in_count[k]] = in[i];
            self->ref_in_count[k]++;

            if (self->ref_in_count[k] == self->ref_size[k])
                self->ref_in_count[k] = 0;

            ref_buf[0] += sum_ref * ref_amp_l[k];
            ref_buf[1] += sum_ref * ref_amp_r[k];
        }

        for (k = 0; k < 2; k++)
        {
            inval = self->input_buffer[k][i] * 0.8 + self->input_buffer[1 - k][i] * 0.2 + ref_buf[k] * 0.1;
            junction = self->total_signal[k] * .25;
            self->total_signal[k] = ref_buf[k] * self->firstRefGain;

            for (j = 0; j < 8; j++)
            {
                self->rnd_time[k][j] += self->rnd_timeInc[k][j];

                if (self->rnd_time[k][j] < 0.0)
                    self->rnd_time[k][j] += 1.0;
                else if (self->rnd_time[k][j] >= 1.0)
                {
                    self->rnd_time[k][j] -= 1.0;
                    self->rnd_oldValue[k][j] = self->rnd_value[k][j];
                    self->rnd_value[k][j] = self->rnd_range[k][j] * RANDOM_UNIFORM - self->rnd_halfRange[k][j];
                    self->rnd_diff[k][j] = self->rnd_value[k][j] - self->rnd_oldValue[k][j];
                }

                self->rnd[k][j] = self->rnd_oldValue[k][j] + self->rnd_diff[k][j] * self->rnd_time[k][j];

                xind = self->in_count[k][j] - (self->delays[k][j] + self->rnd[k][j]);

                if (xind < 0)
                    xind += self->size[k][j];

                ind = (int)xind;
                frac = xind - ind;
                x = self->buffer[k][j][ind];
                x1 = self->buffer[k][j][ind + 1];
                val = x + (x1 - x) * frac;
                val *= feed;
                filt = val + (self->lastSamples[k][j] - val) * self->damp[k];
                self->total_signal[k] += filt;

                self->buffer[k][j][self->in_count[k][j]] = inval + junction - self->lastSamples[k][j];
                self->lastSamples[k][j] = filt;

                if(self->in_count[k][j] == 0)
                    self->buffer[k][j][self->size[k][j]] = self->buffer[k][j][0];

                self->in_count[k][j]++;

                if (self->in_count[k][j] >= self->size[k][j])
                    self->in_count[k][j] = 0;
            }

            self->buffer_streams[i + k * self->bufsize] = self->total_signal[k] * 0.25;
        }
    }
}

static void
STReverb_process_ai(STReverb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt, amp1, amp2, b, f, sum_ref, inpos, feed, step, invp;
    MYFLT ref_amp_l[NUM_REFS];
    MYFLT ref_amp_r[NUM_REFS];
    MYFLT ref_buf[2];
    int i, j, k, k2, ind, half;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT *pos = Stream_getData((Stream *)self->inpos_stream);

    if (self->modebuffer[1] == 0)
        feed = PyFloat_AS_DOUBLE(self->revtime);
    else
        feed = Stream_getData((Stream *)self->revtime_stream)[0];

    MYFLT freq = PyFloat_AS_DOUBLE(self->cutoff);

    if (feed < 0.01)
        feed = 0.01;

    feed = MYPOW(100.0, -self->avg_time / feed);

    if (freq < 20.0)
        freq = 20.0;
    else if (freq > self->nyquist)
        freq = self->nyquist;

    for (i = 0; i < self->bufsize; i++)
    {
        inpos = pos[i];

        if (inpos < 0.0)
            inpos = 0.0;
        else if (inpos > 1.0)
            inpos = 1.0;

        if (freq != self->lastFreq || inpos != self->lastInpos)
        {
            self->lastFreq = freq;
            self->lastInpos = inpos;
            f = ((1.0 - inpos) * 0.3 + 0.7) * freq;
            b = 2.0 - MYCOS(TWOPI * f / self->sr);
            self->damp[0] = (b - MYSQRT(b * b - 1.0));
            f = (inpos * 0.3 + 0.7) * freq;
            b = 2.0 - MYCOS(TWOPI * f / self->sr);
            self->damp[1] = (b - MYSQRT(b * b - 1.0));
        }

        /* position of the source and first reflexions */
        amp1 = 1.0 - inpos;
        amp2 = inpos;
        half = (NUM_REFS - 1) / 2;

        if (inpos <= 0.5)
        {
            step = (0.5 - inpos) / half;
            ref_amp_l[half] = ref_amp_r[half] = 0.5;

            for (k = 0; k < half; k++)
            {
                k2 = NUM_REFS - 1 - k;
                ref_amp_r[k] = ref_amp_l[k2] = inpos + step * k;
                ref_amp_l[k] = ref_amp_r[k2] = 1.0 - ref_amp_r[k];
                ref_amp_r[k2] *= inpos + 0.5;
            }
        }
        else
        {
            invp = 1.0 - inpos;
            step = (0.5 - invp) / half;
            ref_amp_l[half] = ref_amp_r[half] = 0.5;

            for (k = 0; k < half; k++)
            {
                k2 = NUM_REFS - 1 - k;
                ref_amp_l[k] = ref_amp_r[k2] = invp + step * k;
                ref_amp_r[k] = ref_amp_l[k2] = 1.0 - ref_amp_l[k];
                ref_amp_l[k2] *= invp + 0.5;
            }
        }

        self->input_buffer[0][i] = in[i] * amp1;
        self->input_buffer[1][i] = in[i] * amp2;
        ref_buf[0] = ref_buf[1] = 0.0;

        for (k = 0; k < NUM_REFS; k++)
        {
            sum_ref = self->ref_buffer[k][self->ref_in_count[k]];
            self->ref_buffer[k][self->ref_in_count[k]] = in[i];
            self->ref_in_count[k]++;

            if (self->ref_in_count[k] == self->ref_size[k])
                self->ref_in_count[k] = 0;

            ref_buf[0] += sum_ref * ref_amp_l[k];
            ref_buf[1] += sum_ref * ref_amp_r[k];
        }

        for (k = 0; k < 2; k++)
        {
            inval = self->input_buffer[k][i] * 0.8 + self->input_buffer[1 - k][i] * 0.2 + ref_buf[k] * 0.1;
            junction = self->total_signal[k] * .25;
            self->total_signal[k] = ref_buf[k] * self->firstRefGain;

            for (j = 0; j < 8; j++)
            {
                self->rnd_time[k][j] += self->rnd_timeInc[k][j];

                if (self->rnd_time[k][j] < 0.0)
                    self->rnd_time[k][j] += 1.0;
                else if (self->rnd_time[k][j] >= 1.0)
                {
                    self->rnd_time[k][j] -= 1.0;
                    self->rnd_oldValue[k][j] = self->rnd_value[k][j];
                    self->rnd_value[k][j] = self->rnd_range[k][j] * RANDOM_UNIFORM - self->rnd_halfRange[k][j];
                    self->rnd_diff[k][j] = self->rnd_value[k][j] - self->rnd_oldValue[k][j];
                }

                self->rnd[k][j] = self->rnd_oldValue[k][j] + self->rnd_diff[k][j] * self->rnd_time[k][j];

                xind = self->in_count[k][j] - (self->delays[k][j] + self->rnd[k][j]);

                if (xind < 0)
                    xind += self->size[k][j];

                ind = (int)xind;
                frac = xind - ind;
                x = self->buffer[k][j][ind];
                x1 = self->buffer[k][j][ind + 1];
                val = x + (x1 - x) * frac;
                val *= feed;
                filt = val + (self->lastSamples[k][j] - val) * self->damp[k];
                self->total_signal[k] += filt;

                self->buffer[k][j][self->in_count[k][j]] = inval + junction - self->lastSamples[k][j];
                self->lastSamples[k][j] = filt;

                if(self->in_count[k][j] == 0)
                    self->buffer[k][j][self->size[k][j]] = self->buffer[k][j][0];

                self->in_count[k][j]++;

                if (self->in_count[k][j] >= self->size[k][j])
                    self->in_count[k][j] = 0;
            }

            self->buffer_streams[i + k * self->bufsize] = self->total_signal[k] * 0.25;
        }
    }
}

static void
STReverb_process_ia(STReverb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt, amp1, amp2, b, f, sum_ref, feed, freq, step, invp;
    MYFLT ref_amp_l[NUM_REFS];
    MYFLT ref_amp_r[NUM_REFS];
    MYFLT ref_buf[2];
    int i, j, k, k2, ind, half;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT inpos = PyFloat_AS_DOUBLE(self->inpos);

    if (self->modebuffer[1] == 0)
        feed = PyFloat_AS_DOUBLE(self->revtime);
    else
        feed = Stream_getData((Stream *)self->revtime_stream)[0];

    MYFLT *fr = Stream_getData((Stream *)self->cutoff_stream);

    if (inpos < 0.0)
        inpos = 0.0;
    else if (inpos > 1.0)
        inpos = 1.0;

    if (feed < 0.01)
        feed = 0.01;

    feed = MYPOW(100.0, -self->avg_time / feed);

    /* position of the source and first reflexions */
    amp1 = 1.0 - inpos;
    amp2 = inpos;
    half = (NUM_REFS - 1) / 2;

    if (inpos <= 0.5)
    {
        step = (0.5 - inpos) / half;
        ref_amp_l[half] = ref_amp_r[half] = 0.5;

        for (k = 0; k < half; k++)
        {
            k2 = NUM_REFS - 1 - k;
            ref_amp_r[k] = ref_amp_l[k2] = inpos + step * k;
            ref_amp_l[k] = ref_amp_r[k2] = 1.0 - ref_amp_r[k];
            ref_amp_r[k2] *= inpos + 0.5;
        }
    }
    else
    {
        invp = 1.0 - inpos;
        step = (0.5 - invp) / half;
        ref_amp_l[half] = ref_amp_r[half] = 0.5;

        for (k = 0; k < half; k++)
        {
            k2 = NUM_REFS - 1 - k;
            ref_amp_l[k] = ref_amp_r[k2] = invp + step * k;
            ref_amp_r[k] = ref_amp_l[k2] = 1.0 - ref_amp_l[k];
            ref_amp_l[k2] *= invp + 0.5;
        }
    }

    for (i = 0; i < self->bufsize; i++)
    {
        freq = fr[i];

        if (freq < 20.0)
            freq = 20.0;
        else if (freq > self->nyquist)
            freq = self->nyquist;

        if (freq != self->lastFreq || inpos != self->lastInpos)
        {
            self->lastFreq = freq;
            self->lastInpos = inpos;
            f = ((1.0 - inpos) * 0.3 + 0.7) * freq;
            b = 2.0 - MYCOS(TWOPI * f / self->sr);
            self->damp[0] = (b - MYSQRT(b * b - 1.0));
            f = (inpos * 0.3 + 0.7) * freq;
            b = 2.0 - MYCOS(TWOPI * f / self->sr);
            self->damp[1] = (b - MYSQRT(b * b - 1.0));
        }

        self->input_buffer[0][i] = in[i] * amp1;
        self->input_buffer[1][i] = in[i] * amp2;
        ref_buf[0] = ref_buf[1] = 0.0;

        for (k = 0; k < NUM_REFS; k++)
        {
            sum_ref = self->ref_buffer[k][self->ref_in_count[k]];
            self->ref_buffer[k][self->ref_in_count[k]] = in[i];
            self->ref_in_count[k]++;

            if (self->ref_in_count[k] == self->ref_size[k])
                self->ref_in_count[k] = 0;

            ref_buf[0] += sum_ref * ref_amp_l[k];
            ref_buf[1] += sum_ref * ref_amp_r[k];
        }

        for (k = 0; k < 2; k++)
        {
            inval = self->input_buffer[k][i] * 0.8 + self->input_buffer[1 - k][i] * 0.2 + ref_buf[k] * 0.1;
            junction = self->total_signal[k] * .25;
            self->total_signal[k] = ref_buf[k] * self->firstRefGain;

            for (j = 0; j < 8; j++)
            {
                self->rnd_time[k][j] += self->rnd_timeInc[k][j];

                if (self->rnd_time[k][j] < 0.0)
                    self->rnd_time[k][j] += 1.0;
                else if (self->rnd_time[k][j] >= 1.0)
                {
                    self->rnd_time[k][j] -= 1.0;
                    self->rnd_oldValue[k][j] = self->rnd_value[k][j];
                    self->rnd_value[k][j] = self->rnd_range[k][j] * RANDOM_UNIFORM - self->rnd_halfRange[k][j];
                    self->rnd_diff[k][j] = self->rnd_value[k][j] - self->rnd_oldValue[k][j];
                }

                self->rnd[k][j] = self->rnd_oldValue[k][j] + self->rnd_diff[k][j] * self->rnd_time[k][j];

                xind = self->in_count[k][j] - (self->delays[k][j] + self->rnd[k][j]);

                if (xind < 0)
                    xind += self->size[k][j];

                ind = (int)xind;
                frac = xind - ind;
                x = self->buffer[k][j][ind];
                x1 = self->buffer[k][j][ind + 1];
                val = x + (x1 - x) * frac;
                val *= feed;
                filt = val + (self->lastSamples[k][j] - val) * self->damp[k];
                self->total_signal[k] += filt;

                self->buffer[k][j][self->in_count[k][j]] = inval + junction - self->lastSamples[k][j];
                self->lastSamples[k][j] = filt;

                if(self->in_count[k][j] == 0)
                    self->buffer[k][j][self->size[k][j]] = self->buffer[k][j][0];

                self->in_count[k][j]++;

                if (self->in_count[k][j] >= self->size[k][j])
                    self->in_count[k][j] = 0;
            }

            self->buffer_streams[i + k * self->bufsize] = self->total_signal[k] * 0.25;
        }
    }
}

static void
STReverb_process_aa(STReverb *self)
{
    MYFLT val, x, x1, xind, frac, junction, inval, filt, amp1, amp2, b, f, sum_ref, inpos, feed, freq, step, invp;
    MYFLT ref_amp_l[NUM_REFS];
    MYFLT ref_amp_r[NUM_REFS];
    MYFLT ref_buf[2];
    int i, j, k, k2, ind, half;

    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    MYFLT *pos = Stream_getData((Stream *)self->inpos_stream);

    if (self->modebuffer[1] == 0)
        feed = PyFloat_AS_DOUBLE(self->revtime);
    else
        feed = Stream_getData((Stream *)self->revtime_stream)[0];

    MYFLT *fr = Stream_getData((Stream *)self->cutoff_stream);

    if (feed < 0.01)
        feed = 0.01;

    feed = MYPOW(100.0, -self->avg_time / feed);

    for (i = 0; i < self->bufsize; i++)
    {
        inpos = pos[i];
        freq = fr[i];

        if (inpos < 0.0)
            inpos = 0.0;
        else if (inpos > 1.0)
            inpos = 1.0;

        if (freq < 20.0)
            freq = 20.0;
        else if (freq > self->nyquist)
            freq = self->nyquist;

        if (freq != self->lastFreq || inpos != self->lastInpos)
        {
            self->lastFreq = freq;
            self->lastInpos = inpos;
            f = ((1.0 - inpos) * 0.3 + 0.7) * freq;
            b = 2.0 - MYCOS(TWOPI * f / self->sr);
            self->damp[0] = (b - MYSQRT(b * b - 1.0));
            f = (inpos * 0.3 + 0.7) * freq;
            b = 2.0 - MYCOS(TWOPI * f / self->sr);
            self->damp[1] = (b - MYSQRT(b * b - 1.0));
        }

        /* position of the source and first reflexions */
        amp1 = 1.0 - inpos;
        amp2 = inpos;
        half = (NUM_REFS - 1) / 2;

        if (inpos <= 0.5)
        {
            step = (0.5 - inpos) / half;
            ref_amp_l[half] = ref_amp_r[half] = 0.5;

            for (k = 0; k < half; k++)
            {
                k2 = NUM_REFS - 1 - k;
                ref_amp_r[k] = ref_amp_l[k2] = inpos + step * k;
                ref_amp_l[k] = ref_amp_r[k2] = 1.0 - ref_amp_r[k];
                ref_amp_r[k2] *= inpos + 0.5;
            }
        }
        else
        {
            invp = 1.0 - inpos;
            step = (0.5 - invp) / half;
            ref_amp_l[half] = ref_amp_r[half] = 0.5;

            for (k = 0; k < half; k++)
            {
                k2 = NUM_REFS - 1 - k;
                ref_amp_l[k] = ref_amp_r[k2] = invp + step * k;
                ref_amp_r[k] = ref_amp_l[k2] = 1.0 - ref_amp_l[k];
                ref_amp_l[k2] *= invp + 0.5;
            }
        }

        self->input_buffer[0][i] = in[i] * amp1;
        self->input_buffer[1][i] = in[i] * amp2;
        ref_buf[0] = ref_buf[1] = 0.0;

        for (k = 0; k < NUM_REFS; k++)
        {
            sum_ref = self->ref_buffer[k][self->ref_in_count[k]];
            self->ref_buffer[k][self->ref_in_count[k]] = in[i];
            self->ref_in_count[k]++;

            if (self->ref_in_count[k] == self->ref_size[k])
                self->ref_in_count[k] = 0;

            ref_buf[0] += sum_ref * ref_amp_l[k];
            ref_buf[1] += sum_ref * ref_amp_r[k];
        }

        for (k = 0; k < 2; k++)
        {
            inval = self->input_buffer[k][i] * 0.8 + self->input_buffer[1 - k][i] * 0.2 + ref_buf[k] * 0.1;
            junction = self->total_signal[k] * .25;
            self->total_signal[k] = ref_buf[k] * self->firstRefGain;

            for (j = 0; j < 8; j++)
            {
                self->rnd_time[k][j] += self->rnd_timeInc[k][j];

                if (self->rnd_time[k][j] < 0.0)
                    self->rnd_time[k][j] += 1.0;
                else if (self->rnd_time[k][j] >= 1.0)
                {
                    self->rnd_time[k][j] -= 1.0;
                    self->rnd_oldValue[k][j] = self->rnd_value[k][j];
                    self->rnd_value[k][j] = self->rnd_range[k][j] * RANDOM_UNIFORM - self->rnd_halfRange[k][j];
                    self->rnd_diff[k][j] = self->rnd_value[k][j] - self->rnd_oldValue[k][j];
                }

                self->rnd[k][j] = self->rnd_oldValue[k][j] + self->rnd_diff[k][j] * self->rnd_time[k][j];

                xind = self->in_count[k][j] - (self->delays[k][j] + self->rnd[k][j]);

                if (xind < 0)
                    xind += self->size[k][j];

                ind = (int)xind;
                frac = xind - ind;
                x = self->buffer[k][j][ind];
                x1 = self->buffer[k][j][ind + 1];
                val = x + (x1 - x) * frac;
                val *= feed;
                filt = val + (self->lastSamples[k][j] - val) * self->damp[k];
                self->total_signal[k] += filt;

                self->buffer[k][j][self->in_count[k][j]] = inval + junction - self->lastSamples[k][j];
                self->lastSamples[k][j] = filt;

                if(self->in_count[k][j] == 0)
                    self->buffer[k][j][self->size[k][j]] = self->buffer[k][j][0];

                self->in_count[k][j]++;

                if (self->in_count[k][j] >= self->size[k][j])
                    self->in_count[k][j] = 0;
            }

            self->buffer_streams[i + k * self->bufsize] = self->total_signal[k] * 0.25;
        }
    }
}

static void
STReverb_mix_i(STReverb *self)
{
    int i, k;
    MYFLT val;

    MYFLT mix = PyFloat_AS_DOUBLE(self->mix);

    if (mix < 0.0)
        mix = 0.0;
    else if (mix > 1.0)
        mix = 1.0;

    for (i = 0; i < self->bufsize; i++)
    {
        for (k = 0; k < 2; k++)
        {
            val = self->input_buffer[k][i] + (self->buffer_streams[i + k * self->bufsize] - self->input_buffer[k][i]) * mix;
            self->buffer_streams[i + k * self->bufsize] = val;
        }
    }
}

static void
STReverb_mix_a(STReverb *self)
{
    int i, k;
    MYFLT mix, val;

    MYFLT *mi = Stream_getData((Stream *)self->mix_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        mix = mi[i];

        if (mix < 0.0)
            mix = 0.0;
        else if (mix > 1.0)
            mix = 1.0;

        for (k = 0; k < 2; k++)
        {
            val = self->input_buffer[k][i] + (self->buffer_streams[i + k * self->bufsize] - self->input_buffer[k][i]) * mix;
            self->buffer_streams[i + k * self->bufsize] = val;
        }
    }
}

static void
STReverb_setProcMode(STReverb *self)
{
    int procmode, mixmode;
    procmode = self->modebuffer[0] + self->modebuffer[2] * 10;
    mixmode = self->modebuffer[3];

    switch (procmode)
    {
        case 0:
            self->proc_func_ptr = STReverb_process_ii;
            break;

        case 1:
            self->proc_func_ptr = STReverb_process_ai;
            break;

        case 10:
            self->proc_func_ptr = STReverb_process_ia;
            break;

        case 11:
            self->proc_func_ptr = STReverb_process_aa;
            break;
    }

    switch (mixmode)
    {
        case 0:
            self->mix_func_ptr = STReverb_mix_i;
            break;

        case 1:
            self->mix_func_ptr = STReverb_mix_a;
            break;
    }
}

MYFLT *
STReverb_getSamplesBuffer(STReverb *self)
{
    return (MYFLT *)self->buffer_streams;
}

static void
STReverb_compute_next_data_frame(STReverb *self)
{
    (*self->proc_func_ptr)(self);
    (*self->mix_func_ptr)(self);
}

static int
STReverb_traverse(STReverb *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->input);
    Py_VISIT(self->inpos);
    Py_VISIT(self->revtime);
    Py_VISIT(self->cutoff);
    Py_VISIT(self->mix);
    return 0;
}

static int
STReverb_clear(STReverb *self)
{
    pyo_CLEAR
    Py_CLEAR(self->input);
    Py_CLEAR(self->inpos);
    Py_CLEAR(self->revtime);
    Py_CLEAR(self->cutoff);
    Py_CLEAR(self->mix);
    return 0;
}

static void
STReverb_dealloc(STReverb* self)
{
    int i, k;
    pyo_DEALLOC

    for (k = 0; k < 2; k++)
    {
        PyMem_RawFree(self->input_buffer[k]);

        for (i = 0; i < 8; i++)
        {
            PyMem_RawFree(self->buffer[k][i]);
        }
    }

    for (i = 0; i < NUM_REFS; i++)
    {
        PyMem_RawFree(self->ref_buffer[i]);
    }

    PyMem_RawFree(self->buffer_streams);
    STReverb_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
STReverb_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i, j, k, din;
    long maxsize;
    MYFLT roomSize = 1.0;
    MYFLT firstRefTmp = -3.0;
    PyObject *inputtmp, *input_streamtmp, *inpostmp = NULL, *revtimetmp = NULL, *cutofftmp = NULL, *mixtmp = NULL;
    STReverb *self;
    self = (STReverb *)type->tp_alloc(type, 0);

    self->inpos = PyFloat_FromDouble(0.5);
    self->revtime = PyFloat_FromDouble(0.5);
    self->cutoff = PyFloat_FromDouble(5000.0);
    self->mix = PyFloat_FromDouble(0.5);
    self->total_signal[0] = self->total_signal[1] = self->lastFreq = self->damp[0] = self->damp[1] = 0.0;
    self->lastInpos = -1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;

    INIT_OBJECT_COMMON

    self->nyquist = self->sr * 0.49;
    self->srfac = self->sr / 44100.0;

    Stream_setFunctionPtr(self->stream, STReverb_compute_next_data_frame);
    self->mode_func_ptr = STReverb_setProcMode;

    static char *kwlist[] = {"input", "inpos", "revtime", "cutoff", "mix", "roomSize", "firstRefGain", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE_O_OOOOFF, kwlist, &inputtmp, &inpostmp, &revtimetmp, &cutofftmp, &mixtmp, &roomSize, &firstRefTmp))
        Py_RETURN_NONE;

    INIT_INPUT_STREAM

    if (inpostmp)
    {
        PyObject_CallMethod((PyObject *)self, "setInpos", "O", inpostmp);
    }

    if (revtimetmp)
    {
        PyObject_CallMethod((PyObject *)self, "setRevtime", "O", revtimetmp);
    }

    if (cutofftmp)
    {
        PyObject_CallMethod((PyObject *)self, "setCutoff", "O", cutofftmp);
    }

    if (mixtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMix", "O", mixtmp);
    }

    PyObject_CallMethod(self->server, "addStream", "O", self->stream);

    self->firstRefGain = MYPOW(10.0, firstRefTmp * 0.05);

    if (roomSize < 0.25)
        roomSize = 0.25;
    else if (roomSize > 4.0)
        roomSize = 4.0;

    self->avg_time = 0.0;

    for (k = 0; k < 2; k++)
    {
        din = k * 3;

        for (i = 0; i < 8; i++)
        {
            self->in_count[k][i] = 0;
            self->lastSamples[k][i] = 0.0;
            self->rnd[k][i] = self->rnd_value[k][i] = self->rnd_oldValue[k][i] = self->rnd_diff[k][i] = 0.0;
            self->rnd_time[k][i] = 1.0;
            self->rnd_timeInc[k][i] = reverbParams[i][2] * randomScaling / self->sr;
            self->rnd_range[k][i] = reverbParams[i][1] * randomScaling * self->sr;
            self->rnd_halfRange[k][i] = self->rnd_range[k][i] * 0.5;
            self->delays[k][i] = reverbParams[i][din] * self->srfac * roomSize;
            self->avg_time += self->delays[k][i] / self->sr;
            self->size[k][i] = reverbParams[i][din] * self->srfac * roomSize + (int)(reverbParams[i][1] * self->sr + 0.5);
            maxsize = reverbParams[i][din] * self->srfac * 4.0 + (int)(reverbParams[i][1] * self->sr + 0.5);
            self->buffer[k][i] = (MYFLT *)PyMem_RawRealloc(self->buffer[k][i], (maxsize + 1) * sizeof(MYFLT));

            for (j = 0; j < (maxsize + 1); j++)
            {
                self->buffer[k][i][j] = 0.;
            }
        }
    }

    self->avg_time /= 16.0;

    for (k = 0; k < NUM_REFS; k++)
    {
        self->ref_in_count[k] = 0;
        self->ref_size[k] = (int)(first_ref_delays[k] * self->srfac * roomSize + 0.5);
        maxsize = (int)(first_ref_delays[k] * self->srfac * 4.0 + 0.5);
        self->ref_buffer[k] = (MYFLT *)PyMem_RawRealloc(self->ref_buffer[k], (maxsize + 1) * sizeof(MYFLT));

        for (i = 0; i < (maxsize + 1); i++)
        {
            self->ref_buffer[k][i] = 0.0;
        }
    }

    for (k = 0; k < 2; k++)
    {
        self->input_buffer[k] = (MYFLT *)PyMem_RawRealloc(self->input_buffer[k], self->bufsize * sizeof(MYFLT));

        for (i = 0; i < self->bufsize; i++)
        {
            self->input_buffer[k][i] = 0.0;
        }
    }

    self->buffer_streams = (MYFLT *)PyMem_RawRealloc(self->buffer_streams, 2 * self->bufsize * sizeof(MYFLT));

    for (i = 0; i < (2 * self->bufsize); i++)
    {
        self->buffer_streams[i] = 0.0;
    }

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * STReverb_getServer(STReverb* self) { GET_SERVER };
static PyObject * STReverb_getStream(STReverb* self) { GET_STREAM };

static PyObject * STReverb_play(STReverb *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * STReverb_stop(STReverb *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject *
STReverb_reset(STReverb *self)
{
    int i, j, k, maxsize;

    for (k = 0; k < 2; k++)
    {
        for (i = 0; i < 8; i++)
        {
            self->in_count[k][i] = 0;
            self->lastSamples[k][i] = 0.0;

            for (j = 0; j < self->size[k][i]; j++)
            {
                self->buffer[k][i][j] = 0.;
            }
        }
    }

    for (k = 0; k < NUM_REFS; k++)
    {
        self->ref_in_count[k] = 0;
        maxsize = (int)(first_ref_delays[k] * self->srfac * 4.0 + 0.5);

        for (i = 0; i < (maxsize + 1); i++)
        {
            self->ref_buffer[k][i] = 0.0;
        }
    }

    for (k = 0; k < 2; k++)
    {
        for (i = 0; i < self->bufsize; i++)
        {
            self->input_buffer[k][i] = 0.0;
        }
    }

    for (i = 0; i < (2 * self->bufsize); i++)
    {
        self->buffer_streams[i] = 0.0;
    }

    self->total_signal[0] = self->total_signal[1] = 0.0;

    Py_RETURN_NONE;
}

static PyObject * STReverb_setInpos(STReverb *self, PyObject *arg) { SET_PARAM(self->inpos, self->inpos_stream, 0); }
static PyObject * STReverb_setRevtime(STReverb *self, PyObject *arg) { SET_PARAM(self->revtime, self->revtime_stream, 1); }
static PyObject * STReverb_setCutoff(STReverb *self, PyObject *arg) { SET_PARAM(self->cutoff, self->cutoff_stream, 2); }
static PyObject * STReverb_setMix(STReverb *self, PyObject *arg) {SET_PARAM(self->mix, self->mix_stream, 3); }

static PyObject *
STReverb_setRoomSize(STReverb *self, PyObject *arg)
{
    int i, j, k, din;
    long maxsize;
    MYFLT roomSize;

    ASSERT_ARG_NOT_NULL

    if (PyNumber_Check(arg))
    {
        roomSize = PyFloat_AsDouble(arg);

        if (roomSize < 0.25)
            roomSize = 0.25;
        else if (roomSize > 4.0)
            roomSize = 4.0;

        self->avg_time = 0.0;

        for (k = 0; k < 2; k++)
        {
            din = k * 3;

            for (i = 0; i < 8; i++)
            {
                self->in_count[k][i] = 0;
                self->lastSamples[k][i] = 0.0;
                self->rnd[k][i] = self->rnd_value[k][i] = self->rnd_oldValue[k][i] = self->rnd_diff[k][i] = 0.0;
                self->rnd_time[k][i] = 1.0;
                self->delays[k][i] = reverbParams[i][din] * self->srfac * roomSize;
                self->avg_time += self->delays[k][i] / self->sr;
                self->size[k][i] = reverbParams[i][din] * self->srfac * roomSize + (int)(reverbParams[i][1] * self->sr + 0.5);
                maxsize = reverbParams[i][din] * self->srfac * 2 + (int)(reverbParams[i][1] * self->sr + 0.5);

                for (j = 0; j < (maxsize + 1); j++)
                {
                    self->buffer[k][i][j] = 0.;
                }
            }
        }

        self->avg_time /= 16.0;

        for (k = 0; k < NUM_REFS; k++)
        {
            self->ref_in_count[k] = 0;
            self->ref_size[k] = (int)(first_ref_delays[k] * self->srfac * roomSize + 0.5);
            maxsize = (int)(first_ref_delays[k] * self->srfac * 2 + 0.5);

            for (i = 0; i < (maxsize + 1); i++)
            {
                self->ref_buffer[k][i] = 0.0;
            }
        }
    }

    Py_RETURN_NONE;
}

static PyObject *
STReverb_setFirstRefGain(STReverb *self, PyObject *arg)
{
    MYFLT tmp;

    ASSERT_ARG_NOT_NULL

    if (PyNumber_Check(arg))
    {
        tmp = PyFloat_AsDouble(arg);
        self->firstRefGain = MYPOW(10.0, tmp * 0.05);
    }

    Py_RETURN_NONE;
}

static PyMemberDef STReverb_members[] =
{
    {"server", T_OBJECT_EX, offsetof(STReverb, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(STReverb, stream), 0, "Stream object."},
    {"input", T_OBJECT_EX, offsetof(STReverb, input), 0, "Input sound object."},
    {"inpos", T_OBJECT_EX, offsetof(STReverb, inpos), 0, "Position left-right of the source."},
    {"revtime", T_OBJECT_EX, offsetof(STReverb, revtime), 0, "Reverb duration value."},
    {"cutoff", T_OBJECT_EX, offsetof(STReverb, cutoff), 0, "STReverb lowpass filter cutoff."},
    {"mix", T_OBJECT_EX, offsetof(STReverb, mix), 0, "Balance between dry and wet signals."},
    {NULL}  /* Sentinel */
};

static PyMethodDef STReverb_methods[] =
{
    {"getServer", (PyCFunction)STReverb_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)STReverb_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)STReverb_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)STReverb_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"reset", (PyCFunction)STReverb_reset, METH_NOARGS, "Reset the delay line."},
    {"setInpos", (PyCFunction)STReverb_setInpos, METH_O, "Sets position of the source between 0 -> 1."},
    {"setRevtime", (PyCFunction)STReverb_setRevtime, METH_O, "Sets reverb duration in seconds."},
    {"setCutoff", (PyCFunction)STReverb_setCutoff, METH_O, "Sets lowpass filter cutoff."},
    {"setMix", (PyCFunction)STReverb_setMix, METH_O, "Sets balance between dry and wet signals."},
    {"setFirstRefGain", (PyCFunction)STReverb_setFirstRefGain, METH_O, "Sets gain of the first reflexions."},
    {"setRoomSize", (PyCFunction)STReverb_setRoomSize, METH_O, "Sets room size scaler."},
    {NULL}  /* Sentinel */
};

PyTypeObject STReverbType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.STReverb_base",         /*tp_name*/
    sizeof(STReverb),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)STReverb_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_as_async (tp_compare in Python 2)*/
    0,                         /*tp_repr*/
    0,             /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
    "STReverb objects. Waveguide-based reverberation network.",           /* tp_doc */
    (traverseproc)STReverb_traverse,   /* tp_traverse */
    (inquiry)STReverb_clear,           /* tp_clear */
    0,                     /* tp_richcompare */
    0,                     /* tp_weaklistoffset */
    0,                     /* tp_iter */
    0,                     /* tp_iternext */
    STReverb_methods,             /* tp_methods */
    STReverb_members,             /* tp_members */
    0,                      /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,      /* tp_init */
    0,                         /* tp_alloc */
    STReverb_new,                 /* tp_new */
};

/************************************************************************************************/
/* STReverb streamer object */
/************************************************************************************************/
typedef struct
{
    pyo_audio_HEAD
    STReverb *mainSplitter;
    int modebuffer[2];
    int chnl; // STRev order
} STRev;

static void STRev_postprocessing_ii(STRev *self) { POST_PROCESSING_II };
static void STRev_postprocessing_ai(STRev *self) { POST_PROCESSING_AI };
static void STRev_postprocessing_ia(STRev *self) { POST_PROCESSING_IA };
static void STRev_postprocessing_aa(STRev *self) { POST_PROCESSING_AA };
static void STRev_postprocessing_ireva(STRev *self) { POST_PROCESSING_IREVA };
static void STRev_postprocessing_areva(STRev *self) { POST_PROCESSING_AREVA };
static void STRev_postprocessing_revai(STRev *self) { POST_PROCESSING_REVAI };
static void STRev_postprocessing_revaa(STRev *self) { POST_PROCESSING_REVAA };
static void STRev_postprocessing_revareva(STRev *self) { POST_PROCESSING_REVAREVA };

static void
STRev_setProcMode(STRev *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;

    switch (muladdmode)
    {
        case 0:
            self->muladd_func_ptr = STRev_postprocessing_ii;
            break;

        case 1:
            self->muladd_func_ptr = STRev_postprocessing_ai;
            break;

        case 2:
            self->muladd_func_ptr = STRev_postprocessing_revai;
            break;

        case 10:
            self->muladd_func_ptr = STRev_postprocessing_ia;
            break;

        case 11:
            self->muladd_func_ptr = STRev_postprocessing_aa;
            break;

        case 12:
            self->muladd_func_ptr = STRev_postprocessing_revaa;
            break;

        case 20:
            self->muladd_func_ptr = STRev_postprocessing_ireva;
            break;

        case 21:
            self->muladd_func_ptr = STRev_postprocessing_areva;
            break;

        case 22:
            self->muladd_func_ptr = STRev_postprocessing_revareva;
            break;
    }
}

static void
STRev_compute_next_data_frame(STRev *self)
{
    int i;
    MYFLT *tmp;
    int offset = self->chnl * self->bufsize;
    tmp = STReverb_getSamplesBuffer((STReverb *)self->mainSplitter);

    for (i = 0; i < self->bufsize; i++)
    {
        self->data[i] = tmp[i + offset];
    }

    (*self->muladd_func_ptr)(self);
}

static int
STRev_traverse(STRev *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->mainSplitter);
    return 0;
}

static int
STRev_clear(STRev *self)
{
    pyo_CLEAR
    Py_CLEAR(self->mainSplitter);
    return 0;
}

static void
STRev_dealloc(STRev* self)
{
    pyo_DEALLOC
    STRev_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
STRev_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *maintmp = NULL, *multmp = NULL, *addtmp = NULL;
    STRev *self;
    self = (STRev *)type->tp_alloc(type, 0);

    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, STRev_compute_next_data_frame);
    self->mode_func_ptr = STRev_setProcMode;

    static char *kwlist[] = {"mainSplitter", "chnl", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "Oi|OO", kwlist, &maintmp, &self->chnl, &multmp, &addtmp))
        Py_RETURN_NONE;

    self->mainSplitter = (STReverb *)maintmp;
    Py_INCREF(self->mainSplitter);

    if (multmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMul", "O", multmp);
    }

    if (addtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setAdd", "O", addtmp);
    }

    PyObject_CallMethod(self->server, "addStream", "O", self->stream);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * STRev_getServer(STRev* self) { GET_SERVER };
static PyObject * STRev_getStream(STRev* self) { GET_STREAM };
static PyObject * STRev_setMul(STRev *self, PyObject *arg) { SET_MUL };
static PyObject * STRev_setAdd(STRev *self, PyObject *arg) { SET_ADD };
static PyObject * STRev_setSub(STRev *self, PyObject *arg) { SET_SUB };
static PyObject * STRev_setDiv(STRev *self, PyObject *arg) { SET_DIV };

static PyObject * STRev_play(STRev *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * STRev_out(STRev *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * STRev_stop(STRev *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * STRev_multiply(STRev *self, PyObject *arg) { MULTIPLY };
static PyObject * STRev_inplace_multiply(STRev *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * STRev_add(STRev *self, PyObject *arg) { ADD };
static PyObject * STRev_inplace_add(STRev *self, PyObject *arg) { INPLACE_ADD };
static PyObject * STRev_sub(STRev *self, PyObject *arg) { SUB };
static PyObject * STRev_inplace_sub(STRev *self, PyObject *arg) { INPLACE_SUB };
static PyObject * STRev_div(STRev *self, PyObject *arg) { DIV };
static PyObject * STRev_inplace_div(STRev *self, PyObject *arg) { INPLACE_DIV };

static PyMemberDef STRev_members[] =
{
    {"server", T_OBJECT_EX, offsetof(STRev, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(STRev, stream), 0, "Stream object."},
    {"mul", T_OBJECT_EX, offsetof(STRev, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(STRev, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef STRev_methods[] =
{
    {"getServer", (PyCFunction)STRev_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)STRev_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)STRev_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)STRev_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)STRev_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setMul", (PyCFunction)STRev_setMul, METH_O, "Sets STRev mul factor."},
    {"setAdd", (PyCFunction)STRev_setAdd, METH_O, "Sets STRev add factor."},
    {"setSub", (PyCFunction)STRev_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)STRev_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods STRev_as_number =
{
    (binaryfunc)STRev_add,                      /*nb_add*/
    (binaryfunc)STRev_sub,                 /*nb_subtract*/
    (binaryfunc)STRev_multiply,                 /*nb_multiply*/
    0,                /*nb_remainder*/
    0,                   /*nb_divmod*/
    0,                   /*nb_power*/
    0,                  /*nb_neg*/
    0,                /*nb_pos*/
    0,                  /*(unaryfunc)array_abs,*/
    0,                    /*nb_nonzero*/
    0,                    /*nb_invert*/
    0,               /*nb_lshift*/
    0,              /*nb_rshift*/
    0,              /*nb_and*/
    0,              /*nb_xor*/
    0,               /*nb_or*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    (binaryfunc)STRev_inplace_add,              /*inplace_add*/
    (binaryfunc)STRev_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)STRev_inplace_multiply,         /*inplace_multiply*/
    0,        /*inplace_remainder*/
    0,           /*inplace_power*/
    0,       /*inplace_lshift*/
    0,      /*inplace_rshift*/
    0,      /*inplace_and*/
    0,      /*inplace_xor*/
    0,       /*inplace_or*/
    0,             /*nb_floor_divide*/
    (binaryfunc)STRev_div,                       /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    (binaryfunc)STRev_inplace_div,                       /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject STRevType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.STRev_base",         /*tp_name*/
    sizeof(STRev),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)STRev_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_as_async (tp_compare in Python 2)*/
    0,                         /*tp_repr*/
    &STRev_as_number,             /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,  /*tp_flags*/
    "STRev objects. Reads one channel from a STReverb object.",           /* tp_doc */
    (traverseproc)STRev_traverse,   /* tp_traverse */
    (inquiry)STRev_clear,           /* tp_clear */
    0,                     /* tp_richcompare */
    0,                     /* tp_weaklistoffset */
    0,                     /* tp_iter */
    0,                     /* tp_iternext */
    STRev_methods,             /* tp_methods */
    STRev_members,             /* tp_members */
    0,                      /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,      /* tp_init */
    0,                         /* tp_alloc */
    STRev_new,                 /* tp_new */
};