Chris@26: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@26: Chris@26: /* Chris@26: VampyHost Chris@26: Chris@26: Use Vamp audio analysis plugins in Python Chris@26: Chris@26: Gyorgy Fazekas and Chris Cannam Chris@26: Centre for Digital Music, Queen Mary, University of London Chris@117: Copyright 2008-2015 Queen Mary, University of London Chris@26: Chris@26: Permission is hereby granted, free of charge, to any person Chris@26: obtaining a copy of this software and associated documentation Chris@26: files (the "Software"), to deal in the Software without Chris@26: restriction, including without limitation the rights to use, copy, Chris@26: modify, merge, publish, distribute, sublicense, and/or sell copies Chris@26: of the Software, and to permit persons to whom the Software is Chris@26: furnished to do so, subject to the following conditions: Chris@26: Chris@26: The above copyright notice and this permission notice shall be Chris@26: included in all copies or substantial portions of the Software. Chris@26: Chris@26: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@26: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@26: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@26: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@26: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@26: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@26: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@26: Chris@26: Except as contained in this notice, the names of the Centre for Chris@26: Digital Music; Queen Mary, University of London; and the authors Chris@26: shall not be used in advertising or otherwise to promote the sale, Chris@26: use or other dealings in this Software without prior written Chris@26: authorization. Chris@26: */ Chris@26: Chris@26: #include "PyRealTime.h" Chris@30: Chris@26: #include Chris@26: Chris@26: using namespace std; Chris@26: using namespace Vamp; Chris@26: Chris@124: #if (PY_MAJOR_VERSION >= 3) Chris@124: #define PyInt_AS_LONG PyLong_AS_LONG Chris@124: #define PyInt_FromSsize_t PyLong_FromSsize_t Chris@124: #endif Chris@124: Chris@26: /* CONSTRUCTOR: New RealTime object from sec and nsec */ Chris@26: static PyObject* Chris@26: RealTime_new(PyTypeObject *type, PyObject *args, PyObject *kw) Chris@26: { Chris@124: int sec = 0; Chris@124: int nsec = 0; Chris@124: int unaryInt = 0; Chris@26: double unary = 0; Chris@26: const char *fmt = NULL; Chris@26: Chris@124: if (!PyArg_ParseTuple(args, ":RealTime.new ")) { // zero time Chris@26: Chris@124: PyErr_Clear(); Chris@124: Chris@124: /// new RealTime from exact ('format',int) e.g. ('milliseconds',200) Chris@124: if (!PyArg_ParseTuple(args, "si:RealTime.new ", Chris@124: (const char *) &fmt, Chris@124: (int *) &unaryInt)) { Chris@124: Chris@124: PyErr_Clear(); Chris@124: Chris@124: /// new RealTime from ('format',float) e.g. ('seconds',2.34123) Chris@124: if (!PyArg_ParseTuple(args, "sd:RealTime.new ", Chris@124: (const char *) &fmt, Chris@124: (double *) &unary)) { Chris@124: Chris@124: PyErr_Clear(); Chris@124: Chris@124: /// new RealTime from (sec{int},nsec{int}) e.g. (2,34) Chris@124: if (!PyArg_ParseTuple(args, "ii:RealTime.new ", Chris@124: (int*) &sec, Chris@124: (int*) &nsec)) { Chris@124: Chris@124: PyErr_SetString(PyExc_TypeError, Chris@124: "RealTime constructor requires either (sec,nsec) integer tuple, or ('format',float) where 'format' is 'seconds' or 'milliseconds'"); Chris@124: return NULL; Chris@124: } Chris@124: } Chris@124: } Chris@26: } Chris@26: Chris@87: PyErr_Clear(); Chris@26: Chris@124: // Using PyObject_New because we use PyObject_Del to delete in the Chris@124: // destructor Chris@124: RealTimeObject *self = PyObject_New(RealTimeObject, &RealTime_Type); Chris@124: PyObject_Init((PyObject *)self, &RealTime_Type); Chris@39: Chris@26: if (self == NULL) return NULL; Chris@26: Chris@26: self->rt = NULL; Chris@26: Chris@26: if (sec == 0 && nsec == 0 && fmt == 0) Chris@39: self->rt = new RealTime(); Chris@26: else if (fmt == 0) Chris@39: self->rt = new RealTime(sec,nsec); Chris@26: else { Chris@26: /// new RealTime from seconds or milliseconds: i.e. >>>RealTime('seconds',12.3) Chris@39: if (!string(fmt).compare("float") || Chris@124: !string(fmt).compare("seconds")) { Chris@26: Chris@124: if (unaryInt != 0) { Chris@124: self->rt = new RealTime(RealTime::fromMilliseconds(unaryInt * 1000)); Chris@124: } else { Chris@124: self->rt = new RealTime(RealTime::fromSeconds(unary)); Chris@124: } Chris@124: Chris@124: } else if (!string(fmt).compare("milliseconds")) { Chris@124: Chris@124: if (unaryInt != 0) { Chris@124: self->rt = new RealTime(RealTime::fromMilliseconds(unaryInt)); Chris@124: } else { Chris@124: self->rt = new RealTime(RealTime::fromSeconds(unary / 1000.0)); Chris@124: } Chris@124: } Chris@26: } Chris@26: Chris@26: if (!self->rt) { Chris@39: PyErr_SetString(PyExc_TypeError, Chris@39: "RealTime initialised with wrong arguments."); Chris@39: return NULL; Chris@26: } Chris@26: Chris@26: return (PyObject *) self; Chris@26: } Chris@26: Chris@26: /* DESTRUCTOR: delete type object */ Chris@26: static void Chris@26: RealTimeObject_dealloc(RealTimeObject *self) Chris@26: { Chris@124: delete self->rt; // delete the C object Chris@124: Chris@124: // "If the type is not subtypable (doesn’t have the Chris@124: // Py_TPFLAGS_BASETYPE flag bit set), it is permissible to call Chris@124: // the object deallocator directly instead of via tp_free" Chris@124: PyObject_Del(self); // delete the Python object (original) Chris@26: } Chris@26: Chris@26: /* RealTime Object's Methods */ Chris@26: //these are internals not exposed by the module but the object Chris@26: Chris@26: /* Returns a Tuple containing sec and nsec values */ Chris@26: static PyObject * Chris@26: RealTime_values(RealTimeObject *self) Chris@26: { Chris@134: return Py_BuildValue("(ii)", self->rt->sec, self->rt->nsec); Chris@26: } Chris@26: Chris@26: /* Returns a Text representation */ Chris@26: static PyObject * Chris@26: RealTime_toString(RealTimeObject *self, PyObject *args) Chris@26: { Chris@134: return Py_BuildValue("s", self->rt->toText().c_str()); Chris@26: } Chris@26: Chris@26: /* Frame representation */ Chris@26: static PyObject * Chris@26: RealTime_toFrame(PyObject *self, PyObject *args) Chris@26: { Chris@26: unsigned int samplerate; Chris@39: Chris@134: if (!PyArg_ParseTuple(args, "I:realtime.toFrame object ", Chris@134: (unsigned int *) &samplerate)) { Chris@39: PyErr_SetString(PyExc_ValueError,"Integer Sample Rate Required."); Chris@39: return NULL; Chris@26: } Chris@39: Chris@26: return Py_BuildValue("k", Chris@39: RealTime::realTime2Frame( Chris@39: *(const RealTime*) ((RealTimeObject*)self)->rt, Chris@39: (unsigned int) samplerate)); Chris@26: } Chris@26: Chris@26: /* Conversion of realtime to a double precision floating point value */ Chris@26: /* ...in Python called by e.g. float(realtime) */ Chris@26: static PyObject * Chris@26: RealTime_float(PyObject *s) Chris@26: { Chris@26: double drt = ((double) ((RealTimeObject*)s)->rt->sec + Chris@39: (double)((double) ((RealTimeObject*)s)->rt->nsec)/1000000000); Chris@39: return PyFloat_FromDouble(drt); Chris@26: } Chris@26: Chris@26: Chris@26: /* Type object's (RealTime) methods table */ Chris@26: static PyMethodDef RealTime_methods[] = Chris@26: { Chris@124: {"values", (PyCFunction)RealTime_values, METH_NOARGS, Chris@26: PyDoc_STR("values() -> Tuple of sec,nsec representation.")}, Chris@26: Chris@124: {"to_string", (PyCFunction)RealTime_toString, METH_NOARGS, Chris@82: PyDoc_STR("to_string() -> Return a user-readable string to the nearest millisecond in a form like HH:MM:SS.mmm")}, Chris@26: Chris@82: {"to_frame", (PyCFunction)RealTime_toFrame, METH_VARARGS, Chris@82: PyDoc_STR("to_frame(samplerate) -> Sample count for given sample rate.")}, Chris@26: Chris@82: {"to_float", (PyCFunction)RealTime_float, METH_NOARGS, Chris@82: PyDoc_STR("to_float() -> Floating point representation.")}, Chris@39: Chris@39: {NULL, NULL} /* sentinel */ Chris@26: }; Chris@26: Chris@26: Chris@26: /* Methods implementing protocols */ Chris@26: // these functions are called by the interpreter Chris@26: Chris@26: /* Object Protocol */ Chris@26: Chris@26: static int Chris@26: RealTime_setattr(RealTimeObject *self, char *name, PyObject *value) Chris@26: { Chris@124: if (!string(name).compare("sec")) { Chris@124: self->rt->sec = (int) PyInt_AS_LONG(value); Chris@39: return 0; Chris@26: } Chris@26: Chris@124: if (!string(name).compare("nsec")) { Chris@124: self->rt->nsec = (int) PyInt_AS_LONG(value); Chris@39: return 0; Chris@26: } Chris@26: Chris@26: return -1; Chris@26: } Chris@26: Chris@26: static PyObject * Chris@112: RealTime_getattro(RealTimeObject *self, PyObject *nameobj) Chris@26: { Chris@112: string name; Chris@112: #if PY_MAJOR_VERSION < 3 Chris@112: name = PyString_AsString(nameobj); Chris@112: #else Chris@112: name = PyBytes_AsString(PyUnicode_AsUTF8String(nameobj)); Chris@112: #endif Chris@112: Chris@26: if ( !string(name).compare("sec") ) { Chris@39: return PyInt_FromSsize_t( Chris@39: (Py_ssize_t) self->rt->sec); Chris@26: } Chris@26: Chris@26: if ( !string(name).compare("nsec") ) { Chris@39: return PyInt_FromSsize_t( Chris@39: (Py_ssize_t) self->rt->nsec); Chris@26: } Chris@26: Chris@112: return PyObject_GenericGetAttr((PyObject *)self, nameobj); Chris@26: } Chris@26: Chris@112: static PyObject * Chris@112: RealTime_richcompare(PyObject *self, PyObject *other, int op) Chris@73: { Chris@73: if (!PyRealTime_Check(self) || !PyRealTime_Check(other)) { Chris@73: PyErr_SetString(PyExc_TypeError, "RealTime Object Expected."); Chris@112: return Py_False; Chris@73: } Chris@73: Chris@113: RealTime *ap = PyRealTime_AS_REALTIME(self); Chris@113: RealTime *bp = PyRealTime_AS_REALTIME(other); Chris@113: Chris@113: if (!ap || !bp) return Py_False; Chris@113: const RealTime &a = *ap; Chris@113: const RealTime &b = *bp; Chris@73: Chris@116: // cerr << "a = " << a << ", b = " << b << ", op = " << op << endl; Chris@116: Chris@116: PyObject *result = Py_False; Chris@116: Chris@112: if (op == Py_LT) { Chris@116: result = (a < b) ? Py_True : Py_False; Chris@112: } else if (op == Py_LE) { Chris@116: result = (a <= b) ? Py_True : Py_False; Chris@112: } else if (op == Py_EQ) { Chris@116: result = (a == b) ? Py_True : Py_False; Chris@112: } else if (op == Py_NE) { Chris@116: result = (a != b) ? Py_True : Py_False; Chris@112: } else if (op == Py_GT) { Chris@116: result = (a > b) ? Py_True : Py_False; Chris@112: } else if (op == Py_GE) { Chris@116: result = (a >= b) ? Py_True : Py_False; Chris@112: } Chris@116: Chris@116: // cerr << "returning: " << (result == Py_True ? "true" : "false") << endl; Chris@116: Chris@134: Py_INCREF(result); Chris@116: return result; Chris@73: } Chris@73: Chris@26: /* String representation called by e.g. str(realtime), print realtime*/ Chris@26: static PyObject * Chris@26: RealTime_repr(PyObject *self) Chris@26: { Chris@26: return Py_BuildValue("s", Chris@39: ((RealTimeObject*)self)->rt->toString().c_str()); Chris@26: } Chris@26: Chris@26: Chris@26: /* Number Protocol */ Chris@26: /// TODO: implement all methods available in Vamp::RealTime() objects Chris@26: Chris@26: static PyObject * Chris@26: RealTime_add(PyObject *s, PyObject *w) Chris@26: { Chris@124: RealTimeObject *result = PyObject_New(RealTimeObject, &RealTime_Type); Chris@26: if (result == NULL) return NULL; Chris@124: PyObject_Init((PyObject *)result, &RealTime_Type); Chris@26: Chris@26: result->rt = new RealTime( Chris@39: *((RealTimeObject*)s)->rt + *((RealTimeObject*)w)->rt); Chris@26: return (PyObject*)result; Chris@26: } Chris@26: Chris@26: static PyObject * Chris@26: RealTime_subtract(PyObject *s, PyObject *w) Chris@26: { Chris@124: RealTimeObject *result = PyObject_New(RealTimeObject, &RealTime_Type); Chris@26: if (result == NULL) return NULL; Chris@124: PyObject_Init((PyObject *)result, &RealTime_Type); Chris@26: Chris@26: result->rt = new RealTime( Chris@39: *((RealTimeObject*)s)->rt - *((RealTimeObject*)w)->rt); Chris@26: return (PyObject*)result; Chris@26: } Chris@26: Chris@26: static PyNumberMethods realtime_as_number = Chris@26: { Chris@112: (binaryfunc)RealTime_add, /*nb_add*/ Chris@112: (binaryfunc)RealTime_subtract, /*nb_subtract*/ Chris@39: 0, /*nb_multiply*/ Chris@112: #if (PY_MAJOR_VERSION < 3) Chris@39: 0, /*nb_divide*/ Chris@112: #endif Chris@39: 0, /*nb_remainder*/ Chris@39: 0, /*nb_divmod*/ Chris@39: 0, /*nb_power*/ Chris@39: 0, /*nb_neg*/ Chris@39: 0, /*nb_pos*/ Chris@39: 0, /*(unaryfunc)array_abs,*/ Chris@39: 0, /*nb_nonzero*/ Chris@39: 0, /*nb_invert*/ Chris@39: 0, /*nb_lshift*/ Chris@39: 0, /*nb_rshift*/ Chris@39: 0, /*nb_and*/ Chris@39: 0, /*nb_xor*/ Chris@39: 0, /*nb_or*/ Chris@112: #if (PY_MAJOR_VERSION < 3) Chris@26: 0, /*nb_coerce*/ Chris@112: #endif Chris@39: 0, /*nb_int*/ Chris@39: 0, /*nb_long*/ Chris@26: (unaryfunc)RealTime_float,/*nb_float*/ Chris@26: }; Chris@26: Chris@26: /* REAL-TIME TYPE OBJECT */ Chris@26: Chris@26: /* Doc:: 10.3 Type Objects */ /* static */ Chris@26: PyTypeObject RealTime_Type = Chris@26: { Chris@112: PyVarObject_HEAD_INIT(NULL, 0) Chris@124: "vampyhost.RealTime", /*tp_name*/ Chris@39: sizeof(RealTimeObject), /*tp_basicsize*/ Chris@39: 0, /*tp_itemsize*/ Chris@39: /* methods */ Chris@26: (destructor)RealTimeObject_dealloc, /*tp_dealloc*/ Chris@39: 0, /*tp_print*/ Chris@112: 0, /*tp_getattr*/ Chris@26: (setattrfunc)RealTime_setattr, /*tp_setattr*/ Chris@112: 0, /*tp_compare*/ Chris@39: RealTime_repr, /*tp_repr*/ Chris@39: &realtime_as_number, /*tp_as_number*/ Chris@39: 0, /*tp_as_sequence*/ Chris@39: 0, /*tp_as_mapping*/ Chris@39: 0, /*tp_hash*/ Chris@39: 0, /*tp_call*/ Chris@26: 0, /*tp_str*/ Chris@112: (getattrofunc)RealTime_getattro, /*tp_getattro*/ Chris@26: 0, /*tp_setattro*/ Chris@26: 0, /*tp_as_buffer*/ Chris@26: Py_TPFLAGS_DEFAULT, /*tp_flags*/ Chris@39: "RealTime object, used for Vamp plugin timestamps.", /*tp_doc*/ Chris@26: 0, /*tp_traverse*/ Chris@26: 0, /*tp_clear*/ Chris@112: (richcmpfunc)RealTime_richcompare, /*tp_richcompare*/ Chris@26: 0, /*tp_weaklistoffset*/ Chris@26: 0, /*tp_iter*/ Chris@26: 0, /*tp_iternext*/ Chris@26: RealTime_methods, /*tp_methods*/ //TypeObject Methods Chris@26: 0, /*tp_members*/ Chris@26: 0, /*tp_getset*/ Chris@26: 0, /*tp_base*/ Chris@26: 0, /*tp_dict*/ Chris@26: 0, /*tp_descr_get*/ Chris@26: 0, /*tp_descr_set*/ Chris@26: 0, /*tp_dictoffset*/ Chris@26: 0, /*tp_init*/ Chris@124: 0, /*tp_alloc*/ Chris@26: RealTime_new, /*tp_new*/ Chris@124: 0, /*tp_free*/ Chris@26: 0, /*tp_is_gc*/ Chris@26: }; Chris@26: Chris@26: Chris@26: Chris@26: /* PyRealTime C++ API */ Chris@26: Chris@26: /*PyRealTime from RealTime*/ Chris@26: PyObject* Chris@26: PyRealTime_FromRealTime(const Vamp::RealTime& rt) { Chris@26: Chris@26: RealTimeObject *self = Chris@39: PyObject_New(RealTimeObject, &RealTime_Type); Chris@26: if (self == NULL) return NULL; Chris@26: Chris@26: self->rt = new RealTime(rt); Chris@26: return (PyObject*) self; Chris@26: } Chris@26: Chris@26: /*RealTime* from PyRealTime*/ Chris@26: const Vamp::RealTime* Chris@26: PyRealTime_AsRealTime (PyObject *self) { Chris@26: Chris@26: RealTimeObject *s = (RealTimeObject*) self; Chris@26: Chris@26: if (!PyRealTime_Check(s)) { Chris@39: PyErr_SetString(PyExc_TypeError, "RealTime Object Expected."); Chris@39: cerr << "in call PyRealTime_AsPointer(): RealTime Object Expected. " << endl; Chris@113: return NULL; Chris@113: } Chris@26: return s->rt; Chris@26: }; Chris@26: