/**************************************************************************
 * 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 <math.h>
#include "structmember.h"
#include "pyomodule.h"
#include "streammodule.h"
#include "servermodule.h"
#include "dummymodule.h"

typedef struct
{
    pyo_audio_HEAD
    PyObject *min;
    PyObject *max;
    PyObject *freq;
    Stream *min_stream;
    Stream *max_stream;
    Stream *freq_stream;
    MYFLT value;
    MYFLT oldValue;
    MYFLT diff;
    MYFLT time;
    int modebuffer[5]; // need at least 2 slots for mul & add
} Randi;

static void
Randi_generate_iii(Randi *self)
{
    int i;
    MYFLT inc;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    MYFLT range = ma - mi;
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi;
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_aii(Randi *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        range = ma - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi[i];
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_iai(Randi *self)
{
    int i;
    MYFLT inc, range;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        range = ma[i] - mi;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi;
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_aai(Randi *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        range = ma[i] - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi[i];
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_iia(Randi *self)
{
    int i;
    MYFLT inc;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);
    MYFLT range = ma - mi;

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi;
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_aia(Randi *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        range = ma - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi[i];
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_iaa(Randi *self)
{
    int i;
    MYFLT inc, range;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        range = ma[i] - mi;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi;
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void
Randi_generate_aaa(Randi *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        range = ma[i] - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->oldValue = self->value;
            self->value = range * RANDOM_UNIFORM + mi[i];
            self->diff = self->value - self->oldValue;
        }

        self->data[i] = self->oldValue + self->diff * self->time;
    }
}

static void Randi_postprocessing_ii(Randi *self) { POST_PROCESSING_II };
static void Randi_postprocessing_ai(Randi *self) { POST_PROCESSING_AI };
static void Randi_postprocessing_ia(Randi *self) { POST_PROCESSING_IA };
static void Randi_postprocessing_aa(Randi *self) { POST_PROCESSING_AA };
static void Randi_postprocessing_ireva(Randi *self) { POST_PROCESSING_IREVA };
static void Randi_postprocessing_areva(Randi *self) { POST_PROCESSING_AREVA };
static void Randi_postprocessing_revai(Randi *self) { POST_PROCESSING_REVAI };
static void Randi_postprocessing_revaa(Randi *self) { POST_PROCESSING_REVAA };
static void Randi_postprocessing_revareva(Randi *self) { POST_PROCESSING_REVAREVA };

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

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

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

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

        case 11:
            self->proc_func_ptr = Randi_generate_aai;
            break;

        case 100:
            self->proc_func_ptr = Randi_generate_iia;
            break;

        case 101:
            self->proc_func_ptr = Randi_generate_aia;
            break;

        case 110:
            self->proc_func_ptr = Randi_generate_iaa;
            break;

        case 111:
            self->proc_func_ptr = Randi_generate_aaa;
            break;
    }

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

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

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

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

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

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

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

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

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

static void
Randi_compute_next_data_frame(Randi *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
Randi_traverse(Randi *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    Py_VISIT(self->min);
    Py_VISIT(self->max);
    return 0;
}

static int
Randi_clear(Randi *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    Py_CLEAR(self->min);
    Py_CLEAR(self->max);
    return 0;
}

static void
Randi_dealloc(Randi* self)
{
    pyo_DEALLOC
    Randi_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
Randi_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    MYFLT mi, ma;
    PyObject *mintmp = NULL, *maxtmp = NULL, *freqtmp = NULL, *multmp = NULL, *addtmp = NULL;
    Randi *self;
    self = (Randi *)type->tp_alloc(type, 0);

    self->min = PyFloat_FromDouble(0.);
    self->max = PyFloat_FromDouble(1.);
    self->freq = PyFloat_FromDouble(1.);
    self->value = self->oldValue = self->diff = 0.0;
    self->time = 1.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, Randi_compute_next_data_frame);
    self->mode_func_ptr = Randi_setProcMode;

    static char *kwlist[] = {"min", "max", "freq", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOO", kwlist, &mintmp, &maxtmp, &freqtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (mintmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMin", "O", mintmp);
    }

    if (maxtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMax", "O", maxtmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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);

    Server_generateSeed((Server *)self->server, RANDI_ID);

    if (self->modebuffer[2] == 0)
        mi = PyFloat_AS_DOUBLE(self->min);
    else
        mi = Stream_getData((Stream *)self->min_stream)[0];

    if (self->modebuffer[3] == 0)
        ma = PyFloat_AS_DOUBLE(self->max);
    else
        ma = Stream_getData((Stream *)self->max_stream)[0];

    self->value = self->oldValue = (mi + ma) * 0.5;

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * Randi_getServer(Randi* self) { GET_SERVER };
static PyObject * Randi_getStream(Randi* self) { GET_STREAM };
static PyObject * Randi_setMul(Randi *self, PyObject *arg) { SET_MUL };
static PyObject * Randi_setAdd(Randi *self, PyObject *arg) { SET_ADD };
static PyObject * Randi_setSub(Randi *self, PyObject *arg) { SET_SUB };
static PyObject * Randi_setDiv(Randi *self, PyObject *arg) { SET_DIV };

static PyObject * Randi_play(Randi *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Randi_out(Randi *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * Randi_stop(Randi *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * Randi_multiply(Randi *self, PyObject *arg) { MULTIPLY };
static PyObject * Randi_inplace_multiply(Randi *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Randi_add(Randi *self, PyObject *arg) { ADD };
static PyObject * Randi_inplace_add(Randi *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Randi_sub(Randi *self, PyObject *arg) { SUB };
static PyObject * Randi_inplace_sub(Randi *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Randi_div(Randi *self, PyObject *arg) { DIV };
static PyObject * Randi_inplace_div(Randi *self, PyObject *arg) { INPLACE_DIV };

static PyObject * Randi_setMin(Randi *self, PyObject *arg) { SET_PARAM(self->min, self->min_stream, 2); }
static PyObject * Randi_setMax(Randi *self, PyObject *arg) { SET_PARAM(self->max, self->max_stream, 3); }
static PyObject * Randi_setFreq(Randi *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 4); }

static PyMemberDef Randi_members[] =
{
    {"server", T_OBJECT_EX, offsetof(Randi, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(Randi, stream), 0, "Stream object."},
    {"min", T_OBJECT_EX, offsetof(Randi, min), 0, "Minimum possible value."},
    {"max", T_OBJECT_EX, offsetof(Randi, max), 0, "Maximum possible value."},
    {"freq", T_OBJECT_EX, offsetof(Randi, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(Randi, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(Randi, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef Randi_methods[] =
{
    {"getServer", (PyCFunction)Randi_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Randi_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Randi_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)Randi_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)Randi_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setMin", (PyCFunction)Randi_setMin, METH_O, "Sets minimum possible value."},
    {"setMax", (PyCFunction)Randi_setMax, METH_O, "Sets maximum possible value."},
    {"setFreq", (PyCFunction)Randi_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)Randi_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)Randi_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Randi_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Randi_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Randi_as_number =
{
    (binaryfunc)Randi_add,                         /*nb_add*/
    (binaryfunc)Randi_sub,                         /*nb_subtract*/
    (binaryfunc)Randi_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)Randi_inplace_add,                 /*inplace_add*/
    (binaryfunc)Randi_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)Randi_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)Randi_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)Randi_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject RandiType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.Randi_base",                                   /*tp_name*/
    sizeof(Randi),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)Randi_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &Randi_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*/
    "Randi objects. Periodically generates a new random value with interpolation.",           /* tp_doc */
    (traverseproc)Randi_traverse,                  /* tp_traverse */
    (inquiry)Randi_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    Randi_methods,                                 /* tp_methods */
    Randi_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 */
    Randi_new,                                     /* tp_new */
};

/****************/
/**** Randh *****/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *min;
    PyObject *max;
    PyObject *freq;
    Stream *min_stream;
    Stream *max_stream;
    Stream *freq_stream;
    MYFLT value;
    MYFLT time;
    int modebuffer[5]; // need at least 2 slots for mul & add
} Randh;

static void
Randh_generate_iii(Randh *self)
{
    int i;
    MYFLT inc;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    MYFLT range = ma - mi;
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi;
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_aii(Randh *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        range = ma - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi[i];
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_iai(Randh *self)
{
    int i;
    MYFLT inc, range;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        range = ma[i] - mi;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi;
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_aai(Randh *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        range = ma[i] - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi[i];
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_iia(Randh *self)
{
    int i;
    MYFLT inc;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);
    MYFLT range = ma - mi;

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi;
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_aia(Randh *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        range = ma - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi[i];
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_iaa(Randh *self)
{
    int i;
    MYFLT inc, range;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        range = ma[i] - mi;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi;
        }

        self->data[i] = self->value;
    }
}

static void
Randh_generate_aaa(Randh *self)
{
    int i;
    MYFLT inc, range;
    MYFLT *mi = Stream_getData((Stream *)self->min_stream);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        range = ma[i] - mi[i];
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = range * RANDOM_UNIFORM + mi[i];
        }

        self->data[i] = self->value;
    }
}

static void Randh_postprocessing_ii(Randh *self) { POST_PROCESSING_II };
static void Randh_postprocessing_ai(Randh *self) { POST_PROCESSING_AI };
static void Randh_postprocessing_ia(Randh *self) { POST_PROCESSING_IA };
static void Randh_postprocessing_aa(Randh *self) { POST_PROCESSING_AA };
static void Randh_postprocessing_ireva(Randh *self) { POST_PROCESSING_IREVA };
static void Randh_postprocessing_areva(Randh *self) { POST_PROCESSING_AREVA };
static void Randh_postprocessing_revai(Randh *self) { POST_PROCESSING_REVAI };
static void Randh_postprocessing_revaa(Randh *self) { POST_PROCESSING_REVAA };
static void Randh_postprocessing_revareva(Randh *self) { POST_PROCESSING_REVAREVA };

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

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

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

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

        case 11:
            self->proc_func_ptr = Randh_generate_aai;
            break;

        case 100:
            self->proc_func_ptr = Randh_generate_iia;
            break;

        case 101:
            self->proc_func_ptr = Randh_generate_aia;
            break;

        case 110:
            self->proc_func_ptr = Randh_generate_iaa;
            break;

        case 111:
            self->proc_func_ptr = Randh_generate_aaa;
            break;
    }

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

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

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

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

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

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

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

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

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

