[SCM] python-pyo/master: Imported Upstream version 0.7.6+git20150826.1f0dc1aa93

tiago at users.alioth.debian.org tiago at users.alioth.debian.org
Wed Aug 26 11:03:07 UTC 2015


The following commit has been merged in the master branch:
commit 8d9a1420ee483c0fcd39808871d8752556974af9
Author: Tiago Bortoletto Vaz <tiago at debian.org>
Date:   Wed Aug 26 00:51:27 2015 -0400

    Imported Upstream version 0.7.6+git20150826.1f0dc1aa93

diff --git a/ChangeLog b/ChangeLog
index 73619a4..c933e3b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2015-08-25 belangeo <belangeo at gmail.com>
+
+    * Added new objects: MidiListener and OscListener (self-contained MIDI and OSC servers).
+
 -------------------------------------------------------------------------------------
 
 2015-07-27 belangeo <belangeo at gmail.com>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..49b1075
--- /dev/null
+++ b/README.md
@@ -0,0 +1,95 @@
+# Pyo - Python DSP module #
+
+pyo is a Python module written in C to help digital signal processing script 
+creation.
+
+pyo is a Python module containing classes for a wide variety of audio signal 
+processing types. With pyo, user will be able to include signal processing 
+chains directly in Python scripts or projects, and to manipulate them in real 
+time through the interpreter. Tools in pyo module offer primitives, like 
+mathematical operations on audio signal, basic signal processing (filters, 
+delays, synthesis generators, etc.), but also complex algorithms to create 
+sound granulation and others creative audio manipulations. pyo supports OSC 
+protocol (Open Sound Control), to ease communications between softwares, and 
+MIDI protocol, for generating sound events and controlling process parameters. 
+pyo allows creation of sophisticated signal processing chains with all the 
+benefits of a mature, and widely used, general programming language.
+
+Systems : OS X (10.5 -> 10.10), linux, Windows (XP, Vista, 7, 8)
+
+Python versions : 2.6, 2.7
+
+[PYO OFFICAL WEB SITE](http://ajaxsoundstudio.com/pyo/)
+
+**Download latest binaries, source release and documentation** [HERE](http://ajaxsoundstudio.com/pyo/)!
+
+
+How to get pyo running from sources on OS X and linux:
+[INSTALL Instructions](http://ajaxsoundstudio.com/pyodoc/compiling.html)
+
+pyo was awarded **second prize** in the [Lomus 2012](http://concours.afim-asso.org/2012/) Free Software Competition.
+
+## Radio Pyo ##
+
+If you want to listen to scripts rendered in real-time, just connect to [Radio Pyo](http://radiopyo.acaia.ca/) !
+
+You want to have your script played on the radio ? Follow the instructions on this
+[post](http://acaia.ca/~tiago/posts/introducing-radio-pyo-live-music-in-python/) !
+
+## Softwares using pyo as audio engine ##
+
+[Zyne](https://github.com/belangeo/zyne) : A modular soft synthesizer.
+
+[Soundgrain](http://ajaxsoundstudio.com/software/soundgrain/) : A graphical interface where users can draw and edit trajectories to control granular sound synthesis.
+
+[Cecilia 5](http://ajaxsoundstudio.com/software/cecilia/) : An audio signal processing environment.
+
+[PsychoPy](http://www.psychopy.org/) : An open-source application to allow the presentation of stimuli and collection of data for a wide range of neuroscience, psychology and psychophysics experiments.
+
+## Examples ##
+
+pyo is fully integrated to Python and very simple to use.
+
+Play a sound:
+
+```
+>>> from pyo import *
+>>> s = Server().boot()
+>>> s.start()
+>>> sf = SfPlayer("path/to/your/sound.aif", speed=1, loop=True).out()
+```
+
+Granulate an audio buffer:
+
+```
+>>> s = Server().boot()
+>>> s.start()
+>>> snd = SndTable("path/to/your/sound.aif")
+>>> env = HannTable()
+>>> pos = Phasor(freq=snd.getRate()*.25, mul=snd.getSize())
+>>> dur = Noise(mul=.001, add=.1)
+>>> g = Granulator(snd, env, [1, 1.001], pos, dur, 24, mul=.1).out()
+```
+
+Generate melodies:
+
+```
+>>> s = Server().boot()
+>>> s.start()
+>>> wav = SquareTable()
+>>> env = CosTable([(0,0), (100,1), (500,.3), (8191,0)])
+>>> met = Metro(.125, 12).play()
+>>> amp = TrigEnv(met, table=env, mul=.1)
+>>> pit = TrigXnoiseMidi(met, dist='loopseg', x1=20, scale=1, mrange=(48,84))
+>>> out = Osc(table=wav, freq=pit, mul=amp).out()
+```
+
+## Donation ##
+
+This project is developed by Olivier Bélanger on his free time to provide a 
+fully integrated Python dsp module for sound exploration and music composition. 
+If you feel this project is useful to you and want to support it and it's 
+future development please consider donating money. I only ask for a small 
+donation, but of course I appreciate any amount.
+
+[![](https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9CA99DH6ES3HA)
\ No newline at end of file
diff --git a/doc-sphinx/source/api/classes/index.rst b/doc-sphinx/source/api/classes/index.rst
index cf8a2f2..2f5052d 100644
--- a/doc-sphinx/source/api/classes/index.rst
+++ b/doc-sphinx/source/api/classes/index.rst
@@ -5,6 +5,7 @@ Classes by category
    :maxdepth: 2
 
    server
+   listener
    _core
    analysis
    arithmetic
diff --git a/doc-sphinx/source/api/classes/index.rst b/doc-sphinx/source/api/classes/listener.rst
similarity index 96%
copy from doc-sphinx/source/api/classes/index.rst
copy to doc-sphinx/source/api/classes/listener.rst
index cf8a2f2..2f5052d 100644
--- a/doc-sphinx/source/api/classes/index.rst
+++ b/doc-sphinx/source/api/classes/listener.rst
@@ -5,6 +5,7 @@ Classes by category
    :maxdepth: 2
 
    server
+   listener
    _core
    analysis
    arithmetic
diff --git a/include/pyomodule.h b/include/pyomodule.h
index 32a3f3c..bc385fa 100644
--- a/include/pyomodule.h
+++ b/include/pyomodule.h
@@ -201,6 +201,8 @@
 #include "externalmodule.h"
 #endif
 
+extern PyTypeObject MidiListenerType;
+extern PyTypeObject OscListenerType;
 extern PyTypeObject SineType;
 extern PyTypeObject SineLoopType;
 extern PyTypeObject FmType;
diff --git a/include/servermodule.h b/include/servermodule.h
index 91e9d22..761529c 100644
--- a/include/servermodule.h
+++ b/include/servermodule.h
@@ -91,6 +91,7 @@ typedef struct {
     int midi_output;
     int withPortMidi;
     int withPortMidiOut;
+    int midiActive;
     int server_started;
     int server_stopped; /* for fadeout */
     int server_booted;
diff --git a/pyo.py b/pyo.py
index 8c2eb72..187247a 100644
--- a/pyo.py
+++ b/pyo.py
@@ -43,6 +43,7 @@ from pyolib.pattern import *
 import pyolib.randoms as randoms
 from pyolib.randoms import *
 from pyolib.server import *
+from pyolib.listener import *
 import pyolib.players as players
 from pyolib.players import *
 import pyolib.tableprocess as tableprocess
@@ -117,6 +118,8 @@ OBJECTS_TREE = {'functions': sorted(['pa_count_devices', 'pa_get_default_input',
                                   'fourier': sorted(['FFT', 'IFFT', 'CarToPol', 'PolToCar', 'FrameDelta', 'FrameAccum', 'Vectral', 'CvlVerb'])}},
         'Map': {'SLMap': sorted(['SLMapFreq', 'SLMapMul', 'SLMapPhase', 'SLMapQ', 'SLMapDur', 'SLMapPan'])},
         'Server': [],
+        'MidiListener': [],
+        'OscListener': [],
         'Stream': [],
         'TableStream': []}
 
diff --git a/pyolib/listener.py b/pyolib/listener.py
new file mode 100644
index 0000000..7cb9521
--- /dev/null
+++ b/pyolib/listener.py
@@ -0,0 +1,106 @@
+from _core import *
+import time
+import threading
+
+class MidiListener(threading.Thread):
+    """
+    Self-contained midi listener thread.
+
+    This object allows to setup a Midi server that is independent
+    of the audio server (mainly to be able to receive Midi data even
+    when the audio server is stopped). Although it runs in a separated
+    thread, the same device can't be used by this object and the audio
+    server at the same time. It is adviced to call the deactivateMidi() 
+    method on the audio server to avoid conflicts.
+
+    :Parent: threading.Thread
+
+    :Args:
+
+        function : Python function (can't be a list)
+            Function that will be called when a new midi event is available. 
+            This function is called with the incoming midi data as 
+            arguments. The signature of the function must be:
+                
+            def myfunc(status, data1, data2)
+
+        mididev : int, optional
+            Sets the midi input device (see `pm_list_devices()` for the 
+            available devices). The default, -1, means the system default
+            device. A number greater than the highest portmidi device index 
+            will opened all available input devices. 
+
+    >>> s = Server().boot()
+    >>> s.deactivateMidi()
+    >>> def midicall(status, data1, data2):
+    ...     print status, data1, data2
+    >>> listen = MidiListener(midicall, 5)
+    >>> listen.start()
+
+    """
+    def __init__(self, function, mididev=-1):
+        threading.Thread.__init__(self)
+        self.daemon = True
+        self._function = WeakMethod(function)
+        self._mididev = mididev
+        self._listener = MidiListener_base(self._function, self._mididev)
+        
+    def run(self):
+        """
+        Starts the process. The thread runs as daemon, so no need to stop it.
+        
+        """
+        self._listener.play()
+        while True:
+            time.sleep(0.001)
+
+OscListenerLock = threading.Lock()
+
+class OscListener(threading.Thread):
+    """
+    Self-contained OSC listener thread.
+
+    This object allows to setup an OSC server that is independent
+    of the audio server (mainly to be able to receive OSC data even
+    when the audio server is stopped). 
+
+    :Parent: threadind.Thread
+
+    :Args:
+
+        function : Python function (can't be a list)
+            Function that will be called when a new OSC event is available. 
+            This function is called with the incoming address and values as 
+            arguments. The signature of the function must be:
+                
+            def myfunc(address, *args)
+
+        port : int, optional
+            The OSC port on which the values are received. Defaults to 9000. 
+
+    >>> s = Server().boot()
+    >>> def call(address, *args):
+    ...     print address, args
+    >>> listen = OscListener(call, 9901)
+    >>> listen.start()
+
+    """
+    def __init__(self, function, port=9000):
+        threading.Thread.__init__(self)
+        self.daemon = True
+        self._function = WeakMethod(function)
+        self._port = port
+        self._listener = OscListener_base(self._oscrecv, self._port)
+
+    def _oscrecv(self, address, *args):
+        with OscListenerLock:
+            self._function(address, *args)
+
+    def run(self):
+        """
+        Starts the process. The thread runs as daemon, so no need to stop it.
+        
+        """
+        while True:
+            self._listener.get()
+            time.sleep(0.001)
diff --git a/pyolib/opensndctrl.py b/pyolib/opensndctrl.py
index 163cd94..6df3d97 100644
--- a/pyolib/opensndctrl.py
+++ b/pyolib/opensndctrl.py
@@ -559,6 +559,7 @@ class OscDataReceive(PyoObject):
         path, lmax = convertArgsToLists(path)
         for p in path:
             if p not in self._address:
+                self._address.append(p)
                 self._base_objs[0].addAddress(p)
 
     def delAddress(self, path):
diff --git a/pyolib/server.py b/pyolib/server.py
index 148da73..8f211b1 100644
--- a/pyolib/server.py
+++ b/pyolib/server.py
@@ -55,7 +55,7 @@ class Server(object):
         duplex : int {0, 1}, optional
             Input - output mode. 0 is output only and 1 is both ways.
             Defaults to 1.
-        audio : string {'portaudio', 'pa', 'jack', 'coreaudio', 'offline', 'offline_nb}, optional
+        audio : string {'portaudio', 'pa', 'jack', 'coreaudio', 'offline', 'offline_nb', 'embedded'}, optional
             Audio backend to use. 'pa' is equivalent to 'portaudio'. Default is 'portaudio'.
 
             'offline' save the audio output in a soundfile as fast as possible in blocking mode,
@@ -842,6 +842,13 @@ class Server(object):
         """
         return self._server.getIsBooted()
 
+    def deactivateMidi(self):
+        """
+        Deactivate Midi callback. Must be called before the start() method.
+
+        """
+        self._server.deactivateMidi()
+
     def getMidiActive(self):
         """
         Returns 1 if Midi callback is active, otherwise returns 0.
diff --git a/setup.py b/setup.py
index 2f2c75f..873ce31 100644
--- a/setup.py
+++ b/setup.py
@@ -66,7 +66,7 @@ if sys.platform == "darwin":
     macros.append(('_OSX_', None))
 
 path = 'src/engine/'
-files = ['pyomodule.c', 'servermodule.c', 'pvstreammodule.c', 'streammodule.c', 'dummymodule.c', 
+files = ['pyomodule.c', 'listenermodule.c', 'servermodule.c', 'pvstreammodule.c', 'streammodule.c', 'dummymodule.c', 
         'mixmodule.c', 'inputfadermodule.c', 'interpolation.c', 'fft.c', "wind.c"]
 source_files = [path + f for f in files]
 
diff --git a/src/engine/listenermodule.c b/src/engine/listenermodule.c
new file mode 100644
index 0000000..0a45024
--- /dev/null
+++ b/src/engine/listenermodule.c
@@ -0,0 +1,478 @@
+/**************************************************************************
+ * Copyright 2009-2015 Olivier Belanger                                   *
+ *                                                                        *
+ * This file is part of pyo, a python module to help digital signal       *
+ * processing script creation.                                            *
+ *                                                                        *
+ * pyo is free software: you can redistribute it and/or modify            *
+ * it under the terms of the GNU Lesser General Public License as         *
+ * published by the Free Software Foundation, either version 3 of the     *
+ * License, or (at your option) any later version.                        *
+ *                                                                        *
+ * pyo is distributed in the hope that it will be useful,                 *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
+ * GNU Lesser General Public License for more details.                    *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with pyo.  If not, see <http://www.gnu.org/licenses/>.   *
+ *************************************************************************/
+
+#include <Python.h>
+#include "structmember.h"
+#include <math.h>
+#include "pyomodule.h"
+#include "portmidi.h"
+#include "porttime.h"
+#include "lo/lo.h"
+
+static void error(int num, const char *msg, const char *path)
+{
+    printf("liblo server error %d in path %s: %s\n", num, path, msg);
+}
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *osccallable;
+    lo_server osc_server;
+    int oscport;
+} OscListener;
+
+static PyObject *
+OscListener_get(OscListener *self)
+{
+    while (lo_server_recv_noblock(self->osc_server, 0) != 0) {};
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+int process_osc(const char *path, const char *types, lo_arg **argv, int argc,
+                void *data, void *user_data)
+{
+    OscListener *server = (OscListener *)user_data;
+    PyObject *tup;
+    lo_blob *blob = NULL;
+    char *blobdata = NULL;
+    uint32_t blobsize = 0;
+    PyObject *charlist = NULL; 
+    tup = PyTuple_New(argc+1);
+    int i, j = 0;
+
+    PyGILState_STATE s = PyGILState_Ensure();
+    PyTuple_SET_ITEM(tup, 0, PyString_FromString(path));
+    for (i=0; i<argc; i++) {
+        switch (types[i]) {
+            case LO_INT32:
+                PyTuple_SET_ITEM(tup, i+1, PyInt_FromLong(argv[i]->i));
+                break;
+            case LO_INT64:
+                PyTuple_SET_ITEM(tup, i+1, PyLong_FromLong(argv[i]->h));
+                break;
+            case LO_FLOAT:
+                PyTuple_SET_ITEM(tup, i+1, PyFloat_FromDouble(argv[i]->f));
+                break;
+            case LO_DOUBLE:
+                PyTuple_SET_ITEM(tup, i+1, PyFloat_FromDouble(argv[i]->d));
+                break;
+            case LO_STRING:
+                PyTuple_SET_ITEM(tup, i+1, PyString_FromString(&argv[i]->s));
+                break;
+            case LO_CHAR:
+                PyTuple_SET_ITEM(tup, i+1, PyString_FromFormat("%c", argv[i]->c));
+                break;
+            case LO_BLOB:
+                blob = (lo_blob)argv[i];
+                blobsize = lo_blob_datasize(blob);
+                blobdata = lo_blob_dataptr(blob);
+                charlist = PyList_New(blobsize);
+                for (j=0; j<blobsize; j++) {
+                    PyList_SET_ITEM(charlist, j, PyString_FromFormat("%c", blobdata[j]));
+                }
+                PyTuple_SET_ITEM(tup, i+1, charlist);
+                break;
+            case LO_MIDI:
+                charlist = PyList_New(4);
+                for (j=0; j<4; j++) {
+                    PyList_SET_ITEM(charlist, j, PyInt_FromLong(argv[i]->m[j]));
+                }
+                PyTuple_SET_ITEM(tup, i+1, charlist);                    
+                break;
+            case LO_NIL:
+                Py_INCREF(Py_None);
+                PyTuple_SET_ITEM(tup, i+1, Py_None);
+                break;
+            case LO_TRUE:
+                Py_INCREF(Py_True);
+                PyTuple_SET_ITEM(tup, i+1, Py_True);
+                break;
+            case LO_FALSE:
+                Py_INCREF(Py_False);
+                PyTuple_SET_ITEM(tup, i+1, Py_False);
+                break;
+            default:
+                break;
+        }
+    }
+    PyObject_Call((PyObject *)server->osccallable, tup, NULL);
+    PyGILState_Release(s);
+    Py_XDECREF(tup);
+
+    return 0;
+}
+
+static int
+OscListener_traverse(OscListener *self, visitproc visit, void *arg)
+{
+    Py_VISIT(self->osccallable);
+    return 0;
+}
+
+static int
+OscListener_clear(OscListener *self)
+{
+    Py_CLEAR(self->osccallable);
+    return 0;
+}
+
+static void
+OscListener_dealloc(OscListener* self)
+{
+    lo_server_free(self->osc_server);
+    OscListener_clear(self);
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *
+OscListener_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    char buf[20];
+    PyObject *osccalltmp=NULL;
+    OscListener *self;
+
+    self = (OscListener *)type->tp_alloc(type, 0);
+
+    static char *kwlist[] = {"osccallable", "port", NULL};
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, &osccalltmp, &self->oscport))
+        Py_RETURN_NONE;
+
+    if (osccalltmp) {
+        PyObject_CallMethod((PyObject *)self, "setOscFunction", "O", osccalltmp);
+    }
+
+    sprintf(buf, "%i", self->oscport);
+    self->osc_server = lo_server_new(buf, error);
+    lo_server_add_method(self->osc_server, NULL, NULL, process_osc, (void *)self);
+
+    return (PyObject *)self;
+}
+
+static PyObject *
+OscListener_setOscFunction(OscListener *self, PyObject *arg)
+{
+	PyObject *tmp;
+
+    if (arg == Py_None) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+	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->osccallable);
+    Py_INCREF(tmp);
+    self->osccallable = tmp;
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyMemberDef OscListener_members[] = {
+    {NULL}  /* Sentinel */
+};
+
+static PyMethodDef OscListener_methods[] = {
+    {"get", (PyCFunction)OscListener_get, METH_NOARGS, "Check for new osc messages."},
+    {"setOscFunction", (PyCFunction)OscListener_setOscFunction, METH_O, "Sets the function to be called."},
+    {NULL}  /* Sentinel */
+};
+
+PyTypeObject OscListenerType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "_pyo.OscListener_base",         /*tp_name*/
+    sizeof(OscListener),         /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)OscListener_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*/
+    "OscListener objects. Calls a function with OSC data as arguments.",           /* tp_doc */
+    (traverseproc)OscListener_traverse,   /* tp_traverse */
+    (inquiry)OscListener_clear,           /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    OscListener_methods,             /* tp_methods */
+    OscListener_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 */
+    OscListener_new,                 /* tp_new */
+};
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *midicallable;
+    PmStream *midiin[64];
+    int mididev;
+    int midicount;
+    int active;
+} MidiListener;
+
+void process_midi(PtTimestamp timestamp, void *userData)
+{
+    PmError result;
+    PmEvent buffer; /* just one message at a time */
+    int i, status, data1, data2;
+    PyObject *tup = NULL;
+    MidiListener *server = (MidiListener *)userData;
+
+    if (server->active == 0) return;
+
+    PyGILState_STATE s = PyGILState_Ensure();
+    do {
+        for (i=0; i<server->midicount; i++) {
+            result = Pm_Poll(server->midiin[i]);
+            if (result) {
+                if (Pm_Read(server->midiin[i], &buffer, 1) == pmBufferOverflow) 
+                    continue;
+                status = Pm_MessageStatus(buffer.message);
+                data1 = Pm_MessageData1(buffer.message);
+                data2 = Pm_MessageData2(buffer.message);
+                tup = PyTuple_New(3);
+                PyTuple_SetItem(tup, 0, PyInt_FromLong(status));
+                PyTuple_SetItem(tup, 1, PyInt_FromLong(data1));
+                PyTuple_SetItem(tup, 2, PyInt_FromLong(data2));
+                PyObject_Call((PyObject *)server->midicallable, tup, NULL);
+            }
+        }
+    } while (result);
+
+    PyGILState_Release(s);
+    Py_XDECREF(tup);
+}
+
+static int
+MidiListener_traverse(MidiListener *self, visitproc visit, void *arg)
+{
+    Py_VISIT(self->midicallable);
+    return 0;
+}
+
+static int
+MidiListener_clear(MidiListener *self)
+{
+    Py_CLEAR(self->midicallable);
+    return 0;
+}
+
+static void
+MidiListener_dealloc(MidiListener* self)
+{
+    if (self->active == 1)
+        PyObject_CallMethod((PyObject *)self, "stop", NULL);
+    MidiListener_clear(self);
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *
+MidiListener_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *midicalltmp=NULL;
+    MidiListener *self;
+
+    self = (MidiListener *)type->tp_alloc(type, 0);
+
+    self->active = self->midicount = 0;
+    self->mididev = -1;
+
+    static char *kwlist[] = {"midicallable", "mididevice", NULL};
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, &midicalltmp, &self->mididev))
+        Py_RETURN_NONE;
+
+    if (midicalltmp) {
+        PyObject_CallMethod((PyObject *)self, "setMidiFunction", "O", midicalltmp);
+    }
+
+    return (PyObject *)self;
+}
+
+static PyObject * MidiListener_play(MidiListener *self) {
+    int i, num_devices;
+    PmError pmerr;
+
+    /* always start the timer before you start midi */
+    Pt_Start(1, &process_midi, (void *)self);
+    
+    pmerr = Pm_Initialize();
+    if (pmerr) {
+        printf("Portmidi warning: could not initialize Portmidi: %s\n", Pm_GetErrorText(pmerr));
+    }
+
+    num_devices = Pm_CountDevices();
+    if (num_devices > 0) {
+        if (self->mididev < num_devices) {
+            if (self->mididev == -1)
+                self->mididev = Pm_GetDefaultInputDeviceID();
+            const PmDeviceInfo *info = Pm_GetDeviceInfo(self->mididev);
+            if (info != NULL) {
+                if (info->input) {
+                    pmerr = Pm_OpenInput(&self->midiin[0], self->mididev, NULL, 100, NULL, NULL);
+                    if (pmerr) {
+                        printf("Portmidi warning: could not open midi input %d (%s): %s\n",
+                             self->mididev, info->name, Pm_GetErrorText(pmerr));
+                    }
+                    else {
+                        self->midicount = 1;
+                    }
+                }
+            }
+        }
+        else if (self->mididev >= num_devices) {
+            self->midicount = 0;
+            for (i=0; i<num_devices; i++) {
+                const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
+                if (info != NULL) {
+                    if (info->input) {
+                        pmerr = Pm_OpenInput(&self->midiin[self->midicount], i, NULL, 100, NULL, NULL);
+                        if (pmerr) {
+                            printf("Portmidi warning: could not open midi input %d (%s): %s\n",
+                                    i, info->name, Pm_GetErrorText(pmerr));
+                        }
+                        else {
+                            self->midicount++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    for (i=0; i<self->midicount; i++) {
+        Pm_SetFilter(self->midiin[i], PM_FILT_ACTIVE | PM_FILT_CLOCK);
+    }
+    self->active = 1;
+
+	Py_INCREF(Py_None);
+	return Py_None;
+};
+
+static PyObject * MidiListener_stop(MidiListener *self) { 
+    int i;
+    Pt_Stop();
+    for (i=0; i<self->midicount; i++) {
+        Pm_Close(self->midiin[i]);
+    }
+    Pm_Terminate();    
+    self->active = 0;
+	Py_INCREF(Py_None);
+	return Py_None;
+};
+
+static PyObject *
+MidiListener_setMidiFunction(MidiListener *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->midicallable);
+    Py_INCREF(tmp);
+    self->midicallable = tmp;
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyMemberDef MidiListener_members[] = {
+    {NULL}  /* Sentinel */
+};
+
+static PyMethodDef MidiListener_methods[] = {
+    {"play", (PyCFunction)MidiListener_play, METH_NOARGS, "Starts computing without sending sound to soundcard."},
+    {"stop", (PyCFunction)MidiListener_stop, METH_NOARGS, "Stops computing."},
+    {"setMidiFunction", (PyCFunction)MidiListener_setMidiFunction, METH_O, "Sets the function to be called."},
+    {NULL}  /* Sentinel */
+};
+
+PyTypeObject MidiListenerType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "_pyo.MidiListener_base",         /*tp_name*/
+    sizeof(MidiListener),         /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)MidiListener_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*/
+    "MidiListener objects. Calls a function with midi data as arguments.",           /* tp_doc */
+    (traverseproc)MidiListener_traverse,   /* tp_traverse */
+    (inquiry)MidiListener_clear,           /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    MidiListener_methods,             /* tp_methods */
+    MidiListener_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 */
+    MidiListener_new,                 /* tp_new */
+};
diff --git a/src/engine/pyomodule.c b/src/engine/pyomodule.c
index 88b37e4..3278ba5 100644
--- a/src/engine/pyomodule.c
+++ b/src/engine/pyomodule.c
@@ -2261,6 +2261,8 @@ init_pyo64(void)
 #endif
 
     module_add_object(m, "Server_base", &ServerType);
+    module_add_object(m, "MidiListener_base", &MidiListenerType);
+    module_add_object(m, "OscListener_base", &OscListenerType);
     module_add_object(m, "Stream", &StreamType);
     module_add_object(m, "TriggerStream", &TriggerStreamType);
     module_add_object(m, "PVStream", &PVStreamType);
diff --git a/src/engine/servermodule.c b/src/engine/servermodule.c
index 26fdb7d..1cbdb51 100644
--- a/src/engine/servermodule.c
+++ b/src/engine/servermodule.c
@@ -1581,6 +1581,8 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
         return Py_False;
     }
 
+    /* TODO: Need some testing to validate that there is no conflict when more than one server are running at the same time. */
+    /*
     if (strcmp(audioType, "embedded") != 0)
     {
         if (PyServer_get_server() != NULL) {
@@ -1588,6 +1590,7 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
             Py_RETURN_NONE;
         }
     }
+    */
 
     /* find the first free serverID */
     for(serverID = 0; serverID < MAX_NBR_SERVER; serverID++){
@@ -1623,6 +1626,7 @@ Server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     self->midiout_count = 0;
     self->midi_input = -1;
     self->midi_output = -1;
+    self->midiActive = 1;
     self->amp = self->resetAmp = 1.;
     self->currentAmp = self->lastAmp = 0.;
     self->withGUI = 0;
@@ -1781,6 +1785,14 @@ Server_setMidiOutputDevice(Server *self, PyObject *arg)
 }
 
 static PyObject *
+Server_deactivateMidi(Server *self)
+{
+    self->midiActive = 0;
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject *
 Server_setSamplingRate(Server *self, PyObject *arg)
 {
     if (self->server_booted) {
@@ -2081,9 +2093,14 @@ int
 Server_pm_init(Server *self)
 {
     int i = 0;
-   /* Initializing MIDI */
     PmError pmerr;
 
+    if (self->midiActive == 0) {
+        self->withPortMidi = 0;
+        self->withPortMidiOut = 0;
+        return 0;
+    }
+
     pmerr = Pm_Initialize();
     if (pmerr) {
         Server_warning(self, "Portmidi warning: could not initialize Portmidi: %s\n", Pm_GetErrorText(pmerr));
@@ -2901,7 +2918,7 @@ static PyObject *
 Server_setServer(Server *self)
 {
     serverID = self->thisServerID;
-    /* Should return a more conventional signal, like True or False */
+    /* TODO: Should return a more conventional signal, like True or False */
     return PyString_FromString("Server set");
 }
 
@@ -2969,6 +2986,7 @@ static PyMethodDef Server_methods[] = {
     {"setInOutDevice", (PyCFunction)Server_setInOutDevice, METH_O, "Sets both audio input and output device."},
     {"setMidiInputDevice", (PyCFunction)Server_setMidiInputDevice, METH_O, "Sets MIDI input device."},
     {"setMidiOutputDevice", (PyCFunction)Server_setMidiOutputDevice, METH_O, "Sets MIDI output device."},
+    {"deactivateMidi", (PyCFunction)Server_deactivateMidi, METH_NOARGS, "Deactivates midi callback."},
     {"setSamplingRate", (PyCFunction)Server_setSamplingRate, METH_O, "Sets the server's sampling rate."},
     {"setBufferSize", (PyCFunction)Server_setBufferSize, METH_O, "Sets the server's buffer size."},
     {"setNchnls", (PyCFunction)Server_setNchnls, METH_O, "Sets the server's number of output/input channels."},
diff --git a/src/objects/oscmodule.c b/src/objects/oscmodule.c
index e40c8cc..0cf9f58 100644
--- a/src/objects/oscmodule.c
+++ b/src/objects/oscmodule.c
@@ -27,7 +27,7 @@
 #include "dummymodule.h"
 #include "lo/lo.h"
 
-void error(int num, const char *msg, const char *path)
+static void error(int num, const char *msg, const char *path)
 {
     printf("liblo server error %d in path %s: %s\n", num, path, msg);
 }
diff --git a/utils/E-Pyo.py b/utils/E-Pyo.py
index 87d556a..011ffad 100755
--- a/utils/E-Pyo.py
+++ b/utils/E-Pyo.py
@@ -2960,6 +2960,9 @@ class MainFrame(wx.Frame):
             for file in lines:
                 self.submenu2.Append(subId2, toSysEncoding(file))
                 subId2 += 1
+        if subId2 > 2000:
+            for i in range(2000, subId2):
+                self.Bind(wx.EVT_MENU, self.openRecent, id=i)
 
     def openRecent(self, event):
         menu = self.GetMenuBar()
@@ -4570,7 +4573,7 @@ class Editor(stc.StyledTextCtrl):
         #     for i in range(self.GetLineCount()):
         #         pos = self.GetLineEndPosition(i)
         #         if self.GetCharAt(pos-1) != 172:
-        #             self.InsertTextUTF8(pos, "¬")
+        #             self.InsertTextUTF8(pos, "¬")
         self.moveMarkers()
         self.checkScrollbar()
         self.OnModified()

-- 
python-pyo packaging



More information about the pkg-multimedia-commits mailing list