/*************************************************************************
 * Copyright 2010 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 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 General Public License for more details.                           *
 *                                                                        *
 * You should have received a copy of the GNU General Public License      *
 * along with pyo.  If not, see <http://www.gnu.org/licenses/>.           *
 *************************************************************************/

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

typedef struct {
    pyo_audio_HEAD
    PyObject *callable;
    int ctlnumber;
    int toprint;
} CtlScan;

static void
CtlScan_setProcMode(CtlScan *self) {}

static void
CtlScan_compute_next_data_frame(CtlScan *self)
{   
    PmEvent *buffer;
    int i, count;

    buffer = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);

    if (count > 0) {
        PyObject *tup;
        for (i=count-1; i>=0; i--) {
            int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
            int number = Pm_MessageData1(buffer[i].message);
            int value = Pm_MessageData2(buffer[i].message);

            if ((status & 0xF0) == 0xB0) {
                if (number != self->ctlnumber) {
                    self->ctlnumber = number;
                    tup = PyTuple_New(1);
                    PyTuple_SetItem(tup, 0, PyInt_FromLong(self->ctlnumber));
                    PyObject_Call((PyObject *)self->callable, tup, NULL);
                }
                if (self->toprint == 1)
                    printf("ctl number : %i, ctl value : %i, midi channel : %i\n", self->ctlnumber, value, status - 0xB0 + 1);    
            }
        }
    }
}

static int
CtlScan_traverse(CtlScan *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->callable);
    return 0;
}

static int 
CtlScan_clear(CtlScan *self)
{
    pyo_CLEAR
    Py_CLEAR(self->callable);
    return 0;
}

static void
CtlScan_dealloc(CtlScan* self)
{
    pyo_DEALLOC
    CtlScan_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
CtlScan_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *calltmp=NULL;    
    CtlScan *self;
    self = (CtlScan *)type->tp_alloc(type, 0);

    self->ctlnumber = -1;
    self->toprint = 1;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, CtlScan_compute_next_data_frame);
    self->mode_func_ptr = CtlScan_setProcMode;

    static char *kwlist[] = {"callable", "toprint", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, &calltmp, &self->toprint))
        Py_RETURN_NONE;

    if (calltmp) {
        PyObject_CallMethod((PyObject *)self, "setFunction", "O", calltmp);
    }

    PyObject_CallMethod(self->server, "addStream", "O", self->stream);
    
    return (PyObject *)self;
}

static PyObject * CtlScan_getServer(CtlScan* self) { GET_SERVER };
static PyObject * CtlScan_getStream(CtlScan* self) { GET_STREAM };

static PyObject * CtlScan_play(CtlScan *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * CtlScan_stop(CtlScan *self) { STOP };

static PyObject *
CtlScan_setFunction(CtlScan *self, PyObject *arg)
{
	PyObject *tmp;
	
	if (! PyCallable_Check(arg)) {
        PyErr_SetString(PyExc_TypeError, "The callable attribute must be a valid Python function.");
		Py_INCREF(Py_None);
		return Py_None;
	}
    
    tmp = arg;
    Py_XDECREF(self->callable);
    Py_INCREF(tmp);
    self->callable = tmp;
    
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
CtlScan_setToprint(CtlScan *self, PyObject *arg)
{
	
	if (PyInt_Check(arg)) {
	    self->toprint = PyInt_AsLong(arg);
    }
    
	Py_INCREF(Py_None);
	return Py_None;
}
static PyMemberDef CtlScan_members[] = {
    {"server", T_OBJECT_EX, offsetof(CtlScan, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(CtlScan, stream), 0, "Stream object."},
    {NULL}  /* Sentinel */
};

static PyMethodDef CtlScan_methods[] = {
    {"getServer", (PyCFunction)CtlScan_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)CtlScan_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)CtlScan_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)CtlScan_stop, METH_NOARGS, "Stops computing."},
    {"setFunction", (PyCFunction)CtlScan_setFunction, METH_O, "Sets the function to be called."},
    {"setToprint", (PyCFunction)CtlScan_setToprint, METH_O, "If True, print values to the console."},
    {NULL}  /* Sentinel */
};

PyTypeObject CtlScanType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.CtlScan_base",         /*tp_name*/
    sizeof(CtlScan),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)CtlScan_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,             /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "CtlScan objects. Retreive controller numbers from a Midi input.",           /* tp_doc */
    (traverseproc)CtlScan_traverse,   /* tp_traverse */
    (inquiry)CtlScan_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    CtlScan_methods,             /* tp_methods */
    CtlScan_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 */
    CtlScan_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    PyObject *callable;
    int ctlnumber;
    int midichnl;
    int toprint;
} CtlScan2;

static void
CtlScan2_setProcMode(CtlScan2 *self) {}

static void
CtlScan2_compute_next_data_frame(CtlScan2 *self)
{   
    PmEvent *buffer;
    int i, count, midichnl;
    
    buffer = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);
    
    if (count > 0) {
        PyObject *tup;
        for (i=count-1; i>=0; i--) {
            int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
            int number = Pm_MessageData1(buffer[i].message);
            int value = Pm_MessageData2(buffer[i].message);
            
            if ((status & 0xF0) == 0xB0) {
                midichnl = status - 0xB0 + 1;
                if (number != self->ctlnumber || midichnl != self->midichnl) {
                    self->ctlnumber = number;
                    self->midichnl = midichnl;
                    tup = PyTuple_New(2);
                    PyTuple_SetItem(tup, 0, PyInt_FromLong(self->ctlnumber));
                    PyTuple_SetItem(tup, 1, PyInt_FromLong(self->midichnl));
                    PyObject_Call((PyObject *)self->callable, tup, NULL);
                }
                if (self->toprint == 1)
                    printf("ctl number : %i, ctl value : %i, midi channel : %i\n", self->ctlnumber, value, midichnl);    
            }
        }
    }
}

static int
CtlScan2_traverse(CtlScan2 *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->callable);
    return 0;
}

static int 
CtlScan2_clear(CtlScan2 *self)
{
    pyo_CLEAR
    Py_CLEAR(self->callable);
    return 0;
}

static void
CtlScan2_dealloc(CtlScan2* self)
{
    pyo_DEALLOC
    CtlScan2_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
CtlScan2_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *calltmp=NULL;    
    CtlScan2 *self;
    self = (CtlScan2 *)type->tp_alloc(type, 0);
    
    self->ctlnumber = self->midichnl = -1;
    self->toprint = 1;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, CtlScan2_compute_next_data_frame);
    self->mode_func_ptr = CtlScan2_setProcMode;
    
    static char *kwlist[] = {"callable", "toprint", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, &calltmp, &self->toprint))
        Py_RETURN_NONE;
    
    if (calltmp) {
        PyObject_CallMethod((PyObject *)self, "setFunction", "O", calltmp);
    }
    
    PyObject_CallMethod(self->server, "addStream", "O", self->stream);
    
    return (PyObject *)self;
}

static PyObject * CtlScan2_getServer(CtlScan2* self) { GET_SERVER };
static PyObject * CtlScan2_getStream(CtlScan2* self) { GET_STREAM };

static PyObject * CtlScan2_play(CtlScan2 *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * CtlScan2_stop(CtlScan2 *self) { STOP };

static PyObject *
CtlScan2_setFunction(CtlScan2 *self, PyObject *arg)
{
	PyObject *tmp;
	
	if (! PyCallable_Check(arg)) {
        PyErr_SetString(PyExc_TypeError, "The callable attribute must be a valid Python function.");
		Py_INCREF(Py_None);
		return Py_None;
	}
    
    tmp = arg;
    Py_XDECREF(self->callable);
    Py_INCREF(tmp);
    self->callable = tmp;
    
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
CtlScan2_setToprint(CtlScan2 *self, PyObject *arg)
{
	
	if (PyInt_Check(arg)) {
	    self->toprint = PyInt_AsLong(arg);
    }
    
	Py_INCREF(Py_None);
	return Py_None;
}
static PyMemberDef CtlScan2_members[] = {
    {"server", T_OBJECT_EX, offsetof(CtlScan2, server), 0, "Pyo server."},
    {"stream", T_OBJECT_EX, offsetof(CtlScan2, stream), 0, "Stream object."},
    {NULL}  /* Sentinel */
};

static PyMethodDef CtlScan2_methods[] = {
    {"getServer", (PyCFunction)CtlScan2_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)CtlScan2_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)CtlScan2_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)CtlScan2_stop, METH_NOARGS, "Stops computing."},
    {"setFunction", (PyCFunction)CtlScan2_setFunction, METH_O, "Sets the function to be called."},
    {"setToprint", (PyCFunction)CtlScan2_setToprint, METH_O, "If True, print values to the console."},
    {NULL}  /* Sentinel */
};

PyTypeObject CtlScan2Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.CtlScan2_base",         /*tp_name*/
    sizeof(CtlScan2),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)CtlScan2_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,             /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "CtlScan2 objects. Retreive midi channel and controller numbers from a midi input.",           /* tp_doc */
    (traverseproc)CtlScan2_traverse,   /* tp_traverse */
    (inquiry)CtlScan2_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    CtlScan2_methods,             /* tp_methods */
    CtlScan2_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 */
    CtlScan2_new,                 /* tp_new */
};


typedef struct {
    pyo_audio_HEAD
    int ctlnumber;
    int channel;
    int interp;
    MYFLT minscale;
    MYFLT maxscale;
    MYFLT value;
    MYFLT oldValue;
    MYFLT sampleToSec;
    int modebuffer[2];
} Midictl;

static void Midictl_postprocessing_ii(Midictl *self) { POST_PROCESSING_II };
static void Midictl_postprocessing_ai(Midictl *self) { POST_PROCESSING_AI };
static void Midictl_postprocessing_ia(Midictl *self) { POST_PROCESSING_IA };
static void Midictl_postprocessing_aa(Midictl *self) { POST_PROCESSING_AA };
static void Midictl_postprocessing_ireva(Midictl *self) { POST_PROCESSING_IREVA };
static void Midictl_postprocessing_areva(Midictl *self) { POST_PROCESSING_AREVA };
static void Midictl_postprocessing_revai(Midictl *self) { POST_PROCESSING_REVAI };
static void Midictl_postprocessing_revaa(Midictl *self) { POST_PROCESSING_REVAA };
static void Midictl_postprocessing_revareva(Midictl *self) { POST_PROCESSING_REVAREVA };

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

	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = Midictl_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = Midictl_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = Midictl_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = Midictl_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = Midictl_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = Midictl_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = Midictl_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = Midictl_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = Midictl_postprocessing_revareva;
            break;
    }
}