static void
Randh_compute_next_data_frame(Randh *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
Randh_traverse(Randh *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    Py_VISIT(self->min);
    Py_VISIT(self->max);
    return 0;
}

static int
Randh_clear(Randh *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    Py_CLEAR(self->min);
    Py_CLEAR(self->max);
    return 0;
}

static void
Randh_dealloc(Randh* self)
{
    pyo_DEALLOC
    Randh_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
Randh_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    MYFLT mi, ma;
    PyObject *mintmp = NULL, *maxtmp = NULL, *freqtmp = NULL, *multmp = NULL, *addtmp = NULL;
    Randh *self;
    self = (Randh *)type->tp_alloc(type, 0);

    self->min = PyFloat_FromDouble(0.);
    self->max = PyFloat_FromDouble(1.);
    self->freq = PyFloat_FromDouble(1.);
    self->value = 0.0;
    self->time = 1.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, Randh_compute_next_data_frame);
    self->mode_func_ptr = Randh_setProcMode;

    static char *kwlist[] = {"min", "max", "freq", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOO", kwlist, &mintmp, &maxtmp, &freqtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (mintmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMin", "O", mintmp);
    }

    if (maxtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMax", "O", maxtmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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);

    Server_generateSeed((Server *)self->server, RANDH_ID);

    if (self->modebuffer[2] == 0)
        mi = PyFloat_AS_DOUBLE(self->min);
    else
        mi = Stream_getData((Stream *)self->min_stream)[0];

    if (self->modebuffer[3] == 0)
        ma = PyFloat_AS_DOUBLE(self->max);
    else
        ma = Stream_getData((Stream *)self->max_stream)[0];

    self->value = (mi + ma) * 0.5;

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * Randh_getServer(Randh* self) { GET_SERVER };
static PyObject * Randh_getStream(Randh* self) { GET_STREAM };
static PyObject * Randh_setMul(Randh *self, PyObject *arg) { SET_MUL };
static PyObject * Randh_setAdd(Randh *self, PyObject *arg) { SET_ADD };
static PyObject * Randh_setSub(Randh *self, PyObject *arg) { SET_SUB };
static PyObject * Randh_setDiv(Randh *self, PyObject *arg) { SET_DIV };

static PyObject * Randh_play(Randh *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Randh_out(Randh *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * Randh_stop(Randh *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * Randh_multiply(Randh *self, PyObject *arg) { MULTIPLY };
static PyObject * Randh_inplace_multiply(Randh *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Randh_add(Randh *self, PyObject *arg) { ADD };
static PyObject * Randh_inplace_add(Randh *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Randh_sub(Randh *self, PyObject *arg) { SUB };
static PyObject * Randh_inplace_sub(Randh *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Randh_div(Randh *self, PyObject *arg) { DIV };
static PyObject * Randh_inplace_div(Randh *self, PyObject *arg) { INPLACE_DIV };

static PyObject * Randh_setMin(Randh *self, PyObject *arg) { SET_PARAM(self->min, self->min_stream, 2); }
static PyObject * Randh_setMax(Randh *self, PyObject *arg) { SET_PARAM(self->max, self->max_stream, 3); }
static PyObject * Randh_setFreq(Randh *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 4); }

static PyMemberDef Randh_members[] =
{
    {"server", T_OBJECT_EX, offsetof(Randh, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(Randh, stream), 0, "Stream object."},
    {"min", T_OBJECT_EX, offsetof(Randh, min), 0, "Minimum possible value."},
    {"max", T_OBJECT_EX, offsetof(Randh, max), 0, "Maximum possible value."},
    {"freq", T_OBJECT_EX, offsetof(Randh, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(Randh, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(Randh, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef Randh_methods[] =
{
    {"getServer", (PyCFunction)Randh_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Randh_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Randh_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)Randh_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)Randh_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setMin", (PyCFunction)Randh_setMin, METH_O, "Sets minimum possible value."},
    {"setMax", (PyCFunction)Randh_setMax, METH_O, "Sets maximum possible value."},
    {"setFreq", (PyCFunction)Randh_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)Randh_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)Randh_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Randh_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Randh_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Randh_as_number =
{
    (binaryfunc)Randh_add,                         /*nb_add*/
    (binaryfunc)Randh_sub,                         /*nb_subtract*/
    (binaryfunc)Randh_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)Randh_inplace_add,                 /*inplace_add*/
    (binaryfunc)Randh_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)Randh_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)Randh_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)Randh_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject RandhType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.Randh_base",                                   /*tp_name*/
    sizeof(Randh),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)Randh_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &Randh_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*/
    "Randh objects. Periodically generates a new random value.",           /* tp_doc */
    (traverseproc)Randh_traverse,                  /* tp_traverse */
    (inquiry)Randh_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    Randh_methods,                                 /* tp_methods */
    Randh_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 */
    Randh_new,                                     /* tp_new */
};

/****************/
/**** Choice *****/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *freq;
    Stream *freq_stream;
    int chSize;
    MYFLT *choice;
    MYFLT value;
    MYFLT time;
    int modebuffer[3]; // need at least 2 slots for mul & add
} Choice;

static void
Choice_generate_i(Choice *self)
{
    int i;
    MYFLT inc;
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = self->choice[(int)(RANDOM_UNIFORM * self->chSize)];
        }

        self->data[i] = self->value;
    }
}

static void
Choice_generate_a(Choice *self)
{
    int i;
    MYFLT inc;
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = self->choice[(int)(RANDOM_UNIFORM * self->chSize)];
        }

        self->data[i] = self->value;
    }
}

static void Choice_postprocessing_ii(Choice *self) { POST_PROCESSING_II };
static void Choice_postprocessing_ai(Choice *self) { POST_PROCESSING_AI };
static void Choice_postprocessing_ia(Choice *self) { POST_PROCESSING_IA };
static void Choice_postprocessing_aa(Choice *self) { POST_PROCESSING_AA };
static void Choice_postprocessing_ireva(Choice *self) { POST_PROCESSING_IREVA };
static void Choice_postprocessing_areva(Choice *self) { POST_PROCESSING_AREVA };
static void Choice_postprocessing_revai(Choice *self) { POST_PROCESSING_REVAI };
static void Choice_postprocessing_revaa(Choice *self) { POST_PROCESSING_REVAA };
static void Choice_postprocessing_revareva(Choice *self) { POST_PROCESSING_REVAREVA };

static void
Choice_setProcMode(Choice *self)
{
    int procmode, muladdmode;
    procmode = self->modebuffer[2];
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;

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

        case 1:
            self->proc_func_ptr = Choice_generate_a;
            break;
    }

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

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

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

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

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

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

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

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

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

static void
Choice_compute_next_data_frame(Choice *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
Choice_traverse(Choice *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    return 0;
}

static int
Choice_clear(Choice *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    return 0;
}

static void
Choice_dealloc(Choice* self)
{
    pyo_DEALLOC
    PyMem_RawFree(self->choice);
    Choice_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
Choice_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *choicetmp = NULL, *freqtmp = NULL, *multmp = NULL, *addtmp = NULL;
    Choice *self;
    self = (Choice *)type->tp_alloc(type, 0);

    self->freq = PyFloat_FromDouble(1.);
    self->value = 0.0;
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Choice_compute_next_data_frame);
    self->mode_func_ptr = Choice_setProcMode;

    static char *kwlist[] = {"choice", "freq", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, &choicetmp, &freqtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (choicetmp)
    {
        PyObject_CallMethod((PyObject *)self, "setChoice", "O", choicetmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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);

    Server_generateSeed((Server *)self->server, CHOICE_ID);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * Choice_getServer(Choice* self) { GET_SERVER };
static PyObject * Choice_getStream(Choice* self) { GET_STREAM };
static PyObject * Choice_setMul(Choice *self, PyObject *arg) { SET_MUL };
static PyObject * Choice_setAdd(Choice *self, PyObject *arg) { SET_ADD };
static PyObject * Choice_setSub(Choice *self, PyObject *arg) { SET_SUB };
static PyObject * Choice_setDiv(Choice *self, PyObject *arg) { SET_DIV };

static PyObject * Choice_play(Choice *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Choice_out(Choice *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * Choice_stop(Choice *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * Choice_multiply(Choice *self, PyObject *arg) { MULTIPLY };
static PyObject * Choice_inplace_multiply(Choice *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Choice_add(Choice *self, PyObject *arg) { ADD };
static PyObject * Choice_inplace_add(Choice *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Choice_sub(Choice *self, PyObject *arg) { SUB };
static PyObject * Choice_inplace_sub(Choice *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Choice_div(Choice *self, PyObject *arg) { DIV };
static PyObject * Choice_inplace_div(Choice *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Choice_setChoice(Choice *self, PyObject *arg)
{
    int i;

    if (! PyList_Check(arg))
    {
        PyErr_SetString(PyExc_TypeError, "The choice attribute must be a list.");
        Py_RETURN_NONE;
    }

    self->chSize = PyList_Size(arg);
    self->choice = (MYFLT *)PyMem_RawRealloc(self->choice, self->chSize * sizeof(MYFLT));

    for (i = 0; i < self->chSize; i++)
    {
        self->choice[i] = PyFloat_AsDouble(PyList_GET_ITEM(arg, i));
    }

    (*self->mode_func_ptr)(self);

    Py_RETURN_NONE;
}

static PyObject * Choice_setFreq(Choice *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 2); }

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

static PyMethodDef Choice_methods[] =
{
    {"getServer", (PyCFunction)Choice_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Choice_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Choice_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)Choice_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)Choice_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setChoice", (PyCFunction)Choice_setChoice, METH_O, "Sets list of possible floats."},
    {"setFreq", (PyCFunction)Choice_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)Choice_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)Choice_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Choice_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Choice_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Choice_as_number =
{
    (binaryfunc)Choice_add,                         /*nb_add*/
    (binaryfunc)Choice_sub,                         /*nb_subtract*/
    (binaryfunc)Choice_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)Choice_inplace_add,                 /*inplace_add*/
    (binaryfunc)Choice_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)Choice_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)Choice_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)Choice_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject ChoiceType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.Choice_base",                                   /*tp_name*/
    sizeof(Choice),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)Choice_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &Choice_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*/
    "Choice objects. Periodically generates a new random value from a list.",           /* tp_doc */
    (traverseproc)Choice_traverse,                  /* tp_traverse */
    (inquiry)Choice_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    Choice_methods,                                 /* tp_methods */
    Choice_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 */
    Choice_new,                                     /* tp_new */
};

/****************/
/**** RandInt ***/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *max;
    PyObject *freq;
    Stream *max_stream;
    Stream *freq_stream;
    MYFLT value;
    MYFLT time;
    int modebuffer[4]; // need at least 2 slots for mul & add
} RandInt;

static void
RandInt_generate_ii(RandInt *self)
{
    int i;
    MYFLT inc;
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (MYFLT)((int)(RANDOM_UNIFORM * ma));
        }

        self->data[i] = self->value;
    }
}

static void
RandInt_generate_ai(RandInt *self)
{
    int i;
    MYFLT inc;
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (MYFLT)((int)(RANDOM_UNIFORM * ma[i]));
        }

        self->data[i] = self->value;
    }
}

static void
RandInt_generate_ia(RandInt *self)
{
    int i;
    MYFLT inc;
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (MYFLT)((int)(RANDOM_UNIFORM * ma));
        }

        self->data[i] = self->value;
    }
}

static void
RandInt_generate_aa(RandInt *self)
{
    int i;
    MYFLT inc;
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (MYFLT)((int)(RANDOM_UNIFORM * ma[i]));
        }

        self->data[i] = self->value;
    }
}

