Fabian Peter Hammerle 9 년 전
커밋
4776e549e9
6개의 변경된 파일316개의 추가작업 그리고 0개의 파일을 삭제
  1. 21 0
      LICENSE.txt
  2. 2 0
      README.md
  3. 219 0
      jack.c
  4. 2 0
      setup.cfg
  5. 22 0
      setup.py
  6. 50 0
      tests/registration-callback.py

+ 21 - 0
LICENSE.txt

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Fabian Peter Hammerle
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+# jacker
+JACK API for Python

+ 219 - 0
jack.c

@@ -0,0 +1,219 @@
+#include <python2.7/Python.h>
+
+#include <jack/jack.h>
+
+typedef struct {
+    PyObject_HEAD
+    jack_client_t* client;
+    PyObject* registration_callback;
+} Client;
+
+typedef struct {
+    PyObject_HEAD
+    jack_port_t* port;
+} Port;
+
+static PyObject* error;
+static PyObject* failure;
+
+static PyTypeObject port_type = {
+    PyObject_HEAD_INIT(NULL)
+    };
+
+static void jack_registration_callback(jack_port_id_t port_id, int registered, void* arg)
+{
+    // register: non-zero if the port is being registered, zero if the port is being unregistered
+    Client* client = (Client*)arg;
+
+    if(client->registration_callback) {
+        // Ensure that the current thread is ready to call the Python API.
+        PyGILState_STATE gil_state = PyGILState_Ensure();
+
+        Port* port = PyObject_New(Port, &port_type);
+        port->port = jack_port_by_id(client->client, port_id);
+
+        PyObject* callback_argument_list = Py_BuildValue("(O,i)", (PyObject*)port, registered);
+        PyObject* result = PyObject_CallObject(client->registration_callback, callback_argument_list);
+        Py_DECREF(callback_argument_list);
+        if(!result) {
+            PyErr_PrintEx(0);
+        } else {
+            Py_DECREF(result);
+        }
+
+        // Release the thread. No Python API calls are allowed beyond this point.
+        PyGILState_Release(gil_state);
+    }
+}
+
+static PyObject* client___new__(PyTypeObject* type, PyObject* args, PyObject* kwargs)
+{
+    Client* self = (Client*)type->tp_alloc(type, 0);
+
+    if(self) {
+        const char* name;
+        if(!PyArg_ParseTuple(args, "s", &name)) {
+            return NULL;
+        }
+
+        jack_status_t status;
+        self->client = jack_client_open(name, JackNullOption, &status);
+        if(!self->client) {
+            if(status & JackFailure) {
+                PyErr_SetString(failure, "Overall operation failed.");
+            }
+            return NULL;
+        }
+
+        self->registration_callback = NULL;
+        int error_code = jack_set_port_registration_callback(
+            self->client,
+            jack_registration_callback,
+            (void*)self
+            );
+        if(error_code) {
+            PyErr_SetString(error, "Could not set port registration callback.");
+            return NULL;
+        }
+    }
+
+    return (PyObject*)self;
+}
+
+static PyObject* client_activate(Client* self) {
+    int error_code = jack_activate(self->client);
+    if(error_code) {
+        PyErr_SetString(error, "");
+        return NULL;
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+}
+
+static PyObject* client_get_name(Client* self)
+{
+    return (PyObject*)PyString_FromString(jack_get_client_name(self->client));
+}
+
+static PyObject* client_set_port_registration_callback(Client* self, PyObject* args)
+{
+    PyObject* callback;
+    if(!PyArg_ParseTuple(args, "O", &callback)) {
+        return NULL;
+    }
+    if(!PyCallable_Check(callback)) {
+        PyErr_SetString(PyExc_TypeError, "Parameter must be callable.");
+        return NULL;
+    }
+
+    Py_XINCREF(callback);
+    Py_XDECREF(self->registration_callback);
+    self->registration_callback = callback;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static void client_dealloc(Client* self)
+{
+    jack_client_close(self->client);
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyMethodDef client_methods[] = {
+    {
+        "activate",
+        (PyCFunction)client_activate,
+        METH_NOARGS,
+        "Tell the Jack server that the program is ready to start processing audio.",
+        },
+    {
+        "get_name",
+        (PyCFunction)client_get_name,
+        METH_NOARGS,
+        "Return client's actual name.",
+        },
+    {
+        "set_port_registration_callback",
+        (PyCFunction)client_set_port_registration_callback,
+        METH_VARARGS,
+        "Tell the JACK server to call a function whenever a port is registered or unregistered.",
+        },
+    {NULL},
+    };
+
+static PyObject* port_get_name(Port* self)
+{
+    return (PyObject*)PyString_FromString(jack_port_name(self->port));
+}
+
+static PyObject* port___repr__(Port* self)
+{
+    static PyObject* format;
+    if(!format) {
+        format = PyString_FromString("jack.Port(name = '%s')");
+    }
+    PyObject* name = PyString_FromString(jack_port_name(self->port));
+    PyObject* repr = PyString_Format(format, name);
+    Py_DECREF(name);
+    return repr;
+}
+
+static PyMethodDef port_methods[] = {
+    {
+        "get_name",
+        (PyCFunction)port_get_name,
+        METH_NOARGS,
+        "Return port's name.",
+        },
+    {NULL},
+    };
+
+PyMODINIT_FUNC initjack()
+{
+    // Initialize and acquire the global interpreter lock.
+    // This must be done in the main thread before creating engaging in any thread operations.
+    PyEval_InitThreads();
+
+    PyObject* module = Py_InitModule("jack", NULL);
+    if(!module) {
+        return;
+    }
+
+    error = PyErr_NewException((char*)"jack.Error", NULL, NULL);
+    Py_INCREF(error);
+    PyModule_AddObject(module, "Error", error);
+
+    failure = PyErr_NewException((char*)"jack.Failure", error, NULL);
+    Py_INCREF(failure);
+    PyModule_AddObject(module, "Failure", failure);
+
+    static PyTypeObject client_type = {
+        PyObject_HEAD_INIT(NULL)
+        };
+    client_type.tp_name = "jack.Client";
+    client_type.tp_basicsize = sizeof(Client);
+    client_type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+    client_type.tp_new = client___new__;
+    client_type.tp_dealloc = (destructor)client_dealloc;
+    client_type.tp_methods = client_methods;
+    if(PyType_Ready(&client_type) < 0) {
+        return;
+    }
+    Py_INCREF(&client_type);
+    PyModule_AddObject(module, "Client", (PyObject*)&client_type);
+    
+    port_type.tp_name = "jack.Port";
+    port_type.tp_basicsize = sizeof(Port);
+    port_type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+    // Forbid direct instantiation.
+    port_type.tp_new = NULL; 
+    port_type.tp_repr = (reprfunc)port___repr__;
+    port_type.tp_methods = port_methods;
+    if(PyType_Ready(&port_type) < 0) {
+        return;
+    }
+    Py_INCREF(&port_type);
+    PyModule_AddObject(module, "Port", (PyObject*)&port_type);
+}

+ 2 - 0
setup.cfg

@@ -0,0 +1,2 @@
+[metadata]
+description-file = README.md

+ 22 - 0
setup.py

@@ -0,0 +1,22 @@
+from distutils.core import setup, Extension
+
+import glob
+
+setup(
+    name = 'jacker',
+    version = '0.1',
+    description = 'JACK API for Python',
+    author = 'Fabian Peter Hammerle',
+    author_email = 'fabian.hammerle@gmail.com',
+    url = 'https://github.com/fphammerle/jacker',
+    download_url = 'https://github.com/fphammerle/jacker/tarball/0.3',
+    keywords = [],
+    classifiers = [],
+    ext_modules = [
+        Extension(
+            'jack', 
+            sources = glob.glob('*.c'),
+            libraries = ['jack'],
+            )
+        ],
+    )

+ 50 - 0
tests/registration-callback.py

@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# PYTHON_ARGCOMPLETE_OK
+
+import sys
+import jack
+import time
+import argparse
+
+def port_registration_callback(port, register):
+
+    if register:
+        print(str(port) + ' registered')
+    else:
+        print(str(port) + ' unregistered')
+
+def run():
+
+    client = jack.Client("registration callback test");
+    client.set_port_registration_callback(port_registration_callback)
+    client.activate();
+
+    print("client name: " + client.get_name())
+
+    while True:
+        try:
+            time.sleep(1);
+        except KeyboardInterrupt:
+            break
+
+def _init_argparser():
+
+    argparser = argparse.ArgumentParser(description = None)
+    return argparser
+
+def main(argv):
+
+    argparser = _init_argparser()
+    try:
+        import argcomplete
+        argcomplete.autocomplete(argparser)
+    except ImportError:
+        pass
+    args = argparser.parse_args(argv)
+
+    run(**vars(args))
+
+    return 0
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv[1:]))