// Take MIDI events and translate them...
void translateMidi(Midictl *self, PmEvent *buffer, int count)
{
    int i, ok;
    for (i=count-1; i>=0; i--) {
        int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
        int number = Pm_MessageData1(buffer[i].message);
        int value = Pm_MessageData2(buffer[i].message);
        
        if (self->channel == 0) {
            if ((status & 0xF0) == 0xB0)
                ok = 1;
            else
                ok = 0;
        }
        else {
            if (status == (0xB0 | (self->channel - 1)))
                ok = 1;
            else
                ok = 0;
        }

        if (ok == 1 && number == self->ctlnumber) {
            self->oldValue = self->value;
            self->value = (value / 127.) * (self->maxscale - self->minscale) + self->minscale;
            break;
        }
    }    
}

static void
Midictl_compute_next_data_frame(Midictl *self)
{   
    PmEvent *tmp;
    int i, count;
    MYFLT step;

    tmp = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);

    if (count > 0)
        translateMidi((Midictl *)self, tmp, count);
    
    if (self->interp == 0) {
        for (i=0; i<self->bufsize; i++) {
            self->data[i] = self->value;
        }
    }
    else {
        step = (self->value - self->oldValue) / self->bufsize;
        for (i=0; i<self->bufsize; i++) {
            self->data[i] = self->oldValue + step;
        }          
    }
    (*self->muladd_func_ptr)(self);
}

static int
Midictl_traverse(Midictl *self, visitproc visit, void *arg)
{
    pyo_VISIT
    return 0;
}

static int 
Midictl_clear(Midictl *self)
{
    pyo_CLEAR
    return 0;
}

static void
Midictl_dealloc(Midictl* self)
{
    pyo_DEALLOC
    Midictl_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

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

    self->channel = 0;
    self->value = 0.;
    self->oldValue = 0.;
    self->minscale = 0.;
    self->maxscale = 1.;
    self->interp = 1;
	self->modebuffer[0] = 0;
	self->modebuffer[1] = 0;

    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Midictl_compute_next_data_frame);
    self->mode_func_ptr = Midictl_setProcMode;

    static char *kwlist[] = {"ctlnumber", "minscale", "maxscale", "init", "channel", "mul", "add", NULL};

    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE_I_FFFIOO, kwlist, &self->ctlnumber, &self->minscale, &self->maxscale, &self->oldValue, &self->channel, &multmp, &addtmp))
        Py_RETURN_NONE;
 
    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->value = self->oldValue;

    (*self->mode_func_ptr)(self);
    
    return (PyObject *)self;
}

static PyObject * Midictl_getServer(Midictl* self) { GET_SERVER };
static PyObject * Midictl_getStream(Midictl* self) { GET_STREAM };
static PyObject * Midictl_setMul(Midictl *self, PyObject *arg) { SET_MUL };	
static PyObject * Midictl_setAdd(Midictl *self, PyObject *arg) { SET_ADD };	
static PyObject * Midictl_setSub(Midictl *self, PyObject *arg) { SET_SUB };	
static PyObject * Midictl_setDiv(Midictl *self, PyObject *arg) { SET_DIV };	