static void RandInt_postprocessing_ii(RandInt *self) { POST_PROCESSING_II };
static void RandInt_postprocessing_ai(RandInt *self) { POST_PROCESSING_AI };
static void RandInt_postprocessing_ia(RandInt *self) { POST_PROCESSING_IA };
static void RandInt_postprocessing_aa(RandInt *self) { POST_PROCESSING_AA };
static void RandInt_postprocessing_ireva(RandInt *self) { POST_PROCESSING_IREVA };
static void RandInt_postprocessing_areva(RandInt *self) { POST_PROCESSING_AREVA };
static void RandInt_postprocessing_revai(RandInt *self) { POST_PROCESSING_REVAI };
static void RandInt_postprocessing_revaa(RandInt *self) { POST_PROCESSING_REVAA };
static void RandInt_postprocessing_revareva(RandInt *self) { POST_PROCESSING_REVAREVA };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static void
RandInt_compute_next_data_frame(RandInt *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
RandInt_traverse(RandInt *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    Py_VISIT(self->max);
    return 0;
}

static int
RandInt_clear(RandInt *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    Py_CLEAR(self->max);
    return 0;
}

static void
RandInt_dealloc(RandInt* self)
{
    pyo_DEALLOC
    RandInt_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
RandInt_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *maxtmp = NULL, *freqtmp = NULL, *multmp = NULL, *addtmp = NULL;
    RandInt *self;
    self = (RandInt *)type->tp_alloc(type, 0);

    self->max = PyFloat_FromDouble(100.);
    self->freq = PyFloat_FromDouble(1.);
    self->value = 0.0;
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, RandInt_compute_next_data_frame);
    self->mode_func_ptr = RandInt_setProcMode;

    static char *kwlist[] = {"max", "freq", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &maxtmp, &freqtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (maxtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMax", "O", maxtmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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);

    Server_generateSeed((Server *)self->server, RANDINT_ID);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * RandInt_getServer(RandInt* self) { GET_SERVER };
static PyObject * RandInt_getStream(RandInt* self) { GET_STREAM };
static PyObject * RandInt_setMul(RandInt *self, PyObject *arg) { SET_MUL };
static PyObject * RandInt_setAdd(RandInt *self, PyObject *arg) { SET_ADD };
static PyObject * RandInt_setSub(RandInt *self, PyObject *arg) { SET_SUB };
static PyObject * RandInt_setDiv(RandInt *self, PyObject *arg) { SET_DIV };

static PyObject * RandInt_play(RandInt *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * RandInt_out(RandInt *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * RandInt_stop(RandInt *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * RandInt_multiply(RandInt *self, PyObject *arg) { MULTIPLY };
static PyObject * RandInt_inplace_multiply(RandInt *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * RandInt_add(RandInt *self, PyObject *arg) { ADD };
static PyObject * RandInt_inplace_add(RandInt *self, PyObject *arg) { INPLACE_ADD };
static PyObject * RandInt_sub(RandInt *self, PyObject *arg) { SUB };
static PyObject * RandInt_inplace_sub(RandInt *self, PyObject *arg) { INPLACE_SUB };
static PyObject * RandInt_div(RandInt *self, PyObject *arg) { DIV };
static PyObject * RandInt_inplace_div(RandInt *self, PyObject *arg) { INPLACE_DIV };

static PyObject * RandInt_setMax(RandInt *self, PyObject *arg) { SET_PARAM(self->max, self->max_stream, 2); }
static PyObject * RandInt_setFreq(RandInt *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 3); }

static PyMemberDef RandInt_members[] =
{
    {"server", T_OBJECT_EX, offsetof(RandInt, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(RandInt, stream), 0, "Stream object."},
    {"max", T_OBJECT_EX, offsetof(RandInt, max), 0, "Maximum possible value."},
    {"freq", T_OBJECT_EX, offsetof(RandInt, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(RandInt, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(RandInt, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef RandInt_methods[] =
{
    {"getServer", (PyCFunction)RandInt_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)RandInt_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)RandInt_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)RandInt_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)RandInt_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setMax", (PyCFunction)RandInt_setMax, METH_O, "Sets maximum possible value."},
    {"setFreq", (PyCFunction)RandInt_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)RandInt_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)RandInt_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)RandInt_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)RandInt_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods RandInt_as_number =
{
    (binaryfunc)RandInt_add,                         /*nb_add*/
    (binaryfunc)RandInt_sub,                         /*nb_subtract*/
    (binaryfunc)RandInt_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)RandInt_inplace_add,                 /*inplace_add*/
    (binaryfunc)RandInt_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)RandInt_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)RandInt_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)RandInt_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject RandIntType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.RandInt_base",                                   /*tp_name*/
    sizeof(RandInt),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)RandInt_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &RandInt_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*/
    "RandInt objects. Periodically generates a new integer random value.",           /* tp_doc */
    (traverseproc)RandInt_traverse,                  /* tp_traverse */
    (inquiry)RandInt_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    RandInt_methods,                                 /* tp_methods */
    RandInt_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 */
    RandInt_new,                                     /* tp_new */
};

/*****************/
/**** RandDur ****/
/*****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *min;
    PyObject *max;
    Stream *min_stream;
    Stream *max_stream;
    MYFLT value;
    MYFLT time;
    MYFLT inc;
    int modebuffer[4]; // need at least 2 slots for mul & add
} RandDur;

static void
RandDur_generate_ii(RandDur *self)
{
    int i;
    MYFLT range;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += self->inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;

            if (mi < 0.0)
                mi = 0.0;

            range = ma - mi;

            if (range < 0.0)
                range = 0.0;

            self->value = range * RANDOM_UNIFORM + mi;
            self->inc = (1.0 / self->value) / self->sr;
        }

        self->data[i] = self->value;
    }
}

static void
RandDur_generate_ai(RandDur *self)
{
    int i;
    MYFLT range, mi;
    MYFLT *min = Stream_getData((Stream *)self->min_stream);
    MYFLT ma = PyFloat_AS_DOUBLE(self->max);

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += self->inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            mi = min[i];

            if (mi < 0.0)
                mi = 0.0;

            range = ma - mi;

            if (range < 0.0)
                range = 0.0;

            self->value = range * RANDOM_UNIFORM + mi;
            self->inc = (1.0 / self->value) / self->sr;
        }

        self->data[i] = self->value;
    }
}

static void
RandDur_generate_ia(RandDur *self)
{
    int i;
    MYFLT range;
    MYFLT mi = PyFloat_AS_DOUBLE(self->min);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += self->inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;

            if (mi < 0.0)
                mi = 0.0;

            range = ma[i] - mi;

            if (range < 0.0)
                range = 0.0;

            self->value = range * RANDOM_UNIFORM + mi;
            self->inc = (1.0 / self->value) / self->sr;
        }

        self->data[i] = self->value;
    }
}

static void
RandDur_generate_aa(RandDur *self)
{
    int i;
    MYFLT range, mi;
    MYFLT *min = Stream_getData((Stream *)self->min_stream);
    MYFLT *ma = Stream_getData((Stream *)self->max_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += self->inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            mi = min[i];

            if (mi < 0.0)
                mi = 0.0;

            range = ma[i] - mi;

            if (range < 0.0)
                range = 0.0;

            self->value = range * RANDOM_UNIFORM + mi;
            self->inc = (1.0 / self->value) / self->sr;
        }

        self->data[i] = self->value;
    }
}

static void RandDur_postprocessing_ii(RandDur *self) { POST_PROCESSING_II };
static void RandDur_postprocessing_ai(RandDur *self) { POST_PROCESSING_AI };
static void RandDur_postprocessing_ia(RandDur *self) { POST_PROCESSING_IA };
static void RandDur_postprocessing_aa(RandDur *self) { POST_PROCESSING_AA };
static void RandDur_postprocessing_ireva(RandDur *self) { POST_PROCESSING_IREVA };
static void RandDur_postprocessing_areva(RandDur *self) { POST_PROCESSING_AREVA };
static void RandDur_postprocessing_revai(RandDur *self) { POST_PROCESSING_REVAI };
static void RandDur_postprocessing_revaa(RandDur *self) { POST_PROCESSING_REVAA };
static void RandDur_postprocessing_revareva(RandDur *self) { POST_PROCESSING_REVAREVA };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static void
RandDur_compute_next_data_frame(RandDur *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
RandDur_traverse(RandDur *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->min);
    Py_VISIT(self->max);
    return 0;
}

static int
RandDur_clear(RandDur *self)
{
    pyo_CLEAR
    Py_CLEAR(self->min);
    Py_CLEAR(self->max);
    return 0;
}

static void
RandDur_dealloc(RandDur* self)
{
    pyo_DEALLOC
    RandDur_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
RandDur_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    MYFLT mi, ma;
    PyObject *mintmp = NULL, *maxtmp = NULL, *multmp = NULL, *addtmp = NULL;
    RandDur *self;
    self = (RandDur *)type->tp_alloc(type, 0);

    self->min = PyFloat_FromDouble(0.01);
    self->max = PyFloat_FromDouble(1.);
    self->value = self->inc = 0.0;
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, RandDur_compute_next_data_frame);
    self->mode_func_ptr = RandDur_setProcMode;

    static char *kwlist[] = {"min", "max", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &mintmp, &maxtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (mintmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMin", "O", mintmp);
    }

    if (maxtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMax", "O", maxtmp);
    }

    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);

    Server_generateSeed((Server *)self->server, RANDDUR_ID);

    if (self->modebuffer[2] == 0)
        mi = PyFloat_AS_DOUBLE(self->min);
    else
        mi = Stream_getData((Stream *)self->min_stream)[0];

    if (self->modebuffer[3] == 0)
        ma = PyFloat_AS_DOUBLE(self->max);
    else
        ma = Stream_getData((Stream *)self->max_stream)[0];

    self->value = (mi + ma) * 0.5;

    if (self->value == 0.0)
        self->inc = 0.0;
    else
        self->inc = (1.0 / self->value) / self->sr;

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * RandDur_getServer(RandDur* self) { GET_SERVER };
static PyObject * RandDur_getStream(RandDur* self) { GET_STREAM };
static PyObject * RandDur_setMul(RandDur *self, PyObject *arg) { SET_MUL };
static PyObject * RandDur_setAdd(RandDur *self, PyObject *arg) { SET_ADD };
static PyObject * RandDur_setSub(RandDur *self, PyObject *arg) { SET_SUB };
static PyObject * RandDur_setDiv(RandDur *self, PyObject *arg) { SET_DIV };

static PyObject * RandDur_play(RandDur *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * RandDur_out(RandDur *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * RandDur_stop(RandDur *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * RandDur_multiply(RandDur *self, PyObject *arg) { MULTIPLY };
static PyObject * RandDur_inplace_multiply(RandDur *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * RandDur_add(RandDur *self, PyObject *arg) { ADD };
static PyObject * RandDur_inplace_add(RandDur *self, PyObject *arg) { INPLACE_ADD };
static PyObject * RandDur_sub(RandDur *self, PyObject *arg) { SUB };
static PyObject * RandDur_inplace_sub(RandDur *self, PyObject *arg) { INPLACE_SUB };
static PyObject * RandDur_div(RandDur *self, PyObject *arg) { DIV };
static PyObject * RandDur_inplace_div(RandDur *self, PyObject *arg) { INPLACE_DIV };

static PyObject * RandDur_setMin(RandDur *self, PyObject *arg) { SET_PARAM(self->min, self->min_stream, 2); }
static PyObject * RandDur_setMax(RandDur *self, PyObject *arg) { SET_PARAM(self->max, self->max_stream, 3); }

static PyMemberDef RandDur_members[] =
{
    {"server", T_OBJECT_EX, offsetof(RandDur, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(RandDur, stream), 0, "Stream object."},
    {"min", T_OBJECT_EX, offsetof(RandDur, min), 0, "Minimum possible value."},
    {"max", T_OBJECT_EX, offsetof(RandDur, max), 0, "Maximum possible value."},
    {"mul", T_OBJECT_EX, offsetof(RandDur, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(RandDur, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef RandDur_methods[] =
{
    {"getServer", (PyCFunction)RandDur_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)RandDur_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)RandDur_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)RandDur_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)RandDur_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setMin", (PyCFunction)RandDur_setMin, METH_O, "Sets minimum possible value."},
    {"setMax", (PyCFunction)RandDur_setMax, METH_O, "Sets maximum possible value."},
    {"setMul", (PyCFunction)RandDur_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)RandDur_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)RandDur_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)RandDur_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods RandDur_as_number =
{
    (binaryfunc)RandDur_add,                         /*nb_add*/
    (binaryfunc)RandDur_sub,                         /*nb_subtract*/
    (binaryfunc)RandDur_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)RandDur_inplace_add,                 /*inplace_add*/
    (binaryfunc)RandDur_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)RandDur_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)RandDur_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)RandDur_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject RandDurType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.RandDur_base",                                   /*tp_name*/
    sizeof(RandDur),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)RandDur_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &RandDur_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*/
    "RandDur objects. Recursive time varying generation of random values.",           /* tp_doc */
    (traverseproc)RandDur_traverse,                  /* tp_traverse */
    (inquiry)RandDur_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    RandDur_methods,                                 /* tp_methods */
    RandDur_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 */
    RandDur_new,                                     /* tp_new */
};

/****************/
/**** Xnoise *****/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *x1;
    PyObject *x2;
    PyObject *freq;
    Stream *x1_stream;
    Stream *x2_stream;
    Stream *freq_stream;
    MYFLT (*type_func_ptr)();
    MYFLT xx1;
    MYFLT xx2;
    int type;
    MYFLT value;
    MYFLT time;
    MYFLT lastPoissonX1;
    int poisson_tab;
    MYFLT poisson_buffer[2000];
    MYFLT walkerValue;
    MYFLT loop_buffer[15];
    int loopChoice;
    int loopCountPlay;
    int loopTime;
    int loopCountRec;
    int loopLen;
    int loopStop;
    int modebuffer[5]; // need at least 2 slots for mul & add
} Xnoise;

// no parameter
static MYFLT
Xnoise_uniform(Xnoise *self)
{
    return RANDOM_UNIFORM;
}

static MYFLT
Xnoise_linear_min(Xnoise *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;

    if (a < b) return a;
    else return b;
}

static MYFLT
Xnoise_linear_max(Xnoise *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;

    if (a > b) return a;
    else return b;
}

static MYFLT
Xnoise_triangle(Xnoise *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;
    return ((a + b) * 0.5);
}