static PyObject * Midictl_play(Midictl *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Midictl_stop(Midictl *self) { STOP };

static PyObject * Midictl_multiply(Midictl *self, PyObject *arg) { MULTIPLY };
static PyObject * Midictl_inplace_multiply(Midictl *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Midictl_add(Midictl *self, PyObject *arg) { ADD };
static PyObject * Midictl_inplace_add(Midictl *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Midictl_sub(Midictl *self, PyObject *arg) { SUB };
static PyObject * Midictl_inplace_sub(Midictl *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Midictl_div(Midictl *self, PyObject *arg) { DIV };
static PyObject * Midictl_inplace_div(Midictl *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Midictl_setInterpolation(Midictl *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyInt_Check(arg);
    
	if (isNum == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp == 0)
            self->interp = 0;
        else
            self->interp = 1;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
Midictl_setValue(Midictl *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyNumber_Check(arg);
    
	if (isNum == 1) {
		tmp = PyFloat_AsDouble(PyNumber_Float(arg));
        self->oldValue = self->value = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
Midictl_setMinScale(Midictl *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyNumber_Check(arg);
    
	if (isNum == 1) {
		tmp = PyFloat_AsDouble(PyNumber_Float(arg));
        self->minscale = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Midictl_setMaxScale(Midictl *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyNumber_Check(arg);
    
	if (isNum == 1) {
		tmp = PyFloat_AsDouble(PyNumber_Float(arg));
        self->maxscale = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Midictl_setCtlNumber(Midictl *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= 0 && tmp < 128)
            self->ctlnumber = tmp;
	}

	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Midictl_setChannel(Midictl *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= 0 && tmp < 128)
            self->channel = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

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

static PyMethodDef Midictl_methods[] = {
    {"getServer", (PyCFunction)Midictl_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Midictl_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Midictl_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)Midictl_stop, METH_NOARGS, "Stops computing."},
	{"setInterpolation", (PyCFunction)Midictl_setInterpolation, METH_O, "Activate/Deactivate interpolation."},
	{"setValue", (PyCFunction)Midictl_setValue, METH_O, "Resets audio stream to value in argument."},
	{"setMinScale", (PyCFunction)Midictl_setMinScale, METH_O, "Sets the minimum value of scaling."},
	{"setMaxScale", (PyCFunction)Midictl_setMaxScale, METH_O, "Sets the maximum value of scaling."},
	{"setCtlNumber", (PyCFunction)Midictl_setCtlNumber, METH_O, "Sets the controller number."},
	{"setChannel", (PyCFunction)Midictl_setChannel, METH_O, "Sets the midi channel."},
	{"setMul", (PyCFunction)Midictl_setMul, METH_O, "Sets oscillator mul factor."},
	{"setAdd", (PyCFunction)Midictl_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Midictl_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Midictl_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Midictl_as_number = {
    (binaryfunc)Midictl_add,                      /*nb_add*/
    (binaryfunc)Midictl_sub,                 /*nb_subtract*/
    (binaryfunc)Midictl_multiply,                 /*nb_multiply*/
    (binaryfunc)Midictl_div,                   /*nb_divide*/
    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_coerce*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    0,                       /*nb_oct*/
    0,                       /*nb_hex*/
    (binaryfunc)Midictl_inplace_add,              /*inplace_add*/
    (binaryfunc)Midictl_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)Midictl_inplace_multiply,         /*inplace_multiply*/
    (binaryfunc)Midictl_inplace_div,           /*inplace_divide*/
    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*/
    0,              /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    0,      /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject MidictlType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.Midictl_base",         /*tp_name*/
    sizeof(Midictl),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)Midictl_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &Midictl_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 | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "Midictl objects. Retreive audio from an input channel.",           /* tp_doc */
    (traverseproc)Midictl_traverse,   /* tp_traverse */
    (inquiry)Midictl_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    Midictl_methods,             /* tp_methods */
    Midictl_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 */
    Midictl_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    int channel;
    int scale; /* 0 = midi, 1 = transpo */
    MYFLT range;
    MYFLT value;
    MYFLT oldValue;
    MYFLT sampleToSec;
    int modebuffer[2];
} Bendin;

static void Bendin_postprocessing_ii(Bendin *self) { POST_PROCESSING_II };
static void Bendin_postprocessing_ai(Bendin *self) { POST_PROCESSING_AI };
static void Bendin_postprocessing_ia(Bendin *self) { POST_PROCESSING_IA };
static void Bendin_postprocessing_aa(Bendin *self) { POST_PROCESSING_AA };
static void Bendin_postprocessing_ireva(Bendin *self) { POST_PROCESSING_IREVA };
static void Bendin_postprocessing_areva(Bendin *self) { POST_PROCESSING_AREVA };
static void Bendin_postprocessing_revai(Bendin *self) { POST_PROCESSING_REVAI };
static void Bendin_postprocessing_revaa(Bendin *self) { POST_PROCESSING_REVAA };
static void Bendin_postprocessing_revareva(Bendin *self) { POST_PROCESSING_REVAREVA };

static void
Bendin_setProcMode(Bendin *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    
	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = Bendin_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = Bendin_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = Bendin_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = Bendin_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = Bendin_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = Bendin_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = Bendin_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = Bendin_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = Bendin_postprocessing_revareva;
            break;
    }
}

// Take MIDI events and translate them...
void Bendin_translateMidi(Bendin *self, PmEvent *buffer, int count)
{
    int i, ok;
    MYFLT val;
    for (i=count-1; i>=0; i--) {
        int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
        int number = Pm_MessageData1(buffer[i].message);
        int value = Pm_MessageData2(buffer[i].message);
        
        if (self->channel == 0) {
            if ((status & 0xF0) == 0xe0)
                ok = 1;
            else
                ok = 0;
        }
        else {
            if (status == (0xe0 | (self->channel - 1)))
                ok = 1;
            else
                ok = 0;
        }
        
        if (ok == 1) {
            self->oldValue = self->value;
            val = (number + (value << 7) - 8192) / 8192.0 * self->range;
            if (self->scale == 0)
                self->value = val;
            else
                self->value = MYPOW(1.0594630943593, val);
            break;
        }
    }    
}

static void
Bendin_compute_next_data_frame(Bendin *self)
{   
    PmEvent *tmp;
    int i, count;
    
    tmp = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);
    
    if (count > 0)
        Bendin_translateMidi((Bendin *)self, tmp, count);
    MYFLT step = (self->value - self->oldValue) / self->bufsize;
    
    for (i=0; i<self->bufsize; i++) {
        self->data[i] = self->oldValue + step;
    }  
    
    (*self->muladd_func_ptr)(self);
}

static int
Bendin_traverse(Bendin *self, visitproc visit, void *arg)
{
    pyo_VISIT
    return 0;
}

static int 
Bendin_clear(Bendin *self)
{
    pyo_CLEAR
    return 0;
}

static void
Bendin_dealloc(Bendin* self)
{
    pyo_DEALLOC
    Bendin_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
Bendin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *multmp=NULL, *addtmp=NULL;    
    Bendin *self;
    self = (Bendin *)type->tp_alloc(type, 0);
    
    self->channel = 0;
    self->scale = 0;
    self->value = 0.;
    self->oldValue = 0.;
    self->range = 2.;
	self->modebuffer[0] = 0;
	self->modebuffer[1] = 0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Bendin_compute_next_data_frame);
    self->mode_func_ptr = Bendin_setProcMode;

    static char *kwlist[] = {"brange", "scale", "channel", "mul", "add", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE__FIIOO, kwlist, &self->range, &self->scale, &self->channel, &multmp, &addtmp))
        Py_RETURN_NONE;
    
    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->scale == 0)
        self->oldValue = self->value = 0.;
    else
        self->oldValue = self->value = 1.;
        
    (*self->mode_func_ptr)(self);
    
    return (PyObject *)self;
}

static PyObject * Bendin_getServer(Bendin* self) { GET_SERVER };
static PyObject * Bendin_getStream(Bendin* self) { GET_STREAM };
static PyObject * Bendin_setMul(Bendin *self, PyObject *arg) { SET_MUL };	
static PyObject * Bendin_setAdd(Bendin *self, PyObject *arg) { SET_ADD };	
static PyObject * Bendin_setSub(Bendin *self, PyObject *arg) { SET_SUB };	
static PyObject * Bendin_setDiv(Bendin *self, PyObject *arg) { SET_DIV };	

static PyObject * Bendin_play(Bendin *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Bendin_stop(Bendin *self) { STOP };

static PyObject * Bendin_multiply(Bendin *self, PyObject *arg) { MULTIPLY };
static PyObject * Bendin_inplace_multiply(Bendin *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Bendin_add(Bendin *self, PyObject *arg) { ADD };
static PyObject * Bendin_inplace_add(Bendin *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Bendin_sub(Bendin *self, PyObject *arg) { SUB };
static PyObject * Bendin_inplace_sub(Bendin *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Bendin_div(Bendin *self, PyObject *arg) { DIV };
static PyObject * Bendin_inplace_div(Bendin *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Bendin_setBrange(Bendin *self, PyObject *arg)
{
    MYFLT tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyNumber_Check(arg);
    
	if (isNum == 1) {
		tmp = PyFloat_AsDouble(PyNumber_Float(arg));
        if (tmp >= 0.0 && tmp < 128.0)
            self->range = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Bendin_setChannel(Bendin *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= 0 && tmp < 128)
            self->channel = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Bendin_setScale(Bendin *self, PyObject *arg)
{
    int tmp;
    
    if (arg == NULL) {
        Py_INCREF(Py_None);
        return Py_None;
    }
    
    int isInt = PyInt_Check(arg);
    
    if (isInt == 1) {
        tmp = PyInt_AsLong(arg);
        if (tmp == 0)
            self->scale = 0;
        else if (tmp == 1)
            self->scale = 1;
    }
    
    Py_INCREF(Py_None);
    return Py_None;
}	


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

static PyMethodDef Bendin_methods[] = {
    {"getServer", (PyCFunction)Bendin_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Bendin_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Bendin_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)Bendin_stop, METH_NOARGS, "Stops computing."},
	{"setBrange", (PyCFunction)Bendin_setBrange, METH_O, "Sets the bending bipolar range."},
	{"setScale", (PyCFunction)Bendin_setScale, METH_O, "Sets the output type, midi vs transpo."},
	{"setChannel", (PyCFunction)Bendin_setChannel, METH_O, "Sets the midi channel."},
	{"setMul", (PyCFunction)Bendin_setMul, METH_O, "Sets oscillator mul factor."},
	{"setAdd", (PyCFunction)Bendin_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Bendin_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Bendin_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Bendin_as_number = {
    (binaryfunc)Bendin_add,                      /*nb_add*/
    (binaryfunc)Bendin_sub,                 /*nb_subtract*/
    (binaryfunc)Bendin_multiply,                 /*nb_multiply*/
    (binaryfunc)Bendin_div,                   /*nb_divide*/
    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_coerce*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    0,                       /*nb_oct*/
    0,                       /*nb_hex*/
    (binaryfunc)Bendin_inplace_add,              /*inplace_add*/
    (binaryfunc)Bendin_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)Bendin_inplace_multiply,         /*inplace_multiply*/
    (binaryfunc)Bendin_inplace_div,           /*inplace_divide*/
    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*/
    0,              /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    0,      /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject BendinType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.Bendin_base",         /*tp_name*/
    sizeof(Bendin),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)Bendin_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &Bendin_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 | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "Bendin objects. Retreive audio from an input channel.",           /* tp_doc */
    (traverseproc)Bendin_traverse,   /* tp_traverse */
    (inquiry)Bendin_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    Bendin_methods,             /* tp_methods */
    Bendin_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 */
    Bendin_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    int channel;
    MYFLT minscale;
    MYFLT maxscale;
    MYFLT value;
    MYFLT oldValue;
    MYFLT sampleToSec;
    int modebuffer[2];
} Touchin;

static void Touchin_postprocessing_ii(Touchin *self) { POST_PROCESSING_II };
static void Touchin_postprocessing_ai(Touchin *self) { POST_PROCESSING_AI };
static void Touchin_postprocessing_ia(Touchin *self) { POST_PROCESSING_IA };
static void Touchin_postprocessing_aa(Touchin *self) { POST_PROCESSING_AA };
static void Touchin_postprocessing_ireva(Touchin *self) { POST_PROCESSING_IREVA };
static void Touchin_postprocessing_areva(Touchin *self) { POST_PROCESSING_AREVA };
static void Touchin_postprocessing_revai(Touchin *self) { POST_PROCESSING_REVAI };
static void Touchin_postprocessing_revaa(Touchin *self) { POST_PROCESSING_REVAA };
static void Touchin_postprocessing_revareva(Touchin *self) { POST_PROCESSING_REVAREVA };

static void
Touchin_setProcMode(Touchin *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    
	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = Touchin_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = Touchin_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = Touchin_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = Touchin_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = Touchin_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = Touchin_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = Touchin_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = Touchin_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = Touchin_postprocessing_revareva;
            break;
    }
}

// Take MIDI events and translate them...
void Touchin_translateMidi(Touchin *self, PmEvent *buffer, int count)
{
    int i, ok;
    for (i=count-1; i>=0; i--) {
        int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
        int number = Pm_MessageData1(buffer[i].message);
        /* int value = Pm_MessageData2(buffer[i].message); */

        if (self->channel == 0) {
            if ((status & 0xF0) == 0xd0)
                ok = 1;
            else
                ok = 0;
        }
        else {
            if (status == (0xd0 | (self->channel - 1)))
                ok = 1;
            else
                ok = 0;
        }
        
        if (ok == 1) {
            self->oldValue = self->value;
            self->value = (number / 127.) * (self->maxscale - self->minscale) + self->minscale;
            break;
        }
    }    
}

static void
Touchin_compute_next_data_frame(Touchin *self)
{   
    PmEvent *tmp;
    int i, count;
    
    tmp = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);
    
    if (count > 0)
        Touchin_translateMidi((Touchin *)self, tmp, count);
    MYFLT step = (self->value - self->oldValue) / self->bufsize;
    
    for (i=0; i<self->bufsize; i++) {
        self->data[i] = self->oldValue + step;
    }  
    
    (*self->muladd_func_ptr)(self);
}

static int
Touchin_traverse(Touchin *self, visitproc visit, void *arg)
{
    pyo_VISIT
    return 0;
}

static int 
Touchin_clear(Touchin *self)
{
    pyo_CLEAR
    return 0;
}

static void
Touchin_dealloc(Touchin* self)
{
    pyo_DEALLOC
    Touchin_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
Touchin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *multmp=NULL, *addtmp=NULL;    
    Touchin *self;
    self = (Touchin *)type->tp_alloc(type, 0);
    
    self->channel = 0;
    self->value = 0.;
    self->oldValue = 0.;
    self->minscale = 0.;
    self->maxscale = 1.;
	self->modebuffer[0] = 0;
	self->modebuffer[1] = 0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Touchin_compute_next_data_frame);
    self->mode_func_ptr = Touchin_setProcMode;

    static char *kwlist[] = {"minscale", "maxscale", "init", "channel", "mul", "add", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE__FFFIOO, kwlist, &self->minscale, &self->maxscale, &self->oldValue, &self->channel, &multmp, &addtmp))
        Py_RETURN_NONE;
    
    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->value = self->oldValue;
    
    (*self->mode_func_ptr)(self);
    
    return (PyObject *)self;
}

static PyObject * Touchin_getServer(Touchin* self) { GET_SERVER };
static PyObject * Touchin_getStream(Touchin* self) { GET_STREAM };
static PyObject * Touchin_setMul(Touchin *self, PyObject *arg) { SET_MUL };	
static PyObject * Touchin_setAdd(Touchin *self, PyObject *arg) { SET_ADD };	
static PyObject * Touchin_setSub(Touchin *self, PyObject *arg) { SET_SUB };	
static PyObject * Touchin_setDiv(Touchin *self, PyObject *arg) { SET_DIV };	

static PyObject * Touchin_play(Touchin *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Touchin_stop(Touchin *self) { STOP };

static PyObject * Touchin_multiply(Touchin *self, PyObject *arg) { MULTIPLY };
static PyObject * Touchin_inplace_multiply(Touchin *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Touchin_add(Touchin *self, PyObject *arg) { ADD };
static PyObject * Touchin_inplace_add(Touchin *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Touchin_sub(Touchin *self, PyObject *arg) { SUB };
static PyObject * Touchin_inplace_sub(Touchin *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Touchin_div(Touchin *self, PyObject *arg) { DIV };
static PyObject * Touchin_inplace_div(Touchin *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Touchin_setMinScale(Touchin *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyNumber_Check(arg);
    
	if (isNum == 1) {
		tmp = PyFloat_AsDouble(PyNumber_Float(arg));
            self->minscale = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Touchin_setMaxScale(Touchin *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isNum = PyNumber_Check(arg);
    
	if (isNum == 1) {
		tmp = PyFloat_AsDouble(PyNumber_Float(arg));
        self->maxscale = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
Touchin_setChannel(Touchin *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= 0 && tmp < 128)
            self->channel = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

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

static PyMethodDef Touchin_methods[] = {
    {"getServer", (PyCFunction)Touchin_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Touchin_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Touchin_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)Touchin_stop, METH_NOARGS, "Stops computing."},
	{"setMinScale", (PyCFunction)Touchin_setMinScale, METH_O, "Sets the minimum value of scaling."},
	{"setMaxScale", (PyCFunction)Touchin_setMaxScale, METH_O, "Sets the maximum value of scaling."},
	{"setChannel", (PyCFunction)Touchin_setChannel, METH_O, "Sets the midi channel."},
	{"setMul", (PyCFunction)Touchin_setMul, METH_O, "Sets oscillator mul factor."},
	{"setAdd", (PyCFunction)Touchin_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Touchin_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Touchin_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Touchin_as_number = {
    (binaryfunc)Touchin_add,                      /*nb_add*/
    (binaryfunc)Touchin_sub,                 /*nb_subtract*/
    (binaryfunc)Touchin_multiply,                 /*nb_multiply*/
    (binaryfunc)Touchin_div,                   /*nb_divide*/
    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_coerce*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    0,                       /*nb_oct*/
    0,                       /*nb_hex*/
    (binaryfunc)Touchin_inplace_add,              /*inplace_add*/
    (binaryfunc)Touchin_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)Touchin_inplace_multiply,         /*inplace_multiply*/
    (binaryfunc)Touchin_inplace_div,           /*inplace_divide*/
    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*/
    0,              /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    0,      /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject TouchinType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.Touchin_base",         /*tp_name*/
    sizeof(Touchin),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)Touchin_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &Touchin_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 | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "Touchin objects. Retrieve the signal of an aftertouch midi controller.",           /* tp_doc */
    (traverseproc)Touchin_traverse,   /* tp_traverse */
    (inquiry)Touchin_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    Touchin_methods,             /* tp_methods */
    Touchin_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 */
    Touchin_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    int channel;
    MYFLT value;
    int modebuffer[2];
} Programin;

static void Programin_postprocessing_ii(Programin *self) { POST_PROCESSING_II };
static void Programin_postprocessing_ai(Programin *self) { POST_PROCESSING_AI };
static void Programin_postprocessing_ia(Programin *self) { POST_PROCESSING_IA };
static void Programin_postprocessing_aa(Programin *self) { POST_PROCESSING_AA };
static void Programin_postprocessing_ireva(Programin *self) { POST_PROCESSING_IREVA };
static void Programin_postprocessing_areva(Programin *self) { POST_PROCESSING_AREVA };
static void Programin_postprocessing_revai(Programin *self) { POST_PROCESSING_REVAI };
static void Programin_postprocessing_revaa(Programin *self) { POST_PROCESSING_REVAA };
static void Programin_postprocessing_revareva(Programin *self) { POST_PROCESSING_REVAREVA };

static void
Programin_setProcMode(Programin *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    
	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = Programin_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = Programin_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = Programin_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = Programin_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = Programin_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = Programin_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = Programin_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = Programin_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = Programin_postprocessing_revareva;
            break;
    }
}

// Take MIDI events and translate them...
void Programin_translateMidi(Programin *self, PmEvent *buffer, int count)
{
    int i, ok;
    
    for (i=count-1; i>=0; i--) {
        int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
        int number = Pm_MessageData1(buffer[i].message);
        
        if (self->channel == 0) {
            if ((status & 0xF0) == 0xc0)
                ok = 1;
            else
                ok = 0;
        }
        else {
            if (status == (0xc0 | (self->channel - 1)))
                ok = 1;
            else
                ok = 0;
        }
        
        if (ok == 1) {
            self->value = (MYFLT)number;
            break;
        }
    }    
}

static void
Programin_compute_next_data_frame(Programin *self)
{   
    PmEvent *tmp;
    int i, count;
    
    tmp = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);
    
    if (count > 0)
        Programin_translateMidi((Programin *)self, tmp, count);
    
    for (i=0; i<self->bufsize; i++) {
        self->data[i] = self->value;
    }  
    
    (*self->muladd_func_ptr)(self);
}

static int
Programin_traverse(Programin *self, visitproc visit, void *arg)
{
    pyo_VISIT
    return 0;
}

static int 
Programin_clear(Programin *self)
{
    pyo_CLEAR
    return 0;
}

static void
Programin_dealloc(Programin* self)
{
    pyo_DEALLOC
    Programin_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
Programin_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *multmp=NULL, *addtmp=NULL;    
    Programin *self;
    self = (Programin *)type->tp_alloc(type, 0);
    
    self->channel = 0;
    self->value = 0.;
	self->modebuffer[0] = 0;
	self->modebuffer[1] = 0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Programin_compute_next_data_frame);
    self->mode_func_ptr = Programin_setProcMode;

    static char *kwlist[] = {"channel", "mul", "add", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, "iOO", kwlist, &self->channel, &multmp, &addtmp))
        Py_RETURN_NONE;
    
    if (multmp) {
        PyObject_CallMethod((PyObject *)self, "setMul", "O", multmp);
    }
    
    if (addtmp) {
        PyObject_CallMethod((PyObject *)self, "setAdd", "O", addtmp);
    }
    
    PyObject_CallMethod(self->server, "addStream", "O", self->stream);

    (*self->mode_func_ptr)(self);
    
    return (PyObject *)self;
}

static PyObject * Programin_getServer(Programin* self) { GET_SERVER };
static PyObject * Programin_getStream(Programin* self) { GET_STREAM };
static PyObject * Programin_setMul(Programin *self, PyObject *arg) { SET_MUL };	
static PyObject * Programin_setAdd(Programin *self, PyObject *arg) { SET_ADD };	
static PyObject * Programin_setSub(Programin *self, PyObject *arg) { SET_SUB };	
static PyObject * Programin_setDiv(Programin *self, PyObject *arg) { SET_DIV };	

static PyObject * Programin_play(Programin *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Programin_stop(Programin *self) { STOP };

static PyObject * Programin_multiply(Programin *self, PyObject *arg) { MULTIPLY };
static PyObject * Programin_inplace_multiply(Programin *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Programin_add(Programin *self, PyObject *arg) { ADD };
static PyObject * Programin_inplace_add(Programin *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Programin_sub(Programin *self, PyObject *arg) { SUB };
static PyObject * Programin_inplace_sub(Programin *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Programin_div(Programin *self, PyObject *arg) { DIV };
static PyObject * Programin_inplace_div(Programin *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
Programin_setChannel(Programin *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= 0 && tmp < 128)
            self->channel = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

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

static PyMethodDef Programin_methods[] = {
    {"getServer", (PyCFunction)Programin_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)Programin_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)Programin_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)Programin_stop, METH_NOARGS, "Stops computing."},
	{"setChannel", (PyCFunction)Programin_setChannel, METH_O, "Sets the midi channel."},
	{"setMul", (PyCFunction)Programin_setMul, METH_O, "Sets oscillator mul factor."},
	{"setAdd", (PyCFunction)Programin_setAdd, METH_O, "Sets oscillator add factor."},
    {"setSub", (PyCFunction)Programin_setSub, METH_O, "Sets inverse add factor."},
    {"setDiv", (PyCFunction)Programin_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods Programin_as_number = {
    (binaryfunc)Programin_add,                      /*nb_add*/
    (binaryfunc)Programin_sub,                 /*nb_subtract*/
    (binaryfunc)Programin_multiply,                 /*nb_multiply*/
    (binaryfunc)Programin_div,                   /*nb_divide*/
    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_coerce*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    0,                       /*nb_oct*/
    0,                       /*nb_hex*/
    (binaryfunc)Programin_inplace_add,              /*inplace_add*/
    (binaryfunc)Programin_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)Programin_inplace_multiply,         /*inplace_multiply*/
    (binaryfunc)Programin_inplace_div,           /*inplace_divide*/
    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*/
    0,              /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    0,      /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject PrograminType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.Programin_base",         /*tp_name*/
    sizeof(Programin),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)Programin_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &Programin_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 | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "Programin objects. Retrieve the signal of a program change midi controller.",           /* tp_doc */
    (traverseproc)Programin_traverse,   /* tp_traverse */
    (inquiry)Programin_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    Programin_methods,             /* tp_methods */
    Programin_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 */
    Programin_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    int *notebuf; /* pitch, velocity, ... */
    int voices;
    int vcount;
    int scale; /* 0 = midi, 1 = hertz, 2 = transpo */
    int first;
    int last;
    int centralkey;
    int channel;
    int stealing;
} MidiNote;

static void
MidiNote_setProcMode(MidiNote *self) {};

int
pitchIsIn(int *buf, int pitch, int len) {
    int i;
    int isIn = 0;
    for (i=0; i<len; i++) {
        if (buf[i*2] == pitch) {
            isIn = 1;
            break;
        }    
    }
    return isIn;
}

/* no more used */
int firstEmpty(int *buf, int len) {
    int i;
    int voice = -1;
    for (i=0; i<len; i++) {
        if (buf[i*2+1] == 0) {
            voice = i;
            break;
        }    
    }
    return voice;
}

int nextEmptyVoice(int *buf, int voice, int len) {
    int i, tmp;
    int next = -1;
    for (i=0; i<len; i++) {
        tmp = (i + voice) % len;
        if (buf[tmp*2+1] == 0) {
            next = tmp;
            break;
        }
    }    
    return next;    
}

int whichVoice(int *buf, int pitch, int len) {
    int i;
    int voice = 0;
    for (i=0; i<len; i++) {
        if (buf[i*2] == pitch) {
            voice = i;
            break;
        }
    }
    return voice;
}

// Take MIDI events and keep track of notes
void grabMidiNotes(MidiNote *self, PmEvent *buffer, int count)
{
    int i, ok, voice, kind;
    
    for (i=0; i<count; i++) {
        int status = Pm_MessageStatus(buffer[i].message);	// Temp note event holders
        int pitch = Pm_MessageData1(buffer[i].message);
        int velocity = Pm_MessageData2(buffer[i].message);

        if (self->channel == 0) {
            if ((status & 0xF0) == 0x90 || (status & 0xF0) == 0x80)
                ok = 1;
            else
                ok = 0;
        }
        else {
            if ( status == (0x90 | (self->channel - 1)) || status == (0x80 | (self->channel - 1)))
                ok = 1;
            else
                ok = 0;
        }
        
        if (ok == 1) {
            if ((status & 0xF0) == 0x80)
                kind = 0;
            else if ((status & 0xF0) == 0x90 && velocity == 0)
                kind = 0;
            else
                kind = 1;

            if (pitchIsIn(self->notebuf, pitch, self->voices) == 0 && kind == 1 && pitch >= self->first && pitch <= self->last) {
                //printf("%i, %i, %i\n", status, pitch, velocity);
                if (!self->stealing) {
                    voice = nextEmptyVoice(self->notebuf, self->vcount, self->voices);
                    if (voice != -1) {
                        self->vcount = voice;
                        self->notebuf[voice*2] = pitch;
                        self->notebuf[voice*2+1] = velocity;
                    }
                }
                else {
                    self->vcount = (self->vcount + 1) % self->voices;
                    self->notebuf[self->vcount*2] = pitch;
                    self->notebuf[self->vcount*2+1] = velocity;
                }
            }    
            else if (pitchIsIn(self->notebuf, pitch, self->voices) == 1 && kind == 0 && pitch >= self->first && pitch <= self->last) {
                //printf("%i, %i, %i\n", status, pitch, velocity);
                voice = whichVoice(self->notebuf, pitch, self->voices);
                self->notebuf[voice*2] = -1;
                self->notebuf[voice*2+1] = 0.;
            }
        }
    }    
}    

static void
MidiNote_compute_next_data_frame(MidiNote *self)
{   
    PmEvent *tmp;
    int count;
    
    tmp = Server_getMidiEventBuffer((Server *)self->server);
    count = Server_getMidiEventCount((Server *)self->server);
    if (count > 0)
        grabMidiNotes((MidiNote *)self, tmp, count);  
}

static int
MidiNote_traverse(MidiNote *self, visitproc visit, void *arg)
{
    pyo_VISIT
    return 0;
}

static int 
MidiNote_clear(MidiNote *self)
{
    pyo_CLEAR
    return 0;
}

static void
MidiNote_dealloc(MidiNote* self)
{
    pyo_DEALLOC
    free(self->notebuf);
    MidiNote_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
MidiNote_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    
    MidiNote *self;
    self = (MidiNote *)type->tp_alloc(type, 0);

    self->voices = 10;
    self->vcount = 0;
    self->scale = 0;
    self->first = 0;
    self->last = 127;
    self->channel = 0;
    self->stealing = 0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, MidiNote_compute_next_data_frame);
    self->mode_func_ptr = MidiNote_setProcMode;

    static char *kwlist[] = {"voices", "scale", "first", "last", "channel", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iiiii", kwlist, &self->voices, &self->scale, &self->first, &self->last, &self->channel))
        Py_RETURN_NONE;

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

    self->notebuf = (int *)realloc(self->notebuf, self->voices * 2 * sizeof(int));

    for (i=0; i<self->voices; i++) {
        self->notebuf[i*2] = -1;
        self->notebuf[i*2+1] = 0;
    }

    self->centralkey = (self->first + self->last) / 2;
    
    (*self->mode_func_ptr)(self);
     
    return (PyObject *)self;
}

MYFLT MidiNote_getValue(MidiNote *self, int voice, int which)
{
    MYFLT val = -1.0;
    int midival = self->notebuf[voice*2+which];
    if (which == 0 && midival != -1) {
        if (self->scale == 0)
            val = 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 if (which == 0)
        val = (MYFLT)midival;
    else if (which == 1)
        val = (MYFLT)midival / 127.;
    return val;
}

static PyObject * MidiNote_getServer(MidiNote* self) { GET_SERVER };
static PyObject * MidiNote_getStream(MidiNote* self) { GET_STREAM };

static PyObject * MidiNote_play(MidiNote *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * MidiNote_stop(MidiNote *self) { STOP };

static PyObject *
MidiNote_setChannel(MidiNote *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= 0 && tmp < 128)
            self->channel = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
MidiNote_setCentralKey(MidiNote *self, PyObject *arg)
{
    int tmp;
	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1) {
		tmp = PyInt_AsLong(arg);
        if (tmp >= self->first && tmp <= self->last)
            self->centralkey = tmp;
	}
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyObject *
MidiNote_setStealing(MidiNote *self, PyObject *arg)
{	
	if (arg == NULL) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	
	int isInt = PyInt_Check(arg);
    
	if (isInt == 1)
		self->stealing = PyInt_AsLong(arg);
    
	Py_INCREF(Py_None);
	return Py_None;
}	

static PyMemberDef MidiNote_members[] = {
{"server", T_OBJECT_EX, offsetof(MidiNote, server), 0, "Pyo server."},
{"stream", T_OBJECT_EX, offsetof(MidiNote, stream), 0, "Stream object."},
{NULL}  /* Sentinel */
};

static PyMethodDef MidiNote_methods[] = {
{"getServer", (PyCFunction)MidiNote_getServer, METH_NOARGS, "Returns server object."},
{"_getStream", (PyCFunction)MidiNote_getStream, METH_NOARGS, "Returns stream object."},
{"play", (PyCFunction)MidiNote_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
{"stop", (PyCFunction)MidiNote_stop, METH_NOARGS, "Stops computing."},
{"setChannel", (PyCFunction)MidiNote_setChannel, METH_O, "Sets the midi channel."},
{"setCentralKey", (PyCFunction)MidiNote_setCentralKey, METH_O, "Sets the midi key where there is no transposition."},
{"setStealing", (PyCFunction)MidiNote_setStealing, METH_O, "Sets the stealing mode."},
{NULL}  /* Sentinel */
};

PyTypeObject MidiNoteType = {
PyObject_HEAD_INIT(NULL)
0,                         /*ob_size*/
"_pyo.MidiNote_base",         /*tp_name*/
sizeof(MidiNote),         /*tp_basicsize*/
0,                         /*tp_itemsize*/
(destructor)MidiNote_dealloc, /*tp_dealloc*/
0,                         /*tp_print*/
0,                         /*tp_getattr*/
0,                         /*tp_setattr*/
0,                         /*tp_compare*/
0,                         /*tp_repr*/
0,             /*tp_as_number*/
0,                         /*tp_as_sequence*/
0,                         /*tp_as_mapping*/
0,                         /*tp_hash */
0,                         /*tp_call*/
0,                         /*tp_str*/
0,                         /*tp_getattro*/
0,                         /*tp_setattro*/
0,                         /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
"MidiNote objects. Retreive midi note from a midi input.",           /* tp_doc */
(traverseproc)MidiNote_traverse,   /* tp_traverse */
(inquiry)MidiNote_clear,           /* tp_clear */
0,		               /* tp_richcompare */
0,		               /* tp_weaklistoffset */
0,		               /* tp_iter */
0,		               /* tp_iternext */
MidiNote_methods,             /* tp_methods */
MidiNote_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 */
MidiNote_new,                 /* tp_new */
};


/* Notein streamer */
typedef struct {
    pyo_audio_HEAD
    MidiNote *handler;
    int modebuffer[2];
    int voice;
    int mode; /* 0 = pitch, 1 = velocity */
} Notein;

static void Notein_postprocessing_ii(Notein *self) { POST_PROCESSING_II };
static void Notein_postprocessing_ai(Notein *self) { POST_PROCESSING_AI };
static void Notein_postprocessing_ia(Notein *self) { POST_PROCESSING_IA };
static void Notein_postprocessing_aa(Notein *self) { POST_PROCESSING_AA };
static void Notein_postprocessing_ireva(Notein *self) { POST_PROCESSING_IREVA };
static void Notein_postprocessing_areva(Notein *self) { POST_PROCESSING_AREVA };
static void Notein_postprocessing_revai(Notein *self) { POST_PROCESSING_REVAI };
static void Notein_postprocessing_revaa(Notein *self) { POST_PROCESSING_REVAA };
static void Notein_postprocessing_revareva(Notein *self) { POST_PROCESSING_REVAREVA };

static void
Notein_setProcMode(Notein *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    
	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = Notein_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = Notein_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = Notein_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = Notein_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = Notein_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = Notein_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = Notein_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = Notein_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = Notein_postprocessing_revareva;
            break;
    }
}

static void
Notein_compute_next_data_frame(Notein *self)
{
    int i;
    MYFLT tmp = MidiNote_getValue(self->handler, self->voice, self->mode);
    
    if (self->mode == 0 && tmp != -1) {
        for (i=0; i<self->bufsize; i++) {
            self->data[i] = tmp;
        }    
    } 
    else if (self->mode == 1) {
        for (i=0; i<self->bufsize; i++) {
            self->data[i] = tmp;
        }         
        (*self->muladd_func_ptr)(self);
    }    
}

static int
Notein_traverse(Notein *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->handler);
    return 0;
}

static int 
Notein_clear(Notein *self)
{
    pyo_CLEAR
    Py_CLEAR(self->handler);    
    return 0;
}

static void
Notein_dealloc(Notein* self)
{
    pyo_DEALLOC
    Notein_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
Notein_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *handlertmp=NULL, *multmp=NULL, *addtmp=NULL;
    Notein *self;
    self = (Notein *)type->tp_alloc(type, 0);
    
    self->voice = 0;
    self->mode = 0;
    self->modebuffer[0] = 0;
    self->modebuffer[1] = 0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, Notein_compute_next_data_frame);
    self->mode_func_ptr = Notein_setProcMode;

    static char *kwlist[] = {"handler", "voice", "mode", "mul", "add", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O|iiOO", kwlist, &handlertmp, &self->voice, &self->mode, &multmp, &addtmp))
        Py_RETURN_NONE;
    
    Py_XDECREF(self->handler);
    Py_INCREF(handlertmp);
    self->handler = (MidiNote *)handlertmp;
    
    if (multmp) {
        PyObject_CallMethod((PyObject *)self, "setMul", "O", multmp);
    }
    
    if (addtmp) {
        PyObject_CallMethod((PyObject *)self, "setAdd", "O", addtmp);
    }
    
    PyObject_CallMethod(self->server, "addStream", "O", self->stream);
    
    (*self->mode_func_ptr)(self);
    
    return (PyObject *)self;
}

static PyObject * Notein_getServer(Notein* self) { GET_SERVER };
static PyObject * Notein_getStream(Notein* self) { GET_STREAM };
static PyObject * Notein_setMul(Notein *self, PyObject *arg) { SET_MUL };	
static PyObject * Notein_setAdd(Notein *self, PyObject *arg) { SET_ADD };	
static PyObject * Notein_setSub(Notein *self, PyObject *arg) { SET_SUB };	
static PyObject * Notein_setDiv(Notein *self, PyObject *arg) { SET_DIV };	

static PyObject * Notein_play(Notein *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * Notein_stop(Notein *self) { STOP };

static PyObject * Notein_multiply(Notein *self, PyObject *arg) { MULTIPLY };
static PyObject * Notein_inplace_multiply(Notein *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * Notein_add(Notein *self, PyObject *arg) { ADD };
static PyObject * Notein_inplace_add(Notein *self, PyObject *arg) { INPLACE_ADD };
static PyObject * Notein_sub(Notein *self, PyObject *arg) { SUB };
static PyObject * Notein_inplace_sub(Notein *self, PyObject *arg) { INPLACE_SUB };
static PyObject * Notein_div(Notein *self, PyObject *arg) { DIV };
static PyObject * Notein_inplace_div(Notein *self, PyObject *arg) { INPLACE_DIV };

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

static PyMethodDef Notein_methods[] = {
{"getServer", (PyCFunction)Notein_getServer, METH_NOARGS, "Returns server object."},
{"_getStream", (PyCFunction)Notein_getStream, METH_NOARGS, "Returns stream object."},
{"play", (PyCFunction)Notein_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
{"stop", (PyCFunction)Notein_stop, METH_NOARGS, "Stops computing."},
{"setMul", (PyCFunction)Notein_setMul, METH_O, "Sets Notein mul factor."},
{"setAdd", (PyCFunction)Notein_setAdd, METH_O, "Sets Notein add factor."},
{"setSub", (PyCFunction)Notein_setSub, METH_O, "Sets inverse add factor."},
{"setDiv", (PyCFunction)Notein_setDiv, METH_O, "Sets inverse mul factor."},
{NULL}  /* Sentinel */
};

static PyNumberMethods Notein_as_number = {
(binaryfunc)Notein_add,                      /*nb_add*/
(binaryfunc)Notein_sub,                 /*nb_subtract*/
(binaryfunc)Notein_multiply,                 /*nb_multiply*/
(binaryfunc)Notein_div,                   /*nb_divide*/
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_coerce*/
0,                       /*nb_int*/
0,                      /*nb_long*/
0,                     /*nb_float*/
0,                       /*nb_oct*/
0,                       /*nb_hex*/
(binaryfunc)Notein_inplace_add,              /*inplace_add*/
(binaryfunc)Notein_inplace_sub,         /*inplace_subtract*/
(binaryfunc)Notein_inplace_multiply,         /*inplace_multiply*/
(binaryfunc)Notein_inplace_div,           /*inplace_divide*/
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*/
0,              /*nb_true_divide*/
0,     /*nb_inplace_floor_divide*/
0,      /*nb_inplace_true_divide*/
0,                     /* nb_index */
};

PyTypeObject NoteinType = {
PyObject_HEAD_INIT(NULL)
0,                         /*ob_size*/
"_pyo.Notein_base",         /*tp_name*/
sizeof(Notein),         /*tp_basicsize*/
0,                         /*tp_itemsize*/
(destructor)Notein_dealloc, /*tp_dealloc*/
0,                         /*tp_print*/
0,                         /*tp_getattr*/
0,                         /*tp_setattr*/
0,                         /*tp_compare*/
0,                         /*tp_repr*/
&Notein_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 | Py_TPFLAGS_CHECKTYPES,  /*tp_flags*/
"Notein objects. Stream pitch or velocity from a Notein voice.",           /* tp_doc */
(traverseproc)Notein_traverse,   /* tp_traverse */
(inquiry)Notein_clear,           /* tp_clear */
0,		               /* tp_richcompare */
0,		               /* tp_weaklistoffset */
0,		               /* tp_iter */
0,		               /* tp_iternext */
Notein_methods,             /* tp_methods */
Notein_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 */
Notein_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    PyObject *input;
    Stream *input_stream;
    int modebuffer[2];
    int fademode;
    int changed;
    MYFLT topValue;
    MYFLT offsetAmp;
    MYFLT initAmp;
    MYFLT sustainAmp;
    MYFLT attack;
    MYFLT decay;
    MYFLT sustain;
    MYFLT release;
    MYFLT invAttack;
    MYFLT initAmpMinusOffsetAmp;
    MYFLT attackPlusDecay;
    MYFLT invDecay;
    MYFLT initAmpMinusSustainAmp;
    MYFLT invRelease;
    double currentTime;
    MYFLT sampleToSec;
} MidiAdsr;

static void
MidiAdsr_generates(MidiAdsr *self) {
    MYFLT val;
    int i;

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

    for (i=0; i<self->bufsize; i++) {
        if (self->fademode == 0 && in[i] > 0.0) {
            self->fademode = 1;
            self->initAmp = in[i];
            self->offsetAmp = self->data[i];
            self->sustainAmp = self->initAmp * self->sustain;
            self->currentTime = 0.0;
            self->invAttack = 1.0 / self->attack;
            self->invDecay = 1.0 / self->decay; 
            self->attackPlusDecay = self->attack + self->decay;
            self->initAmpMinusOffsetAmp = self->initAmp - self->offsetAmp;
            self->initAmpMinusSustainAmp = self->initAmp - self->sustainAmp;
        }
        else if (self->fademode == 1 && in[i] == 0.0) {
            self->fademode = 0;
            self->currentTime = 0.0;
            self->invRelease = 1.0 / self->release;
        }
        
        if (self->fademode == 1) {
            if (self->currentTime <= self->attack)
                val = self->currentTime * self->invAttack * self->initAmpMinusOffsetAmp + self->offsetAmp;
            else if (self->currentTime <= self->attackPlusDecay)
                val = (self->decay - (self->currentTime - self->attack)) * self->invDecay * self->initAmpMinusSustainAmp + self->sustainAmp;
            else
                val = self->sustainAmp;
            self->topValue = val;
        }    
        else {  
            if (self->currentTime <= self->release)
                val = self->topValue * (1. - self->currentTime * self->invRelease);
            else 
                val = 0.;
        }    
        self->data[i] = val;
        self->currentTime += self->sampleToSec;    
    }
}

static void MidiAdsr_postprocessing_ii(MidiAdsr *self) { POST_PROCESSING_II };
static void MidiAdsr_postprocessing_ai(MidiAdsr *self) { POST_PROCESSING_AI };
static void MidiAdsr_postprocessing_ia(MidiAdsr *self) { POST_PROCESSING_IA };
static void MidiAdsr_postprocessing_aa(MidiAdsr *self) { POST_PROCESSING_AA };
static void MidiAdsr_postprocessing_ireva(MidiAdsr *self) { POST_PROCESSING_IREVA };
static void MidiAdsr_postprocessing_areva(MidiAdsr *self) { POST_PROCESSING_AREVA };
static void MidiAdsr_postprocessing_revai(MidiAdsr *self) { POST_PROCESSING_REVAI };
static void MidiAdsr_postprocessing_revaa(MidiAdsr *self) { POST_PROCESSING_REVAA };
static void MidiAdsr_postprocessing_revareva(MidiAdsr *self) { POST_PROCESSING_REVAREVA };

static void
MidiAdsr_setProcMode(MidiAdsr *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    
    self->proc_func_ptr = MidiAdsr_generates;
    
	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = MidiAdsr_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = MidiAdsr_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = MidiAdsr_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = MidiAdsr_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = MidiAdsr_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = MidiAdsr_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = MidiAdsr_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = MidiAdsr_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = MidiAdsr_postprocessing_revareva;
            break;
    }   
}

static void
MidiAdsr_compute_next_data_frame(MidiAdsr *self)
{
    (*self->proc_func_ptr)(self); 
    (*self->muladd_func_ptr)(self);
}

static int
MidiAdsr_traverse(MidiAdsr *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->input);
    Py_VISIT(self->input_stream);
    return 0;
}

static int 
MidiAdsr_clear(MidiAdsr *self)
{
    pyo_CLEAR
    Py_CLEAR(self->input);
    Py_CLEAR(self->input_stream);
    return 0;
}

static void
MidiAdsr_dealloc(MidiAdsr* self)
{
    pyo_DEALLOC
    MidiAdsr_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
MidiAdsr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *inputtmp, *input_streamtmp, *multmp=NULL, *addtmp=NULL;
    MidiAdsr *self;
    self = (MidiAdsr *)type->tp_alloc(type, 0);
    
	self->modebuffer[0] = 0;
	self->modebuffer[1] = 0;
    self->topValue = 0.0;
    self->fademode = 0;
    self->changed = 0;
    self->attack = 0.01;
    self->decay = 0.05;
    self->sustain = 0.707;
    self->release = 0.1;
    self->currentTime = 0.0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, MidiAdsr_compute_next_data_frame);
    self->mode_func_ptr = MidiAdsr_setProcMode;

    self->sampleToSec = 1. / self->sr;

    static char *kwlist[] = {"input", "attack", "decay", "sustain", "release", "mul", "add", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE_O_FFFFOO, kwlist, &inputtmp, &self->attack, &self->decay, &self->sustain, &self->release, &multmp, &addtmp))
        Py_RETURN_NONE;
    
    INIT_INPUT_STREAM

    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->attack < 0.000001)
        self->attack = 0.000001;
    if (self->decay < 0.000001)
        self->decay = 0.000001;
    if (self->release < 0.000001)
        self->release = 0.000001;
    if (self->sustain < 0.0)
        self->sustain = 0.0;
    else if (self->sustain > 1.0)
        self->sustain = 1.0;

    (*self->mode_func_ptr)(self);

    return (PyObject *)self;
}

static PyObject * MidiAdsr_getServer(MidiAdsr* self) { GET_SERVER };
static PyObject * MidiAdsr_getStream(MidiAdsr* self) { GET_STREAM };
static PyObject * MidiAdsr_setMul(MidiAdsr *self, PyObject *arg) { SET_MUL };	
static PyObject * MidiAdsr_setAdd(MidiAdsr *self, PyObject *arg) { SET_ADD };	
static PyObject * MidiAdsr_setSub(MidiAdsr *self, PyObject *arg) { SET_SUB };	
static PyObject * MidiAdsr_setDiv(MidiAdsr *self, PyObject *arg) { SET_DIV };	

static PyObject * MidiAdsr_play(MidiAdsr *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * MidiAdsr_stop(MidiAdsr *self) { STOP }

static PyObject * MidiAdsr_multiply(MidiAdsr *self, PyObject *arg) { MULTIPLY };
static PyObject * MidiAdsr_inplace_multiply(MidiAdsr *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * MidiAdsr_add(MidiAdsr *self, PyObject *arg) { ADD };
static PyObject * MidiAdsr_inplace_add(MidiAdsr *self, PyObject *arg) { INPLACE_ADD };
static PyObject * MidiAdsr_sub(MidiAdsr *self, PyObject *arg) { SUB };
static PyObject * MidiAdsr_inplace_sub(MidiAdsr *self, PyObject *arg) { INPLACE_SUB };
static PyObject * MidiAdsr_div(MidiAdsr *self, PyObject *arg) { DIV };
static PyObject * MidiAdsr_inplace_div(MidiAdsr *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
MidiAdsr_setAttack(MidiAdsr *self, PyObject *arg)
{
    self->attack = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->attack < 0.000001)
        self->attack = 0.000001;
    self->invAttack = 1.0 / self->attack;
    self->attackPlusDecay = self->attack + self->decay;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiAdsr_setDecay(MidiAdsr *self, PyObject *arg)
{
    self->decay = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->decay < 0.000001)
        self->decay = 0.000001;
    self->invDecay = 1.0 / self->decay; 
    self->attackPlusDecay = self->attack + self->decay;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiAdsr_setSustain(MidiAdsr *self, PyObject *arg)
{
    self->sustain = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->sustain < 0.0)
        self->sustain = 0.0;
    else if (self->sustain > 1.0)
        self->sustain = 1.0;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiAdsr_setRelease(MidiAdsr *self, PyObject *arg)
{
    self->release = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->release < 0.000001)
        self->release = 0.000001;
    self->invRelease = 1.0 / self->release;
    Py_INCREF(Py_None);
    return Py_None;
}

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

static PyMethodDef MidiAdsr_methods[] = {
    {"getServer", (PyCFunction)MidiAdsr_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)MidiAdsr_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)MidiAdsr_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)MidiAdsr_stop, METH_NOARGS, "Starts fadeout and stops computing."},
    {"setMul", (PyCFunction)MidiAdsr_setMul, METH_O, "Sets MidiAdsr mul factor."},
    {"setAdd", (PyCFunction)MidiAdsr_setAdd, METH_O, "Sets MidiAdsr add factor."},
    {"setSub", (PyCFunction)MidiAdsr_setSub, METH_O, "Sets inverse add factor."},
    {"setAttack", (PyCFunction)MidiAdsr_setAttack, METH_O, "Sets attack time in seconds."},
    {"setDecay", (PyCFunction)MidiAdsr_setDecay, METH_O, "Sets decay time in seconds."},
    {"setSustain", (PyCFunction)MidiAdsr_setSustain, METH_O, "Sets sustain level in percent of note amplitude."},
    {"setRelease", (PyCFunction)MidiAdsr_setRelease, METH_O, "Sets release time in seconds."},
    {"setDiv", (PyCFunction)MidiAdsr_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods MidiAdsr_as_number = {
    (binaryfunc)MidiAdsr_add,                      /*nb_add*/
    (binaryfunc)MidiAdsr_sub,                 /*nb_subtract*/
    (binaryfunc)MidiAdsr_multiply,                 /*nb_multiply*/
    (binaryfunc)MidiAdsr_div,                   /*nb_divide*/
    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_coerce*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    0,                       /*nb_oct*/
    0,                       /*nb_hex*/
    (binaryfunc)MidiAdsr_inplace_add,              /*inplace_add*/
    (binaryfunc)MidiAdsr_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)MidiAdsr_inplace_multiply,         /*inplace_multiply*/
    (binaryfunc)MidiAdsr_inplace_div,           /*inplace_divide*/
    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*/
    0,              /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    0,      /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject MidiAdsrType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.MidiAdsr_base",         /*tp_name*/
    sizeof(MidiAdsr),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)MidiAdsr_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &MidiAdsr_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 | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "MidiAdsr objects. Generates MidiAdsr envelope signal.",           /* tp_doc */
    (traverseproc)MidiAdsr_traverse,   /* tp_traverse */
    (inquiry)MidiAdsr_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    MidiAdsr_methods,             /* tp_methods */
    MidiAdsr_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 */
    MidiAdsr_new,                 /* tp_new */
};

typedef struct {
    pyo_audio_HEAD
    PyObject *input;
    Stream *input_stream;
    int modebuffer[2];
    int fademode;
    int changed;
    MYFLT topValue;
    MYFLT offsetAmp;
    MYFLT initAmp;
    MYFLT sustainAmp;
    MYFLT delay;
    MYFLT attack;
    MYFLT decay;
    MYFLT sustain;
    MYFLT release;
    MYFLT invAttack;
    MYFLT initAmpMinusOffsetAmp;
    MYFLT invDecay;
    MYFLT delayPlusAttack;
    MYFLT delayPlusAttackPlusDecay;
    MYFLT initAmpMinusSustainAmp;
    MYFLT invRelease;
    double currentTime;
    MYFLT sampleToSec;
} MidiDelAdsr;

static void
MidiDelAdsr_generates(MidiDelAdsr *self) {
    MYFLT val;
    int i;
    
    MYFLT *in = Stream_getData((Stream *)self->input_stream);
    
    for (i=0; i<self->bufsize; i++) {
        if (self->fademode == 0 && in[i] > 0.0) {
            self->fademode = 1;
            self->initAmp = in[i];
            self->offsetAmp = self->data[i];
            self->sustainAmp = self->initAmp * self->sustain;
            self->currentTime = 0.0;
            self->invAttack = 1.0 / self->attack;
            self->invDecay = 1.0 / self->decay; 
            self->delayPlusAttack = self->delay + self->attack;
            self->delayPlusAttackPlusDecay = self->delay + self->attack + self->decay;
            self->initAmpMinusOffsetAmp = self->initAmp - self->offsetAmp;
            self->initAmpMinusSustainAmp = self->initAmp - self->sustainAmp;
        }
        else if (self->fademode == 1 && in[i] == 0.0) {
            self->fademode = 0;
            self->currentTime = 0.0;
            self->invRelease = 1.0 / self->release;
        }
        
        if (self->fademode == 1) {
            if (self->currentTime < self->delay)
                val = 0.0;
            else if (self->currentTime <= self->delayPlusAttack)
                val = (self->currentTime - self->delay) * self->invAttack * self->initAmpMinusOffsetAmp + self->offsetAmp;
            else if (self->currentTime <= self->delayPlusAttackPlusDecay)
                val = (self->decay - (self->currentTime - self->delay - self->attack)) * self->invDecay * self->initAmpMinusSustainAmp + self->sustainAmp;
            else
                val = self->sustainAmp;
            self->topValue = val;
        }    
        else {  
            if (self->currentTime <= self->release)
                val = self->topValue * (1. - self->currentTime * self->invRelease);
            else 
                val = 0.;
        }    
        self->data[i] = val;
        self->currentTime += self->sampleToSec;    
    }
}

static void MidiDelAdsr_postprocessing_ii(MidiDelAdsr *self) { POST_PROCESSING_II };
static void MidiDelAdsr_postprocessing_ai(MidiDelAdsr *self) { POST_PROCESSING_AI };
static void MidiDelAdsr_postprocessing_ia(MidiDelAdsr *self) { POST_PROCESSING_IA };
static void MidiDelAdsr_postprocessing_aa(MidiDelAdsr *self) { POST_PROCESSING_AA };
static void MidiDelAdsr_postprocessing_ireva(MidiDelAdsr *self) { POST_PROCESSING_IREVA };
static void MidiDelAdsr_postprocessing_areva(MidiDelAdsr *self) { POST_PROCESSING_AREVA };
static void MidiDelAdsr_postprocessing_revai(MidiDelAdsr *self) { POST_PROCESSING_REVAI };
static void MidiDelAdsr_postprocessing_revaa(MidiDelAdsr *self) { POST_PROCESSING_REVAA };
static void MidiDelAdsr_postprocessing_revareva(MidiDelAdsr *self) { POST_PROCESSING_REVAREVA };

static void
MidiDelAdsr_setProcMode(MidiDelAdsr *self)
{
    int muladdmode;
    muladdmode = self->modebuffer[0] + self->modebuffer[1] * 10;
    
    self->proc_func_ptr = MidiDelAdsr_generates;
    
	switch (muladdmode) {
        case 0:        
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_ii;
            break;
        case 1:    
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_ai;
            break;
        case 2:    
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_revai;
            break;
        case 10:        
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_ia;
            break;
        case 11:    
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_aa;
            break;
        case 12:    
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_revaa;
            break;
        case 20:        
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_ireva;
            break;
        case 21:    
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_areva;
            break;
        case 22:    
            self->muladd_func_ptr = MidiDelAdsr_postprocessing_revareva;
            break;
    }   
}

static void
MidiDelAdsr_compute_next_data_frame(MidiDelAdsr *self)
{
    (*self->proc_func_ptr)(self); 
    (*self->muladd_func_ptr)(self);
}

static int
MidiDelAdsr_traverse(MidiDelAdsr *self, visitproc visit, void *arg)
{
    pyo_VISIT
    Py_VISIT(self->input);
    Py_VISIT(self->input_stream);
    return 0;
}

static int 
MidiDelAdsr_clear(MidiDelAdsr *self)
{
    pyo_CLEAR
    Py_CLEAR(self->input);
    Py_CLEAR(self->input_stream);
    return 0;
}

static void
MidiDelAdsr_dealloc(MidiDelAdsr* self)
{
    pyo_DEALLOC
    MidiDelAdsr_clear(self);
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject *
MidiDelAdsr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    int i;
    PyObject *inputtmp, *input_streamtmp, *multmp=NULL, *addtmp=NULL;
    MidiDelAdsr *self;
    self = (MidiDelAdsr *)type->tp_alloc(type, 0);
    
	self->modebuffer[0] = 0;
	self->modebuffer[1] = 0;
    self->topValue = 0.0;
    self->fademode = 0;
    self->changed = 0;
    self->delay = 0.;
    self->attack = 0.01;
    self->decay = 0.05;
    self->sustain = 0.707;
    self->release = 0.1;
    self->currentTime = 0.0;
    
    INIT_OBJECT_COMMON
    Stream_setFunctionPtr(self->stream, MidiDelAdsr_compute_next_data_frame);
    self->mode_func_ptr = MidiDelAdsr_setProcMode;
    
    self->sampleToSec = 1. / self->sr;

    static char *kwlist[] = {"input", "delay", "attack", "decay", "sustain", "release", "mul", "add", NULL};
    
    if (! PyArg_ParseTupleAndKeywords(args, kwds, TYPE_O_FFFFFOO, kwlist, &inputtmp, &self->delay, &self->attack, &self->decay, &self->sustain, &self->release, &multmp, &addtmp))
        Py_RETURN_NONE;
    
    INIT_INPUT_STREAM
    
    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->attack < 0.000001)
        self->attack = 0.000001;
    if (self->decay < 0.000001)
        self->decay = 0.000001;
    if (self->release < 0.000001)
        self->release = 0.000001;
    if (self->sustain < 0.0)
        self->sustain = 0.0;
    else if (self->sustain > 1.0)
        self->sustain = 1.0;
    
    (*self->mode_func_ptr)(self);
    
    return (PyObject *)self;
}

static PyObject * MidiDelAdsr_getServer(MidiDelAdsr* self) { GET_SERVER };
static PyObject * MidiDelAdsr_getStream(MidiDelAdsr* self) { GET_STREAM };
static PyObject * MidiDelAdsr_setMul(MidiDelAdsr *self, PyObject *arg) { SET_MUL };	
static PyObject * MidiDelAdsr_setAdd(MidiDelAdsr *self, PyObject *arg) { SET_ADD };	
static PyObject * MidiDelAdsr_setSub(MidiDelAdsr *self, PyObject *arg) { SET_SUB };	
static PyObject * MidiDelAdsr_setDiv(MidiDelAdsr *self, PyObject *arg) { SET_DIV };	

static PyObject * MidiDelAdsr_play(MidiDelAdsr *self, PyObject *args, PyObject *kwds) { PLAY };
static PyObject * MidiDelAdsr_stop(MidiDelAdsr *self) { STOP }

static PyObject * MidiDelAdsr_multiply(MidiDelAdsr *self, PyObject *arg) { MULTIPLY };
static PyObject * MidiDelAdsr_inplace_multiply(MidiDelAdsr *self, PyObject *arg) { INPLACE_MULTIPLY };
static PyObject * MidiDelAdsr_add(MidiDelAdsr *self, PyObject *arg) { ADD };
static PyObject * MidiDelAdsr_inplace_add(MidiDelAdsr *self, PyObject *arg) { INPLACE_ADD };
static PyObject * MidiDelAdsr_sub(MidiDelAdsr *self, PyObject *arg) { SUB };
static PyObject * MidiDelAdsr_inplace_sub(MidiDelAdsr *self, PyObject *arg) { INPLACE_SUB };
static PyObject * MidiDelAdsr_div(MidiDelAdsr *self, PyObject *arg) { DIV };
static PyObject * MidiDelAdsr_inplace_div(MidiDelAdsr *self, PyObject *arg) { INPLACE_DIV };

static PyObject *
MidiDelAdsr_setDelay(MidiDelAdsr *self, PyObject *arg)
{
    self->delay = PyFloat_AsDouble(PyNumber_Float(arg));
    self->delayPlusAttack = self->delay + self->attack;
    self->delayPlusAttackPlusDecay = self->delay + self->attack + self->decay;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiDelAdsr_setAttack(MidiDelAdsr *self, PyObject *arg)
{
    self->attack = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->attack < 0.000001)
        self->attack = 0.000001;
    self->invAttack = 1.0 / self->attack;
    self->delayPlusAttack = self->delay + self->attack;
    self->delayPlusAttackPlusDecay = self->delay + self->attack + self->decay;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiDelAdsr_setDecay(MidiDelAdsr *self, PyObject *arg)
{
    self->decay = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->decay < 0.000001)
        self->decay = 0.000001;
    self->invDecay = 1.0 / self->decay; 
    self->delayPlusAttackPlusDecay = self->delay + self->attack + self->decay;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiDelAdsr_setSustain(MidiDelAdsr *self, PyObject *arg)
{
    self->sustain = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->sustain < 0.0)
        self->sustain = 0.0;
    else if (self->sustain > 1.0)
        self->sustain = 1.0;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject *
MidiDelAdsr_setRelease(MidiDelAdsr *self, PyObject *arg)
{
    self->release = PyFloat_AsDouble(PyNumber_Float(arg));
    if (self->release < 0.000001)
        self->release = 0.000001;
    self->invRelease = 1.0 / self->release;
    Py_INCREF(Py_None);
    return Py_None;
}

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

static PyMethodDef MidiDelAdsr_methods[] = {
    {"getServer", (PyCFunction)MidiDelAdsr_getServer, METH_NOARGS, "Returns server object."},
    {"_getStream", (PyCFunction)MidiDelAdsr_getStream, METH_NOARGS, "Returns stream object."},
    {"play", (PyCFunction)MidiDelAdsr_play, METH_VARARGS|METH_KEYWORDS, "Starts computing without sending sound to soundcard."},
    {"stop", (PyCFunction)MidiDelAdsr_stop, METH_NOARGS, "Starts fadeout and stops computing."},
    {"setMul", (PyCFunction)MidiDelAdsr_setMul, METH_O, "Sets MidiDelAdsr mul factor."},
    {"setAdd", (PyCFunction)MidiDelAdsr_setAdd, METH_O, "Sets MidiDelAdsr add factor."},
    {"setSub", (PyCFunction)MidiDelAdsr_setSub, METH_O, "Sets inverse add factor."},
    {"setDelay", (PyCFunction)MidiDelAdsr_setDelay, METH_O, "Sets delay time in seconds."},
    {"setAttack", (PyCFunction)MidiDelAdsr_setAttack, METH_O, "Sets attack time in seconds."},
    {"setDecay", (PyCFunction)MidiDelAdsr_setDecay, METH_O, "Sets decay time in seconds."},
    {"setSustain", (PyCFunction)MidiDelAdsr_setSustain, METH_O, "Sets sustain level in percent of note amplitude."},
    {"setRelease", (PyCFunction)MidiDelAdsr_setRelease, METH_O, "Sets release time in seconds."},
    {"setDiv", (PyCFunction)MidiDelAdsr_setDiv, METH_O, "Sets inverse mul factor."},
    {NULL}  /* Sentinel */
};

static PyNumberMethods MidiDelAdsr_as_number = {
    (binaryfunc)MidiDelAdsr_add,                      /*nb_add*/
    (binaryfunc)MidiDelAdsr_sub,                 /*nb_subtract*/
    (binaryfunc)MidiDelAdsr_multiply,                 /*nb_multiply*/
    (binaryfunc)MidiDelAdsr_div,                   /*nb_divide*/
    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_coerce*/
    0,                       /*nb_int*/
    0,                      /*nb_long*/
    0,                     /*nb_float*/
    0,                       /*nb_oct*/
    0,                       /*nb_hex*/
    (binaryfunc)MidiDelAdsr_inplace_add,              /*inplace_add*/
    (binaryfunc)MidiDelAdsr_inplace_sub,         /*inplace_subtract*/
    (binaryfunc)MidiDelAdsr_inplace_multiply,         /*inplace_multiply*/
    (binaryfunc)MidiDelAdsr_inplace_div,           /*inplace_divide*/
    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*/
    0,              /*nb_true_divide*/
    0,     /*nb_inplace_floor_divide*/
    0,      /*nb_inplace_true_divide*/
    0,                     /* nb_index */
};

PyTypeObject MidiDelAdsrType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "_pyo.MidiDelAdsr_base",         /*tp_name*/
    sizeof(MidiDelAdsr),         /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)MidiDelAdsr_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    &MidiDelAdsr_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 | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
    "MidiDelAdsr objects. Generates MidiDelAdsr envelope signal.",           /* tp_doc */
    (traverseproc)MidiDelAdsr_traverse,   /* tp_traverse */
    (inquiry)MidiDelAdsr_clear,           /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    MidiDelAdsr_methods,             /* tp_methods */
    MidiDelAdsr_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 */
    MidiDelAdsr_new,                 /* tp_new */
};