// x1 = slope
static MYFLT
Xnoise_expon_min(Xnoise *self)
{
    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT val = -MYLOG(RANDOM_UNIFORM) / self->xx1;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

static MYFLT
Xnoise_expon_max(Xnoise *self)
{
    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT val = 1.0 - (-MYLOG(RANDOM_UNIFORM) / self->xx1);

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = bandwidth
static MYFLT
Xnoise_biexpon(Xnoise *self)
{
    MYFLT polar, val;

    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT sum = RANDOM_UNIFORM * 2.0;

    if (sum > 1.0)
    {
        polar = -1;
        sum = 2.0 - sum;
    }
    else
        polar = 1;

    val = 0.5 * (polar * MYLOG(sum) / self->xx1) + 0.5;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

static MYFLT
Xnoise_cauchy(Xnoise *self)
{
    MYFLT rnd, val, dir;

    do
    {
        rnd = RANDOM_UNIFORM;
    }
    while (rnd == 0.5);

    if (pyorand() < (PYO_RAND_MAX / 2))
        dir = -1;
    else
        dir = 1;

    val = 0.5 * (MYTAN(rnd) * self->xx1 * dir) + 0.5;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = locator, x2 = shape
static MYFLT
Xnoise_weibull(Xnoise *self)
{
    MYFLT rnd, val;

    if (self->xx2 <= 0.0) self->xx2 = 0.00001;

    rnd = 1.0 / (1.0 - RANDOM_UNIFORM);
    val = self->xx1 * MYPOW(MYLOG(rnd), (1.0 / self->xx2));

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = locator, x2 = bandwidth
static MYFLT
Xnoise_gaussian(Xnoise *self)
{
    MYFLT rnd, val;

    rnd = (RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM);
    val = (self->xx2 * (rnd - 3.0) * 0.33 + self->xx1);

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = gravity center, x2 = compress/expand
static MYFLT
Xnoise_poisson(Xnoise *self)
{
    int i, j, factorial;
    long tot;
    MYFLT val;

    if (self->xx1 < 0.1) self->xx1 = 0.1;

    if (self->xx2 < 0.1) self->xx2 = 0.1;

    if (self->xx1 != self->lastPoissonX1)
    {
        self->lastPoissonX1 = self->xx1;
        self->poisson_tab = 0;
        factorial = 1;

        for (i = 1; i < 12; i++)
        {
            factorial *= i;
            tot = (long)(1000.0 * (MYPOW(2.7182818, -self->xx1) * MYPOW(self->xx1, i) / factorial));

            for (j = 0; j < tot; j++)
            {
                self->poisson_buffer[self->poisson_tab] = i;
                self->poisson_tab++;
            }
        }
    }

    val = self->poisson_buffer[pyorand() % self->poisson_tab] / 12.0 * self->xx2;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = max value, x2 = max step
static MYFLT
Xnoise_walker(Xnoise *self)
{
    unsigned int modulo, dir;

    if (self->xx2 < 0.002) self->xx2 = 0.002;

    modulo = (unsigned int)(self->xx2 * 1000.0);
    dir = pyorand() % 100;

    if (dir < 50)
        self->walkerValue += ((MYFLT)(pyorand() % modulo) * 0.001);
    else
        self->walkerValue -= ((MYFLT)(pyorand() % modulo) * 0.001);

    if (self->walkerValue > self->xx1)
        self->walkerValue = self->xx1;
    else if (self->walkerValue < 0.0)
        self->walkerValue = 0.0;

    return self->walkerValue;
}

// x1 = max value, x2 = max step
static MYFLT
Xnoise_loopseg(Xnoise *self)
{
    unsigned int modulo, dir;

    if (self->loopChoice == 0)
    {

        self->loopCountPlay = self->loopTime = 0;

        if (self->xx2 < 0.002) self->xx2 = 0.002;

        modulo = (unsigned int)(self->xx2 * 1000.0);
        dir = pyorand() % 100;

        if (dir < 50)
            self->walkerValue += ((MYFLT)(pyorand() % modulo) * 0.001);
        else
            self->walkerValue -= ((MYFLT)(pyorand() % modulo) * 0.001);

        if (self->walkerValue > self->xx1)
            self->walkerValue = self->xx1;
        else if (self->walkerValue < 0.0)
            self->walkerValue = 0.0;

        self->loop_buffer[self->loopCountRec++] = self->walkerValue;

        if (self->loopCountRec < self->loopLen)
            self->loopChoice = 0;
        else
        {
            self->loopChoice = 1;
            self->loopStop = (pyorand() % 4) + 1;
        }
    }
    else
    {
        self->loopCountRec = 0;

        self->walkerValue = self->loop_buffer[self->loopCountPlay++];

        if (self->loopCountPlay < self->loopLen)
            self->loopChoice = 1;
        else
        {
            self->loopCountPlay = 0;
            self->loopTime++;
        }

        if (self->loopTime == self->loopStop)
        {
            self->loopChoice = 0;
            self->loopLen = (pyorand() % 10) + 3;
        }
    }

    return self->walkerValue;
}

static void
Xnoise_generate_iii(Xnoise *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_aii(Xnoise *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_iai(Xnoise *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_aai(Xnoise *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_iia(Xnoise *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_aia(Xnoise *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_iaa(Xnoise *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void
Xnoise_generate_aaa(Xnoise *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
        }

        self->data[i] = self->value;
    }
}

static void Xnoise_postprocessing_ii(Xnoise *self) { POST_PROCESSING_II };
static void Xnoise_postprocessing_ai(Xnoise *self) { POST_PROCESSING_AI };
static void Xnoise_postprocessing_ia(Xnoise *self) { POST_PROCESSING_IA };
static void Xnoise_postprocessing_aa(Xnoise *self) { POST_PROCESSING_AA };
static void Xnoise_postprocessing_ireva(Xnoise *self) { POST_PROCESSING_IREVA };
static void Xnoise_postprocessing_areva(Xnoise *self) { POST_PROCESSING_AREVA };
static void Xnoise_postprocessing_revai(Xnoise *self) { POST_PROCESSING_REVAI };
static void Xnoise_postprocessing_revaa(Xnoise *self) { POST_PROCESSING_REVAA };
static void Xnoise_postprocessing_revareva(Xnoise *self) { POST_PROCESSING_REVAREVA };

static void
Xnoise_setRandomType(Xnoise *self)
{

    switch (self->type)
    {
        case 0:
            self->type_func_ptr = Xnoise_uniform;
            break;

        case 1:
            self->type_func_ptr = Xnoise_linear_min;
            break;

        case 2:
            self->type_func_ptr = Xnoise_linear_max;
            break;

        case 3:
            self->type_func_ptr = Xnoise_triangle;
            break;

        case 4:
            self->type_func_ptr = Xnoise_expon_min;
            break;

        case 5:
            self->type_func_ptr = Xnoise_expon_max;
            break;

        case 6:
            self->type_func_ptr = Xnoise_biexpon;
            break;

        case 7:
            self->type_func_ptr = Xnoise_cauchy;
            break;

        case 8:
            self->type_func_ptr = Xnoise_weibull;
            break;

        case 9:
            self->type_func_ptr = Xnoise_gaussian;
            break;

        case 10:
            self->type_func_ptr = Xnoise_poisson;
            break;

        case 11:
            self->type_func_ptr = Xnoise_walker;
            break;

        case 12:
            self->type_func_ptr = Xnoise_loopseg;
            break;
    }
}

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

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

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

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

        case 11:
            self->proc_func_ptr = Xnoise_generate_aai;
            break;

        case 100:
            self->proc_func_ptr = Xnoise_generate_iia;
            break;

        case 101:
            self->proc_func_ptr = Xnoise_generate_aia;
            break;

        case 110:
            self->proc_func_ptr = Xnoise_generate_iaa;
            break;

        case 111:
            self->proc_func_ptr = Xnoise_generate_aaa;
            break;
    }

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

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

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

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

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

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

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

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

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

static void
Xnoise_compute_next_data_frame(Xnoise *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
Xnoise_traverse(Xnoise *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    Py_VISIT(self->x1);
    Py_VISIT(self->x2);
    return 0;
}

static int
Xnoise_clear(Xnoise *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    Py_CLEAR(self->x1);
    Py_CLEAR(self->x2);
    return 0;
}

static void
Xnoise_dealloc(Xnoise* self)
{
    pyo_DEALLOC
    Xnoise_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
Xnoise_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *freqtmp = NULL, *x1tmp = NULL, *x2tmp = NULL, *multmp = NULL, *addtmp = NULL;
    Xnoise *self;
    self = (Xnoise *)type->tp_alloc(type, 0);

    self->x1 = PyFloat_FromDouble(0.5);
    self->x2 = PyFloat_FromDouble(0.5);
    self->freq = PyFloat_FromDouble(1.);
    self->xx1 = self->xx2 = self->walkerValue = 0.5;
    self->value = 0.0;
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;
    self->modebuffer[4] = 0;

    INIT_OBJECT_COMMON

    Server_generateSeed((Server *)self->server, XNOISE_ID);

    self->poisson_tab = 0;
    self->lastPoissonX1 = -99.0;

    for (i = 0; i < 2000; i++)
    {
        self->poisson_buffer[i] = 0.0;
    }

    for (i = 0; i < 15; i++)
    {
        self->loop_buffer[i] = 0.0;
    }

    self->loopChoice = self->loopCountPlay = self->loopTime = self->loopCountRec = self->loopStop = 0;
    self->loopLen = (pyorand() % 10) + 3;

    Stream_setFunctionPtr(self->stream, Xnoise_compute_next_data_frame);
    self->mode_func_ptr = Xnoise_setProcMode;

    static char *kwlist[] = {"type", "freq", "x1", "x2", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iOOOOO", kwlist, &self->type, &freqtmp, &x1tmp, &x2tmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (x1tmp)
    {
        PyObject_CallMethod((PyObject *)self, "setX1", "O", x1tmp);
    }

    if (x2tmp)
    {
        PyObject_CallMethod((PyObject *)self, "setX2", "O", x2tmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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);

    Xnoise_setRandomType(self);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * Xnoise_getServer(Xnoise* self) { GET_SERVER };
static PyObject * Xnoise_getStream(Xnoise* self) { GET_STREAM };
static PyObject * Xnoise_setMul(Xnoise *self, PyObject *arg) { SET_MUL };
static PyObject * Xnoise_setAdd(Xnoise *self, PyObject *arg) { SET_ADD };
static PyObject * Xnoise_setSub(Xnoise *self, PyObject *arg) { SET_SUB };
static PyObject * Xnoise_setDiv(Xnoise *self, PyObject *arg) { SET_DIV };

static PyObject * Xnoise_play(Xnoise *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Xnoise_out(Xnoise *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * Xnoise_stop(Xnoise *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * Xnoise_multiply(Xnoise *self, PyObject *arg) { MULTIPLY };
static PyObject * Xnoise_inplace_multiply(Xnoise *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Xnoise_add(Xnoise *self, PyObject *arg) { ADD };
static PyObject * Xnoise_inplace_add(Xnoise *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Xnoise_sub(Xnoise *self, PyObject *arg) { SUB };
static PyObject * Xnoise_inplace_sub(Xnoise *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Xnoise_div(Xnoise *self, PyObject *arg) { DIV };
static PyObject * Xnoise_inplace_div(Xnoise *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Xnoise_setType(Xnoise *self, PyObject *arg)
{
    ASSERT_ARG_NOT_NULL

    if (PyLong_Check(arg))
    {
        self->type = PyLong_AsLong(arg);
        Xnoise_setRandomType(self);
    }

    Py_RETURN_NONE;
}

static PyObject * Xnoise_setX1(Xnoise *self, PyObject *arg) { SET_PARAM(self->x1, self->x1_stream, 2); }
static PyObject * Xnoise_setX2(Xnoise *self, PyObject *arg) { SET_PARAM(self->x2, self->x2_stream, 3); }
static PyObject * Xnoise_setFreq(Xnoise *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 4); }

static PyMemberDef Xnoise_members[] =
{
    {"server", T_OBJECT_EX, offsetof(Xnoise, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(Xnoise, stream), 0, "Stream object."},
    {"x1", T_OBJECT_EX, offsetof(Xnoise, x1), 0, "first param."},
    {"x2", T_OBJECT_EX, offsetof(Xnoise, x2), 0, "second param."},
    {"freq", T_OBJECT_EX, offsetof(Xnoise, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(Xnoise, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(Xnoise, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef Xnoise_methods[] =
{
    {"getServer", (PyCFunction)Xnoise_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Xnoise_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Xnoise_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)Xnoise_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)Xnoise_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setType", (PyCFunction)Xnoise_setType, METH_O, "Sets distribution type."},
    {"setX1", (PyCFunction)Xnoise_setX1, METH_O, "Sets first param."},
    {"setX2", (PyCFunction)Xnoise_setX2, METH_O, "Sets second param."},
    {"setFreq", (PyCFunction)Xnoise_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)Xnoise_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)Xnoise_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Xnoise_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Xnoise_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Xnoise_as_number =
{
    (binaryfunc)Xnoise_add,                         /*nb_add*/
    (binaryfunc)Xnoise_sub,                         /*nb_subtract*/
    (binaryfunc)Xnoise_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)Xnoise_inplace_add,                 /*inplace_add*/
    (binaryfunc)Xnoise_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)Xnoise_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)Xnoise_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)Xnoise_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject XnoiseType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.Xnoise_base",                                   /*tp_name*/
    sizeof(Xnoise),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)Xnoise_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &Xnoise_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*/
    "Xnoise objects. Periodically generates a new random value.",           /* tp_doc */
    (traverseproc)Xnoise_traverse,                  /* tp_traverse */
    (inquiry)Xnoise_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    Xnoise_methods,                                 /* tp_methods */
    Xnoise_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 */
    Xnoise_new,                                     /* tp_new */
};

/****************/
/**** XnoiseMidi *****/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *x1;
    PyObject *x2;
    PyObject *freq;
    Stream *x1_stream;
    Stream *x2_stream;
    Stream *freq_stream;
    MYFLT (*type_func_ptr)();
    int scale; // 0 = Midi, 1 = frequency, 2 = transpo
    MYFLT xx1;
    MYFLT xx2;
    int range_min;
    int range_max;
    int centralkey;
    int type;
    MYFLT value;
    MYFLT time;
    MYFLT lastPoissonX1;
    int poisson_tab;
    MYFLT poisson_buffer[2000];
    MYFLT walkerValue;
    MYFLT loop_buffer[15];
    int loopChoice;
    int loopCountPlay;
    int loopTime;
    int loopCountRec;
    int loopLen;
    int loopStop;
    int modebuffer[5]; // need at least 2 slots for mul & add
} XnoiseMidi;

static MYFLT
XnoiseMidi_convert(XnoiseMidi *self)
{
    int midival;
    MYFLT val;

    midival = (int)((self->value * (self->range_max - self->range_min)) + self->range_min);

    if (midival < 0)
        midival = 0;
    else if (midival > 127)
        midival = 127;

    if (self->scale == 0)
        val = (MYFLT)midival;
    else if (self->scale == 1)
        val = 8.1757989156437 * MYPOW(1.0594630943593, midival);
    else if (self->scale == 2)
        val = MYPOW(1.0594630943593, midival - self->centralkey);
    else
        val = midival;

    return val;
}

// no parameter
static MYFLT
XnoiseMidi_uniform(XnoiseMidi *self)
{
    return RANDOM_UNIFORM;
}

static MYFLT
XnoiseMidi_linear_min(XnoiseMidi *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;

    if (a < b) return a;
    else return b;
}

static MYFLT
XnoiseMidi_linear_max(XnoiseMidi *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;

    if (a > b) return a;
    else return b;
}

static MYFLT
XnoiseMidi_triangle(XnoiseMidi *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;
    return ((a + b) * 0.5);
}

// x1 = slope
static MYFLT
XnoiseMidi_expon_min(XnoiseMidi *self)
{
    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT val = -MYLOG(RANDOM_UNIFORM) / self->xx1;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

static MYFLT
XnoiseMidi_expon_max(XnoiseMidi *self)
{
    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT val = 1.0 - (-MYLOG(RANDOM_UNIFORM) / self->xx1);

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = bandwidth
static MYFLT
XnoiseMidi_biexpon(XnoiseMidi *self)
{
    MYFLT polar, val;

    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT sum = RANDOM_UNIFORM * 2.0;

    if (sum > 1.0)
    {
        polar = -1;
        sum = 2.0 - sum;
    }
    else
        polar = 1;

    val = 0.5 * (polar * MYLOG(sum) / self->xx1) + 0.5;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

static MYFLT
XnoiseMidi_cauchy(XnoiseMidi *self)
{
    MYFLT rnd, val, dir;

    do
    {
        rnd = RANDOM_UNIFORM;
    }
    while (rnd == 0.5);

    if (pyorand() < (PYO_RAND_MAX / 2))
        dir = -1;
    else
        dir = 1;

    val = 0.5 * (MYTAN(rnd) * self->xx1 * dir) + 0.5;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = locator, x2 = shape
static MYFLT
XnoiseMidi_weibull(XnoiseMidi *self)
{
    MYFLT rnd, val;

    if (self->xx2 <= 0.0) self->xx2 = 0.00001;

    rnd = 1.0 / (1.0 - RANDOM_UNIFORM);
    val = self->xx1 * MYPOW(MYLOG(rnd), (1.0 / self->xx2));

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = locator, x2 = bandwidth
static MYFLT
XnoiseMidi_gaussian(XnoiseMidi *self)
{
    MYFLT rnd, val;

    rnd = (RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM);
    val = (self->xx2 * (rnd - 3.0) * 0.33 + self->xx1);

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = gravity center, x2 = compress/expand
static MYFLT
XnoiseMidi_poisson(XnoiseMidi *self)
{
    int i, j, factorial;
    long tot;
    MYFLT val;

    if (self->xx1 < 0.1) self->xx1 = 0.1;

    if (self->xx2 < 0.1) self->xx2 = 0.1;

    if (self->xx1 != self->lastPoissonX1)
    {
        self->lastPoissonX1 = self->xx1;
        self->poisson_tab = 0;
        factorial = 1;

        for (i = 1; i < 12; i++)
        {
            factorial *= i;
            tot = (long)(1000.0 * (MYPOW(2.7182818, -self->xx1) * MYPOW(self->xx1, i) / factorial));

            for (j = 0; j < tot; j++)
            {
                self->poisson_buffer[self->poisson_tab] = i;
                self->poisson_tab++;
            }
        }
    }

    val = self->poisson_buffer[pyorand() % self->poisson_tab] / 12.0 * self->xx2;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = max value, x2 = max step
static MYFLT
XnoiseMidi_walker(XnoiseMidi *self)
{
    unsigned int modulo, dir;

    if (self->xx2 < 0.002) self->xx2 = 0.002;

    modulo = (unsigned int)(self->xx2 * 1000.0);
    dir = pyorand() % 100;

    if (dir < 50)
        self->walkerValue += ((MYFLT)(pyorand() % modulo) * 0.001);
    else
        self->walkerValue -= ((MYFLT)(pyorand() % modulo) * 0.001);

    if (self->walkerValue > self->xx1)
        self->walkerValue = self->xx1;
    else if (self->walkerValue < 0.0)
        self->walkerValue = 0.0;

    return self->walkerValue;
}

// x1 = max value, x2 = max step
static MYFLT
XnoiseMidi_loopseg(XnoiseMidi *self)
{
    unsigned int modulo, dir;

    if (self->loopChoice == 0)
    {

        self->loopCountPlay = self->loopTime = 0;

        if (self->xx2 < 0.002) self->xx2 = 0.002;

        modulo = (unsigned int)(self->xx2 * 1000.0);
        dir = pyorand() % 100;

        if (dir < 50)
            self->walkerValue += ((MYFLT)(pyorand() % modulo) * 0.001);
        else
            self->walkerValue -= ((MYFLT)(pyorand() % modulo) * 0.001);

        if (self->walkerValue > self->xx1)
            self->walkerValue = self->xx1;
        else if (self->walkerValue < 0.0)
            self->walkerValue = 0.0;

        self->loop_buffer[self->loopCountRec++] = self->walkerValue;

        if (self->loopCountRec < self->loopLen)
            self->loopChoice = 0;
        else
        {
            self->loopChoice = 1;
            self->loopStop = (pyorand() % 4) + 1;
        }
    }
    else
    {
        self->loopCountRec = 0;

        self->walkerValue = self->loop_buffer[self->loopCountPlay++];

        if (self->loopCountPlay < self->loopLen)
            self->loopChoice = 1;
        else
        {
            self->loopCountPlay = 0;
            self->loopTime++;
        }

        if (self->loopTime == self->loopStop)
        {
            self->loopChoice = 0;
            self->loopLen = (pyorand() % 10) + 3;
        }
    }

    return self->walkerValue;
}

static void
XnoiseMidi_generate_iii(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_aii(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_iai(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_aai(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_iia(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_aia(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    self->xx2 = PyFloat_AS_DOUBLE(self->x2);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_iaa(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    self->xx1 = PyFloat_AS_DOUBLE(self->x1);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void
XnoiseMidi_generate_aaa(XnoiseMidi *self)
{
    int i;
    MYFLT inc;
    MYFLT *x1 = Stream_getData((Stream *)self->x1_stream);
    MYFLT *x2 = Stream_getData((Stream *)self->x2_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->xx1 = x1[i];
            self->xx2 = x2[i];
            self->value = (*self->type_func_ptr)(self);
            self->value = XnoiseMidi_convert(self);
        }

        self->data[i] = self->value;
    }
}

static void XnoiseMidi_postprocessing_ii(XnoiseMidi *self) { POST_PROCESSING_II };
static void XnoiseMidi_postprocessing_ai(XnoiseMidi *self) { POST_PROCESSING_AI };
static void XnoiseMidi_postprocessing_ia(XnoiseMidi *self) { POST_PROCESSING_IA };
static void XnoiseMidi_postprocessing_aa(XnoiseMidi *self) { POST_PROCESSING_AA };
static void XnoiseMidi_postprocessing_ireva(XnoiseMidi *self) { POST_PROCESSING_IREVA };
static void XnoiseMidi_postprocessing_areva(XnoiseMidi *self) { POST_PROCESSING_AREVA };
static void XnoiseMidi_postprocessing_revai(XnoiseMidi *self) { POST_PROCESSING_REVAI };
static void XnoiseMidi_postprocessing_revaa(XnoiseMidi *self) { POST_PROCESSING_REVAA };
static void XnoiseMidi_postprocessing_revareva(XnoiseMidi *self) { POST_PROCESSING_REVAREVA };

static void
XnoiseMidi_setRandomType(XnoiseMidi *self)
{

    switch (self->type)
    {
        case 0:
            self->type_func_ptr = XnoiseMidi_uniform;
            break;

        case 1:
            self->type_func_ptr = XnoiseMidi_linear_min;
            break;

        case 2:
            self->type_func_ptr = XnoiseMidi_linear_max;
            break;

        case 3:
            self->type_func_ptr = XnoiseMidi_triangle;
            break;

        case 4:
            self->type_func_ptr = XnoiseMidi_expon_min;
            break;

        case 5:
            self->type_func_ptr = XnoiseMidi_expon_max;
            break;

        case 6:
            self->type_func_ptr = XnoiseMidi_biexpon;
            break;

        case 7:
            self->type_func_ptr = XnoiseMidi_cauchy;
            break;

        case 8:
            self->type_func_ptr = XnoiseMidi_weibull;
            break;

        case 9:
            self->type_func_ptr = XnoiseMidi_gaussian;
            break;

        case 10:
            self->type_func_ptr = XnoiseMidi_poisson;
            break;

        case 11:
            self->type_func_ptr = XnoiseMidi_walker;
            break;

        case 12:
            self->type_func_ptr = XnoiseMidi_loopseg;
            break;
    }
}

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

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

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

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

        case 11:
            self->proc_func_ptr = XnoiseMidi_generate_aai;
            break;

        case 100:
            self->proc_func_ptr = XnoiseMidi_generate_iia;
            break;

        case 101:
            self->proc_func_ptr = XnoiseMidi_generate_aia;
            break;

        case 110:
            self->proc_func_ptr = XnoiseMidi_generate_iaa;
            break;

        case 111:
            self->proc_func_ptr = XnoiseMidi_generate_aaa;
            break;
    }

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

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

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

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

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

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

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

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

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

static void
XnoiseMidi_compute_next_data_frame(XnoiseMidi *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
XnoiseMidi_traverse(XnoiseMidi *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    Py_VISIT(self->x1);
    Py_VISIT(self->x2);
    return 0;
}

static int
XnoiseMidi_clear(XnoiseMidi *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    Py_CLEAR(self->x1);
    Py_CLEAR(self->x2);
    return 0;
}

static void
XnoiseMidi_dealloc(XnoiseMidi* self)
{
    pyo_DEALLOC
    XnoiseMidi_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
XnoiseMidi_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *freqtmp = NULL, *x1tmp = NULL, *x2tmp = NULL, *rangetmp = NULL, *multmp = NULL, *addtmp = NULL;
    XnoiseMidi *self;
    self = (XnoiseMidi *)type->tp_alloc(type, 0);

    self->x1 = PyFloat_FromDouble(0.5);
    self->x2 = PyFloat_FromDouble(0.5);
    self->freq = PyFloat_FromDouble(1.);
    self->xx1 = self->xx2 = self->walkerValue = 0.5;
    self->value = 0.0;
    self->time = 1.0;
    self->scale = 0;
    self->range_min = 0;
    self->range_max = 127;
    self->centralkey = 64;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;
    self->modebuffer[4] = 0;

    INIT_OBJECT_COMMON

    Server_generateSeed((Server *)self->server, XNOISEMIDI_ID);

    self->poisson_tab = 0;
    self->lastPoissonX1 = -99.0;

    for (i = 0; i < 2000; i++)
    {
        self->poisson_buffer[i] = 0.0;
    }

    for (i = 0; i < 15; i++)
    {
        self->loop_buffer[i] = 0.0;
    }

    self->loopChoice = self->loopCountPlay = self->loopTime = self->loopCountRec = self->loopStop = 0;
    self->loopLen = (pyorand() % 10) + 3;

    Stream_setFunctionPtr(self->stream, XnoiseMidi_compute_next_data_frame);
    self->mode_func_ptr = XnoiseMidi_setProcMode;

    static char *kwlist[] = {"type", "freq", "x1", "x2", "scale", "range", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iOOOiOOO", kwlist, &self->type, &freqtmp, &x1tmp, &x2tmp, &self->scale, &rangetmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (x1tmp)
    {
        PyObject_CallMethod((PyObject *)self, "setX1", "O", x1tmp);
    }

    if (x2tmp)
    {
        PyObject_CallMethod((PyObject *)self, "setX2", "O", x2tmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    if (rangetmp)
    {
        PyObject_CallMethod((PyObject *)self, "setRange", "O", rangetmp);
    }

    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);

    XnoiseMidi_setRandomType(self);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * XnoiseMidi_getServer(XnoiseMidi* self) { GET_SERVER };
static PyObject * XnoiseMidi_getStream(XnoiseMidi* self) { GET_STREAM };
static PyObject * XnoiseMidi_setMul(XnoiseMidi *self, PyObject *arg) { SET_MUL };
static PyObject * XnoiseMidi_setAdd(XnoiseMidi *self, PyObject *arg) { SET_ADD };
static PyObject * XnoiseMidi_setSub(XnoiseMidi *self, PyObject *arg) { SET_SUB };
static PyObject * XnoiseMidi_setDiv(XnoiseMidi *self, PyObject *arg) { SET_DIV };

static PyObject * XnoiseMidi_play(XnoiseMidi *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * XnoiseMidi_out(XnoiseMidi *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * XnoiseMidi_stop(XnoiseMidi *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * XnoiseMidi_multiply(XnoiseMidi *self, PyObject *arg) { MULTIPLY };
static PyObject * XnoiseMidi_inplace_multiply(XnoiseMidi *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * XnoiseMidi_add(XnoiseMidi *self, PyObject *arg) { ADD };
static PyObject * XnoiseMidi_inplace_add(XnoiseMidi *self, PyObject *arg) { INPLACE_ADD };
static PyObject * XnoiseMidi_sub(XnoiseMidi *self, PyObject *arg) { SUB };
static PyObject * XnoiseMidi_inplace_sub(XnoiseMidi *self, PyObject *arg) { INPLACE_SUB };
static PyObject * XnoiseMidi_div(XnoiseMidi *self, PyObject *arg) { DIV };
static PyObject * XnoiseMidi_inplace_div(XnoiseMidi *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
XnoiseMidi_setType(XnoiseMidi *self, PyObject *arg)
{
    ASSERT_ARG_NOT_NULL

    if (PyLong_Check(arg))
    {
        self->type = PyLong_AsLong(arg);
        XnoiseMidi_setRandomType(self);
    }

    Py_RETURN_NONE;
}

static PyObject *
XnoiseMidi_setScale(XnoiseMidi *self, PyObject *arg)
{
    int tmp;

    ASSERT_ARG_NOT_NULL

    if (PyLong_Check(arg))
    {
        tmp = PyLong_AsLong(arg);

        if (tmp >= 0 && tmp <= 2)
            self->scale = tmp;
        else
            PySys_WriteStdout("XnoiseMidi: scale attribute must be an integer {0, 1, 2}\n");
    }

    Py_RETURN_NONE;
}

static PyObject *
XnoiseMidi_setRange(XnoiseMidi *self, PyObject *args)
{
    if (args == NULL)
    {
        Py_RETURN_NONE;
    }

    int isTuple = PyTuple_Check(args);

    if (isTuple == 1)
    {
        self->range_min = PyLong_AsLong(PyTuple_GET_ITEM(args, 0));
        self->range_max = PyLong_AsLong(PyTuple_GET_ITEM(args, 1));
        self->centralkey = (int)((self->range_max + self->range_min) / 2);
    }

    Py_RETURN_NONE;
}

static PyObject * XnoiseMidi_setX1(XnoiseMidi *self, PyObject *arg) { SET_PARAM(self->x1, self->x1_stream, 2); }
static PyObject * XnoiseMidi_setX2(XnoiseMidi *self, PyObject *arg) { SET_PARAM(self->x2, self->x2_stream, 3); }
static PyObject * XnoiseMidi_setFreq(XnoiseMidi *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 4); }

static PyMemberDef XnoiseMidi_members[] =
{
    {"server", T_OBJECT_EX, offsetof(XnoiseMidi, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(XnoiseMidi, stream), 0, "Stream object."},
    {"x1", T_OBJECT_EX, offsetof(XnoiseMidi, x1), 0, "first param."},
    {"x2", T_OBJECT_EX, offsetof(XnoiseMidi, x2), 0, "second param."},
    {"freq", T_OBJECT_EX, offsetof(XnoiseMidi, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(XnoiseMidi, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(XnoiseMidi, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef XnoiseMidi_methods[] =
{
    {"getServer", (PyCFunction)XnoiseMidi_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)XnoiseMidi_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)XnoiseMidi_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)XnoiseMidi_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)XnoiseMidi_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setType", (PyCFunction)XnoiseMidi_setType, METH_O, "Sets distribution type."},
    {"setScale", (PyCFunction)XnoiseMidi_setScale, METH_O, "Sets output scale."},
    {"setRange", (PyCFunction)XnoiseMidi_setRange, METH_VARARGS, "Sets range in midi notes (min, max)."},
    {"setX1", (PyCFunction)XnoiseMidi_setX1, METH_O, "Sets first param."},
    {"setX2", (PyCFunction)XnoiseMidi_setX2, METH_O, "Sets second param."},
    {"setFreq", (PyCFunction)XnoiseMidi_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)XnoiseMidi_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)XnoiseMidi_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)XnoiseMidi_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)XnoiseMidi_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods XnoiseMidi_as_number =
{
    (binaryfunc)XnoiseMidi_add,                         /*nb_add*/
    (binaryfunc)XnoiseMidi_sub,                         /*nb_subtract*/
    (binaryfunc)XnoiseMidi_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)XnoiseMidi_inplace_add,                 /*inplace_add*/
    (binaryfunc)XnoiseMidi_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)XnoiseMidi_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)XnoiseMidi_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)XnoiseMidi_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject XnoiseMidiType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.XnoiseMidi_base",                                   /*tp_name*/
    sizeof(XnoiseMidi),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)XnoiseMidi_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &XnoiseMidi_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*/
    "XnoiseMidi objects. Periodically generates a new random value.",           /* tp_doc */
    (traverseproc)XnoiseMidi_traverse,                  /* tp_traverse */
    (inquiry)XnoiseMidi_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    XnoiseMidi_methods,                                 /* tp_methods */
    XnoiseMidi_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 */
    XnoiseMidi_new,                                     /* tp_new */
};

/****************/
/**** XnoiseDur *****/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *x1;
    PyObject *x2;
    PyObject *min;
    PyObject *max;
    Stream *x1_stream;
    Stream *x2_stream;
    Stream *min_stream;
    Stream *max_stream;
    MYFLT (*type_func_ptr)();
    MYFLT xx1;
    MYFLT xx2;
    int type;
    MYFLT value;
    MYFLT time;
    MYFLT inc;
    MYFLT lastPoissonX1;
    int poisson_tab;
    MYFLT poisson_buffer[2000];
    MYFLT walkerValue;
    MYFLT loop_buffer[15];
    int loopChoice;
    int loopCountPlay;
    int loopTime;
    int loopCountRec;
    int loopLen;
    int loopStop;
    int modebuffer[6]; // need at least 2 slots for mul & add
} XnoiseDur;

// no parameter
static MYFLT
XnoiseDur_uniform(XnoiseDur *self)
{
    return RANDOM_UNIFORM;
}

static MYFLT
XnoiseDur_linear_min(XnoiseDur *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;

    if (a < b) return a;
    else return b;
}

static MYFLT
XnoiseDur_linear_max(XnoiseDur *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;

    if (a > b) return a;
    else return b;
}

static MYFLT
XnoiseDur_triangle(XnoiseDur *self)
{
    MYFLT a = RANDOM_UNIFORM;
    MYFLT b = RANDOM_UNIFORM;
    return ((a + b) * 0.5);
}

// x1 = slope
static MYFLT
XnoiseDur_expon_min(XnoiseDur *self)
{
    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT val = -MYLOG(RANDOM_UNIFORM) / self->xx1;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

static MYFLT
XnoiseDur_expon_max(XnoiseDur *self)
{
    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT val = 1.0 - (-MYLOG(RANDOM_UNIFORM) / self->xx1);

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = bandwidth
static MYFLT
XnoiseDur_biexpon(XnoiseDur *self)
{
    MYFLT polar, val;

    if (self->xx1 <= 0.0) self->xx1 = 0.00001;

    MYFLT sum = RANDOM_UNIFORM * 2.0;

    if (sum > 1.0)
    {
        polar = -1;
        sum = 2.0 - sum;
    }
    else
        polar = 1;

    val = 0.5 * (polar * MYLOG(sum) / self->xx1) + 0.5;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

static MYFLT
XnoiseDur_cauchy(XnoiseDur *self)
{
    MYFLT rnd, val, dir;

    do
    {
        rnd = RANDOM_UNIFORM;
    }
    while (rnd == 0.5);

    if (pyorand() < (PYO_RAND_MAX / 2))
        dir = -1;
    else
        dir = 1;

    val = 0.5 * (MYTAN(rnd) * self->xx1 * dir) + 0.5;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = locator, x2 = shape
static MYFLT
XnoiseDur_weibull(XnoiseDur *self)
{
    MYFLT rnd, val;

    if (self->xx2 <= 0.0) self->xx2 = 0.00001;

    rnd = 1.0 / (1.0 - RANDOM_UNIFORM);
    val = self->xx1 * MYPOW(MYLOG(rnd), (1.0 / self->xx2));

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = locator, x2 = bandwidth
static MYFLT
XnoiseDur_gaussian(XnoiseDur *self)
{
    MYFLT rnd, val;

    rnd = (RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM + RANDOM_UNIFORM);
    val = (self->xx2 * (rnd - 3.0) * 0.33 + self->xx1);

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = gravity center, x2 = compress/expand
static MYFLT
XnoiseDur_poisson(XnoiseDur *self)
{
    int i, j, factorial;
    long tot;
    MYFLT val;

    if (self->xx1 < 0.1) self->xx1 = 0.1;

    if (self->xx2 < 0.1) self->xx2 = 0.1;

    if (self->xx1 != self->lastPoissonX1)
    {
        self->lastPoissonX1 = self->xx1;
        self->poisson_tab = 0;
        factorial = 1;

        for (i = 1; i < 12; i++)
        {
            factorial *= i;
            tot = (long)(1000.0 * (MYPOW(2.7182818, -self->xx1) * MYPOW(self->xx1, i) / factorial));

            for (j = 0; j < tot; j++)
            {
                self->poisson_buffer[self->poisson_tab] = i;
                self->poisson_tab++;
            }
        }
    }

    val = self->poisson_buffer[pyorand() % self->poisson_tab] / 12.0 * self->xx2;

    if (val < 0.0) return 0.0;
    else if (val > 1.0) return 1.0;
    else return val;
}

// x1 = max value, x2 = max step
static MYFLT
XnoiseDur_walker(XnoiseDur *self)
{
    unsigned int modulo, dir;

    if (self->xx2 < 0.002) self->xx2 = 0.002;

    modulo = (unsigned int)(self->xx2 * 1000.0);
    dir = pyorand() % 100;

    if (dir < 50)
        self->walkerValue += ((MYFLT)(pyorand() % modulo) * 0.001);
    else
        self->walkerValue -= ((MYFLT)(pyorand() % modulo) * 0.001);

    if (self->walkerValue > self->xx1)
        self->walkerValue = self->xx1;
    else if (self->walkerValue < 0.0)
        self->walkerValue = 0.0;

    return self->walkerValue;
}

// x1 = max value, x2 = max step
static MYFLT
XnoiseDur_loopseg(XnoiseDur *self)
{
    unsigned int modulo, dir;

    if (self->loopChoice == 0)
    {

        self->loopCountPlay = self->loopTime = 0;

        if (self->xx2 < 0.002) self->xx2 = 0.002;

        modulo = (unsigned int)(self->xx2 * 1000.0);
        dir = pyorand() % 100;

        if (dir < 50)
            self->walkerValue += ((MYFLT)(pyorand() % modulo) * 0.001);
        else
            self->walkerValue -= ((MYFLT)(pyorand() % modulo) * 0.001);

        if (self->walkerValue > self->xx1)
            self->walkerValue = self->xx1;
        else if (self->walkerValue < 0.0)
            self->walkerValue = 0.0;

        self->loop_buffer[self->loopCountRec++] = self->walkerValue;

        if (self->loopCountRec < self->loopLen)
            self->loopChoice = 0;
        else
        {
            self->loopChoice = 1;
            self->loopStop = (pyorand() % 4) + 1;
        }
    }
    else
    {
        self->loopCountRec = 0;

        self->walkerValue = self->loop_buffer[self->loopCountPlay++];

        if (self->loopCountPlay < self->loopLen)
            self->loopChoice = 1;
        else
        {
            self->loopCountPlay = 0;
            self->loopTime++;
        }

        if (self->loopTime == self->loopStop)
        {
            self->loopChoice = 0;
            self->loopLen = (pyorand() % 10) + 3;
        }
    }

    return self->walkerValue;
}

static void
XnoiseDur_generate(XnoiseDur *self)
{
    int i;
    MYFLT min, max;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += self->inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;

            if (self->modebuffer[2] == 0)
                self->xx1 = PyFloat_AS_DOUBLE(self->x1);
            else
                self->xx1 = Stream_getData((Stream *)self->x1_stream)[i];

            if (self->modebuffer[3] == 0)
                self->xx2 = PyFloat_AS_DOUBLE(self->x2);
            else
                self->xx2 = Stream_getData((Stream *)self->x2_stream)[i];

            if (self->modebuffer[4] == 0)
                min = PyFloat_AS_DOUBLE(self->min);
            else
                min = Stream_getData((Stream *)self->min_stream)[i];

            if (self->modebuffer[5] == 0)
                max = PyFloat_AS_DOUBLE(self->max);
            else
                max = Stream_getData((Stream *)self->max_stream)[i];

            if (min > max)
                max = min;

            self->value = (*self->type_func_ptr)(self) * (max - min) + min;

            if (self->value == 0.0)
                self->inc = 0.0;
            else
                self->inc = (1.0 / self->value) / self->sr;
        }

        self->data[i] = self->value;
    }
}

static void XnoiseDur_postprocessing_ii(XnoiseDur *self) { POST_PROCESSING_II };
static void XnoiseDur_postprocessing_ai(XnoiseDur *self) { POST_PROCESSING_AI };
static void XnoiseDur_postprocessing_ia(XnoiseDur *self) { POST_PROCESSING_IA };
static void XnoiseDur_postprocessing_aa(XnoiseDur *self) { POST_PROCESSING_AA };
static void XnoiseDur_postprocessing_ireva(XnoiseDur *self) { POST_PROCESSING_IREVA };
static void XnoiseDur_postprocessing_areva(XnoiseDur *self) { POST_PROCESSING_AREVA };
static void XnoiseDur_postprocessing_revai(XnoiseDur *self) { POST_PROCESSING_REVAI };
static void XnoiseDur_postprocessing_revaa(XnoiseDur *self) { POST_PROCESSING_REVAA };
static void XnoiseDur_postprocessing_revareva(XnoiseDur *self) { POST_PROCESSING_REVAREVA };

static void
XnoiseDur_setRandomType(XnoiseDur *self)
{

    switch (self->type)
    {
        case 0:
            self->type_func_ptr = XnoiseDur_uniform;
            break;

        case 1:
            self->type_func_ptr = XnoiseDur_linear_min;
            break;

        case 2:
            self->type_func_ptr = XnoiseDur_linear_max;
            break;

        case 3:
            self->type_func_ptr = XnoiseDur_triangle;
            break;

        case 4:
            self->type_func_ptr = XnoiseDur_expon_min;
            break;

        case 5:
            self->type_func_ptr = XnoiseDur_expon_max;
            break;

        case 6:
            self->type_func_ptr = XnoiseDur_biexpon;
            break;

        case 7:
            self->type_func_ptr = XnoiseDur_cauchy;
            break;

        case 8:
            self->type_func_ptr = XnoiseDur_weibull;
            break;

        case 9:
            self->type_func_ptr = XnoiseDur_gaussian;
            break;

        case 10:
            self->type_func_ptr = XnoiseDur_poisson;
            break;

        case 11:
            self->type_func_ptr = XnoiseDur_walker;
            break;

        case 12:
            self->type_func_ptr = XnoiseDur_loopseg;
            break;
    }
}

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

    self->proc_func_ptr = XnoiseDur_generate;

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

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

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

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

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

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

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

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

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

static void
XnoiseDur_compute_next_data_frame(XnoiseDur *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
XnoiseDur_traverse(XnoiseDur *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->min);
    Py_VISIT(self->max);
    Py_VISIT(self->x1);
    Py_VISIT(self->x2);
    return 0;
}

static int
XnoiseDur_clear(XnoiseDur *self)
{
    pyo_CLEAR
    Py_CLEAR(self->min);
    Py_CLEAR(self->max);
    Py_CLEAR(self->x1);
    Py_CLEAR(self->x2);
    return 0;
}

static void
XnoiseDur_dealloc(XnoiseDur* self)
{
    pyo_DEALLOC
    XnoiseDur_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
XnoiseDur_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    MYFLT mi, ma;
    PyObject *mintmp = NULL, *maxtmp = NULL, *x1tmp = NULL, *x2tmp = NULL, *multmp = NULL, *addtmp = NULL;
    XnoiseDur *self;
    self = (XnoiseDur *)type->tp_alloc(type, 0);

    self->x1 = PyFloat_FromDouble(0.5);
    self->x2 = PyFloat_FromDouble(0.5);
    self->min = PyFloat_FromDouble(0.0);
    self->max = PyFloat_FromDouble(1.0);
    self->xx1 = self->xx2 = self->walkerValue = 0.5;
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;
    self->modebuffer[4] = 0;
    self->modebuffer[5] = 0;

    INIT_OBJECT_COMMON

    Server_generateSeed((Server *)self->server, XNOISEDUR_ID);

    self->poisson_tab = 0;
    self->lastPoissonX1 = -99.0;

    for (i = 0; i < 2000; i++)
    {
        self->poisson_buffer[i] = 0.0;
    }

    for (i = 0; i < 15; i++)
    {
        self->loop_buffer[i] = 0.0;
    }

    self->loopChoice = self->loopCountPlay = self->loopTime = self->loopCountRec = self->loopStop = 0;
    self->loopLen = (pyorand() % 10) + 3;

    Stream_setFunctionPtr(self->stream, XnoiseDur_compute_next_data_frame);
    self->mode_func_ptr = XnoiseDur_setProcMode;

    static char *kwlist[] = {"type", "min", "max", "x1", "x2", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iOOOOOO", kwlist, &self->type, &mintmp, &maxtmp, &x1tmp, &x2tmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (x1tmp)
    {
        PyObject_CallMethod((PyObject *)self, "setX1", "O", x1tmp);
    }

    if (x2tmp)
    {
        PyObject_CallMethod((PyObject *)self, "setX2", "O", x2tmp);
    }

    if (mintmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMin", "O", mintmp);
    }

    if (maxtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setMax", "O", maxtmp);
    }

    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);

    if (self->modebuffer[2] == 0)
        mi = PyFloat_AS_DOUBLE(self->min);
    else
        mi = Stream_getData((Stream *)self->min_stream)[0];

    if (self->modebuffer[3] == 0)
        ma = PyFloat_AS_DOUBLE(self->max);
    else
        ma = Stream_getData((Stream *)self->max_stream)[0];

    self->value = (mi + ma) * 0.5;

    if (self->value == 0.0)
        self->inc = 0.0;
    else
        self->inc = (1.0 / self->value) / self->sr;

    XnoiseDur_setRandomType(self);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * XnoiseDur_getServer(XnoiseDur* self) { GET_SERVER };
static PyObject * XnoiseDur_getStream(XnoiseDur* self) { GET_STREAM };
static PyObject * XnoiseDur_setMul(XnoiseDur *self, PyObject *arg) { SET_MUL };
static PyObject * XnoiseDur_setAdd(XnoiseDur *self, PyObject *arg) { SET_ADD };
static PyObject * XnoiseDur_setSub(XnoiseDur *self, PyObject *arg) { SET_SUB };
static PyObject * XnoiseDur_setDiv(XnoiseDur *self, PyObject *arg) { SET_DIV };

static PyObject * XnoiseDur_play(XnoiseDur *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * XnoiseDur_out(XnoiseDur *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * XnoiseDur_stop(XnoiseDur *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * XnoiseDur_multiply(XnoiseDur *self, PyObject *arg) { MULTIPLY };
static PyObject * XnoiseDur_inplace_multiply(XnoiseDur *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * XnoiseDur_add(XnoiseDur *self, PyObject *arg) { ADD };
static PyObject * XnoiseDur_inplace_add(XnoiseDur *self, PyObject *arg) { INPLACE_ADD };
static PyObject * XnoiseDur_sub(XnoiseDur *self, PyObject *arg) { SUB };
static PyObject * XnoiseDur_inplace_sub(XnoiseDur *self, PyObject *arg) { INPLACE_SUB };
static PyObject * XnoiseDur_div(XnoiseDur *self, PyObject *arg) { DIV };
static PyObject * XnoiseDur_inplace_div(XnoiseDur *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
XnoiseDur_setType(XnoiseDur *self, PyObject *arg)
{
    ASSERT_ARG_NOT_NULL

    if (PyLong_Check(arg))
    {
        self->type = PyLong_AsLong(arg);
        XnoiseDur_setRandomType(self);
    }

    Py_RETURN_NONE;
}

static PyObject * XnoiseDur_setX1(XnoiseDur *self, PyObject *arg) { SET_PARAM(self->x1, self->x1_stream, 2); }
static PyObject * XnoiseDur_setX2(XnoiseDur *self, PyObject *arg) { SET_PARAM(self->x2, self->x2_stream, 3); }
static PyObject * XnoiseDur_setMin(XnoiseDur *self, PyObject *arg) { SET_PARAM(self->min, self->min_stream, 4); }
static PyObject * XnoiseDur_setMax(XnoiseDur *self, PyObject *arg) { SET_PARAM(self->max, self->max_stream, 5); }

static PyMemberDef XnoiseDur_members[] =
{
    {"server", T_OBJECT_EX, offsetof(XnoiseDur, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(XnoiseDur, stream), 0, "Stream object."},
    {"x1", T_OBJECT_EX, offsetof(XnoiseDur, x1), 0, "first param."},
    {"x2", T_OBJECT_EX, offsetof(XnoiseDur, x2), 0, "second param."},
    {"min", T_OBJECT_EX, offsetof(XnoiseDur, min), 0, "Minimum value."},
    {"max", T_OBJECT_EX, offsetof(XnoiseDur, max), 0, "Maximum value."},
    {"mul", T_OBJECT_EX, offsetof(XnoiseDur, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(XnoiseDur, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef XnoiseDur_methods[] =
{
    {"getServer", (PyCFunction)XnoiseDur_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)XnoiseDur_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)XnoiseDur_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)XnoiseDur_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)XnoiseDur_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setType", (PyCFunction)XnoiseDur_setType, METH_O, "Sets distribution type."},
    {"setX1", (PyCFunction)XnoiseDur_setX1, METH_O, "Sets first param."},
    {"setX2", (PyCFunction)XnoiseDur_setX2, METH_O, "Sets second param."},
    {"setMin", (PyCFunction)XnoiseDur_setMin, METH_O, "Sets minimum value."},
    {"setMax", (PyCFunction)XnoiseDur_setMax, METH_O, "Sets maximum value."},
    {"setMul", (PyCFunction)XnoiseDur_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)XnoiseDur_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)XnoiseDur_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)XnoiseDur_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods XnoiseDur_as_number =
{
    (binaryfunc)XnoiseDur_add,                         /*nb_add*/
    (binaryfunc)XnoiseDur_sub,                         /*nb_subtract*/
    (binaryfunc)XnoiseDur_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)XnoiseDur_inplace_add,                 /*inplace_add*/
    (binaryfunc)XnoiseDur_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)XnoiseDur_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)XnoiseDur_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)XnoiseDur_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject XnoiseDurType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.XnoiseDur_base",                                   /*tp_name*/
    sizeof(XnoiseDur),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)XnoiseDur_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &XnoiseDur_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*/
    "XnoiseDur objects. Generates a random value and uses this value as time base for the next generation.",           /* tp_doc */
    (traverseproc)XnoiseDur_traverse,                  /* tp_traverse */
    (inquiry)XnoiseDur_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    XnoiseDur_methods,                                 /* tp_methods */
    XnoiseDur_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 */
    XnoiseDur_new,                                     /* tp_new */
};

/****************/
/****** Urn *****/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *freq;
    Stream *freq_stream;
    int *list;
    int max;
    int length;
    int lastvalue;
    MYFLT value;
    MYFLT time;
    MYFLT *trigsBuffer;
    TriggerStream *trig_stream;
    int modebuffer[3]; // need at least 2 slots for mul & add
} Urn;

static void
Urn_reset(Urn *self)
{
    int i;
    self->lastvalue = (int)self->value;
    self->length = self->max;
    self->list = (int *)PyMem_RawRealloc(self->list, self->max * sizeof(int));

    for (i = 0; i < self->max; i++)
    {
        self->list[i] = i;
    }
}

static int
Urn_choose(Urn *self)
{
    int x = 0;
    int value = 0;
    int i, pick;

    pick = pyorand() % self->length;

    while (pick == self->lastvalue)
        pick = pyorand() % self->length;

    for (i = 0; i < self->length; i++)
    {
        if (i != pick)
            self->list[x++] = self->list[i];
        else
            value = self->list[i];
    }

    self->length = x;
    self->lastvalue = -1;

    return value;
}

static void
Urn_generate_i(Urn *self)
{
    int i;
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    MYFLT inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->trigsBuffer[i] = 0.0;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (MYFLT)Urn_choose(self);

            if (self->length == 0)
            {
                self->trigsBuffer[i] = 1.0;
                Urn_reset(self);
            }
        }

        self->data[i] = self->value;
    }
}

static void
Urn_generate_a(Urn *self)
{
    int i;
    MYFLT inc;
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        self->trigsBuffer[i] = 0.0;
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time < 0.0)
            self->time += 1.0;
        else if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (MYFLT)Urn_choose(self);

            if (self->length == 0)
            {
                self->trigsBuffer[i] = 1.0;
                Urn_reset(self);
            }
        }

        self->data[i] = self->value;
    }
}

static void Urn_postprocessing_ii(Urn *self) { POST_PROCESSING_II };
static void Urn_postprocessing_ai(Urn *self) { POST_PROCESSING_AI };
static void Urn_postprocessing_ia(Urn *self) { POST_PROCESSING_IA };
static void Urn_postprocessing_aa(Urn *self) { POST_PROCESSING_AA };
static void Urn_postprocessing_ireva(Urn *self) { POST_PROCESSING_IREVA };
static void Urn_postprocessing_areva(Urn *self) { POST_PROCESSING_AREVA };
static void Urn_postprocessing_revai(Urn *self) { POST_PROCESSING_REVAI };
static void Urn_postprocessing_revaa(Urn *self) { POST_PROCESSING_REVAA };
static void Urn_postprocessing_revareva(Urn *self) { POST_PROCESSING_REVAREVA };

static void
Urn_setProcMode(Urn *self)
{
    int procmode, muladdmode;
    procmode = self->modebuffer[2];
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;

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

        case 1:
            self->proc_func_ptr = Urn_generate_a;
            break;
    }

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

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

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

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

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

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

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

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

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

static void
Urn_compute_next_data_frame(Urn *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
Urn_traverse(Urn *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    return 0;
}

static int
Urn_clear(Urn *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    return 0;
}

static void
Urn_dealloc(Urn* self)
{
    pyo_DEALLOC
    PyMem_RawFree(self->list);
    PyMem_RawFree(self->trigsBuffer);
    Urn_clear(self);
    Py_TYPE(self->trig_stream)->tp_free((PyObject*)self->trig_stream);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
Urn_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *freqtmp = NULL, *multmp = NULL, *addtmp = NULL;
    Urn *self;
    self = (Urn *)type->tp_alloc(type, 0);

    self->freq = PyFloat_FromDouble(1.);
    self->max = 100;
    self->length = 0;
    self->lastvalue = -1;
    self->value = 0.0;
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Urn_compute_next_data_frame);
    self->mode_func_ptr = Urn_setProcMode;

    static char *kwlist[] = {"max", "freq", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iOOO", kwlist, &self->max, &freqtmp, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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->trigsBuffer = (MYFLT *)PyMem_RawRealloc(self->trigsBuffer, self->bufsize * sizeof(MYFLT));

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

    MAKE_NEW_TRIGGER_STREAM(self->trig_stream, &TriggerStreamType, NULL);
    TriggerStream_setData(self->trig_stream, self->trigsBuffer);

    Urn_reset(self);

    Server_generateSeed((Server *)self->server, URN_ID);

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * Urn_getServer(Urn* self) { GET_SERVER };
static PyObject * Urn_getStream(Urn* self) { GET_STREAM };
static PyObject * Urn_getTriggerStream(Urn* self) { GET_TRIGGER_STREAM };
static PyObject * Urn_setMul(Urn *self, PyObject *arg) { SET_MUL };
static PyObject * Urn_setAdd(Urn *self, PyObject *arg) { SET_ADD };
static PyObject * Urn_setSub(Urn *self, PyObject *arg) { SET_SUB };
static PyObject * Urn_setDiv(Urn *self, PyObject *arg) { SET_DIV };

static PyObject * Urn_play(Urn *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Urn_out(Urn *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * Urn_stop(Urn *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * Urn_multiply(Urn *self, PyObject *arg) { MULTIPLY };
static PyObject * Urn_inplace_multiply(Urn *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Urn_add(Urn *self, PyObject *arg) { ADD };
static PyObject * Urn_inplace_add(Urn *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Urn_sub(Urn *self, PyObject *arg) { SUB };
static PyObject * Urn_inplace_sub(Urn *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Urn_div(Urn *self, PyObject *arg) { DIV };
static PyObject * Urn_inplace_div(Urn *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Urn_setMax(Urn *self, PyObject *arg)
{
    if (PyNumber_Check(arg) == 1)
        self->max = PyLong_AsLong(arg);

    Urn_reset(self);

    Py_RETURN_NONE;
}

static PyObject * Urn_setFreq(Urn *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 2); }

static PyMemberDef Urn_members[] =
{
    {"server", T_OBJECT_EX, offsetof(Urn, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(Urn, stream), 0, "Stream object."},
    {"trig_stream", T_OBJECT_EX, offsetof(Urn, trig_stream), 0, "Trigger Stream object."},
    {"freq", T_OBJECT_EX, offsetof(Urn, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(Urn, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(Urn, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef Urn_methods[] =
{
    {"getServer", (PyCFunction)Urn_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Urn_getStream, METH_NOARGS, "Returns stream object."},
    {"_getTriggerStream", (PyCFunction)Urn_getTriggerStream, METH_NOARGS, "Returns trigger stream object."},
    {"play", (PyCFunction)Urn_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)Urn_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)Urn_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setMax", (PyCFunction)Urn_setMax, METH_O, "Sets maximum possible value."},
    {"setFreq", (PyCFunction)Urn_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)Urn_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)Urn_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Urn_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Urn_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Urn_as_number =
{
    (binaryfunc)Urn_add,                         /*nb_add*/
    (binaryfunc)Urn_sub,                         /*nb_subtract*/
    (binaryfunc)Urn_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)Urn_inplace_add,                 /*inplace_add*/
    (binaryfunc)Urn_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)Urn_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)Urn_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)Urn_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject UrnType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.Urn_base",                                   /*tp_name*/
    sizeof(Urn),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)Urn_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &Urn_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*/
    "Urn objects. Periodically generates a new integer random value without repetition.",           /* tp_doc */
    (traverseproc)Urn_traverse,                  /* tp_traverse */
    (inquiry)Urn_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    Urn_methods,                                 /* tp_methods */
    Urn_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 */
    Urn_new,                                     /* tp_new */
};

/****************/
/**** LogiMap ***/
/****************/
typedef struct
{
    pyo_audio_HEAD
    PyObject *chaos;
    PyObject *freq;
    Stream *chaos_stream;
    Stream *freq_stream;
    MYFLT init;
    MYFLT value;
    MYFLT time;
    int modebuffer[4]; // need at least 2 slots for mul & add
} LogiMap;

static void
LogiMap_generate_ii(LogiMap *self)
{
    int i;
    MYFLT inc;
    MYFLT ch = PyFloat_AS_DOUBLE(self->chaos);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    if (ch <= 0.0)
        ch = 0.001;
    else if (ch >= 1.0)
        ch = 0.999;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (ch + 3) * self->value * (1.0 - self->value);
        }

        self->data[i] = self->value;
    }
}

static void
LogiMap_generate_ai(LogiMap *self)
{
    int i;
    MYFLT inc, ch = 0.0;
    MYFLT *chaos = Stream_getData((Stream *)self->chaos_stream);
    MYFLT fr = PyFloat_AS_DOUBLE(self->freq);
    inc = fr / self->sr;

    for (i = 0; i < self->bufsize; i++)
    {
        self->time += inc;

        if (self->time >= 1.0)
        {
            self->time -= 1.0;
            ch = chaos[i];

            if (ch <= 0.0)
                ch = 0.001;
            else if (ch >= 1.0)
                ch = 0.999;

            self->value = (ch + 3) * self->value * (1.0 - self->value);
        }

        self->data[i] = self->value;
    }
}

static void
LogiMap_generate_ia(LogiMap *self)
{
    int i;
    MYFLT inc;
    MYFLT ch = PyFloat_AS_DOUBLE(self->chaos);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    if (ch <= 0.0)
        ch = 0.001;
    else if (ch >= 1.0)
        ch = 0.999;

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time >= 1.0)
        {
            self->time -= 1.0;
            self->value = (ch + 3) * self->value * (1.0 - self->value);
        }

        self->data[i] = self->value;
    }
}

static void
LogiMap_generate_aa(LogiMap *self)
{
    int i;
    MYFLT inc, ch = 0.0;
    MYFLT *chaos = Stream_getData((Stream *)self->chaos_stream);
    MYFLT *fr = Stream_getData((Stream *)self->freq_stream);

    for (i = 0; i < self->bufsize; i++)
    {
        inc = fr[i] / self->sr;
        self->time += inc;

        if (self->time >= 1.0)
        {
            self->time -= 1.0;
            ch = chaos[i];

            if (ch <= 0.0)
                ch = 0.001;
            else if (ch >= 1.0)
                ch = 0.999;

            self->value = (ch + 3) * self->value * (1.0 - self->value);
        }

        self->data[i] = self->value;
    }
}

static void LogiMap_postprocessing_ii(LogiMap *self) { POST_PROCESSING_II };
static void LogiMap_postprocessing_ai(LogiMap *self) { POST_PROCESSING_AI };
static void LogiMap_postprocessing_ia(LogiMap *self) { POST_PROCESSING_IA };
static void LogiMap_postprocessing_aa(LogiMap *self) { POST_PROCESSING_AA };
static void LogiMap_postprocessing_ireva(LogiMap *self) { POST_PROCESSING_IREVA };
static void LogiMap_postprocessing_areva(LogiMap *self) { POST_PROCESSING_AREVA };
static void LogiMap_postprocessing_revai(LogiMap *self) { POST_PROCESSING_REVAI };
static void LogiMap_postprocessing_revaa(LogiMap *self) { POST_PROCESSING_REVAA };
static void LogiMap_postprocessing_revareva(LogiMap *self) { POST_PROCESSING_REVAREVA };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static void
LogiMap_compute_next_data_frame(LogiMap *self)
{
    (*self->proc_func_ptr)(self);
    (*self->muladd_func_ptr)(self);
}

static int
LogiMap_traverse(LogiMap *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->freq);
    Py_VISIT(self->chaos);
    return 0;
}

static int
LogiMap_clear(LogiMap *self)
{
    pyo_CLEAR
    Py_CLEAR(self->freq);
    Py_CLEAR(self->chaos);
    return 0;
}

static void
LogiMap_dealloc(LogiMap* self)
{
    pyo_DEALLOC
    LogiMap_clear(self);
    Py_TYPE(self->stream)->tp_free((PyObject*)self->stream);
    Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
LogiMap_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    MYFLT init;
    PyObject *chaostmp = NULL, *freqtmp = NULL, *multmp = NULL, *addtmp = NULL;
    LogiMap *self;
    self = (LogiMap *)type->tp_alloc(type, 0);

    self->chaos = PyFloat_FromDouble(0.6);
    self->freq = PyFloat_FromDouble(1.);
    self->time = 1.0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    self->modebuffer[2] = 0;
    self->modebuffer[3] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, LogiMap_compute_next_data_frame);
    self->mode_func_ptr = LogiMap_setProcMode;

    static char *kwlist[] = {"chaos", "freq", "init", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE__OOFOO, kwlist, &chaostmp, &freqtmp, &init, &multmp, &addtmp))
        Py_RETURN_NONE;

    if (chaostmp)
    {
        PyObject_CallMethod((PyObject *)self, "setChaos", "O", chaostmp);
    }

    if (freqtmp)
    {
        PyObject_CallMethod((PyObject *)self, "setFreq", "O", freqtmp);
    }

    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);

    if (init <= 0)
        init = 0.001;
    else if (init >= 1.0)
        init = 0.999;

    self->value = self->init = init;

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * LogiMap_getServer(LogiMap* self) { GET_SERVER };
static PyObject * LogiMap_getStream(LogiMap* self) { GET_STREAM };
static PyObject * LogiMap_setMul(LogiMap *self, PyObject *arg) { SET_MUL };
static PyObject * LogiMap_setAdd(LogiMap *self, PyObject *arg) { SET_ADD };
static PyObject * LogiMap_setSub(LogiMap *self, PyObject *arg) { SET_SUB };
static PyObject * LogiMap_setDiv(LogiMap *self, PyObject *arg) { SET_DIV };

static PyObject * LogiMap_play(LogiMap *self, PyObject *args, PyObject *kwds)
{
    self->value = self->init;
    PLAY
};
static PyObject * LogiMap_out(LogiMap *self, PyObject *args, PyObject *kwds) { OUT };
static PyObject * LogiMap_stop(LogiMap *self, PyObject *args, PyObject *kwds) { STOP };

static PyObject * LogiMap_multiply(LogiMap *self, PyObject *arg) { MULTIPLY };
static PyObject * LogiMap_inplace_multiply(LogiMap *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * LogiMap_add(LogiMap *self, PyObject *arg) { ADD };
static PyObject * LogiMap_inplace_add(LogiMap *self, PyObject *arg) { INPLACE_ADD };
static PyObject * LogiMap_sub(LogiMap *self, PyObject *arg) { SUB };
static PyObject * LogiMap_inplace_sub(LogiMap *self, PyObject *arg) { INPLACE_SUB };
static PyObject * LogiMap_div(LogiMap *self, PyObject *arg) { DIV };
static PyObject * LogiMap_inplace_div(LogiMap *self, PyObject *arg) { INPLACE_DIV };

static PyObject * LogiMap_setChaos(LogiMap *self, PyObject *arg) { SET_PARAM(self->chaos, self->chaos_stream, 2); }
static PyObject * LogiMap_setFreq(LogiMap *self, PyObject *arg) { SET_PARAM(self->freq, self->freq_stream, 3); }

static PyMemberDef LogiMap_members[] =
{
    {"server", T_OBJECT_EX, offsetof(LogiMap, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(LogiMap, stream), 0, "Stream object."},
    {"chaos", T_OBJECT_EX, offsetof(LogiMap, chaos), 0, "chaosimum possible value."},
    {"freq", T_OBJECT_EX, offsetof(LogiMap, freq), 0, "Polling frequency."},
    {"mul", T_OBJECT_EX, offsetof(LogiMap, mul), 0, "Mul factor."},
    {"add", T_OBJECT_EX, offsetof(LogiMap, add), 0, "Add factor."},
    {NULL}  /* Sentinel */
};

static PyMethodDef LogiMap_methods[] =
{
    {"getServer", (PyCFunction)LogiMap_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)LogiMap_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)LogiMap_play, METH_VARARGS | METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"out", (PyCFunction)LogiMap_out, METH_VARARGS | METH_KEYWORDS, "Starts computing and sends sound to soundcard channel speficied by argument."},
    {"stop", (PyCFunction)LogiMap_stop, METH_VARARGS | METH_KEYWORDS, "Stops computing."},
    {"setChaos", (PyCFunction)LogiMap_setChaos, METH_O, "Sets the randomization degree."},
    {"setFreq", (PyCFunction)LogiMap_setFreq, METH_O, "Sets polling frequency."},
    {"setMul", (PyCFunction)LogiMap_setMul, METH_O, "Sets oscillator mul factor."},
    {"setAdd", (PyCFunction)LogiMap_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)LogiMap_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)LogiMap_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods LogiMap_as_number =
{
    (binaryfunc)LogiMap_add,                         /*nb_add*/
    (binaryfunc)LogiMap_sub,                         /*nb_subtract*/
    (binaryfunc)LogiMap_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)LogiMap_inplace_add,                 /*inplace_add*/
    (binaryfunc)LogiMap_inplace_sub,                 /*inplace_subtract*/
    (binaryfunc)LogiMap_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)LogiMap_div,                       /*nb_true_divide*/
    0,                                              /*nb_inplace_floor_divide*/
    (binaryfunc)LogiMap_inplace_div,                       /*nb_inplace_true_divide*/
    0,                                              /* nb_index */
};

PyTypeObject LogiMapType =
{
    PyVarObject_HEAD_INIT(NULL, 0)
    "_pyo.LogiMap_base",                                   /*tp_name*/
    sizeof(LogiMap),                                 /*tp_basicsize*/
    0,                                              /*tp_itemsize*/
    (destructor)LogiMap_dealloc,                     /*tp_dealloc*/
    0,                                              /*tp_print*/
    0,                                              /*tp_getattr*/
    0,                                              /*tp_setattr*/
    0,                                              /*tp_as_async (tp_compare in Python 2)*/
    0,                                              /*tp_repr*/
    &LogiMap_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*/
    "LogiMap objects. Random generator based on the logistic equation.",           /* tp_doc */
    (traverseproc)LogiMap_traverse,                  /* tp_traverse */
    (inquiry)LogiMap_clear,                          /* tp_clear */
    0,                                              /* tp_richcompare */
    0,                                              /* tp_weaklistoffset */
    0,                                              /* tp_iter */
    0,                                              /* tp_iternext */
    LogiMap_methods,                                 /* tp_methods */
    LogiMap_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 */
    LogiMap_new,                                     /* tp_new */
};
