changeset 124:bea7cf4126b5

Add RealTime tests, simplify allocations & a couple of fixes
author Chris Cannam
date Wed, 24 Jun 2015 14:11:19 +0100
parents 89cc3595c404
children ece31f26017e
files native/PyPluginObject.cpp native/PyRealTime.cpp test/test_realtime.py vamp/__init__.py
diffstat 4 files changed, 193 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/native/PyPluginObject.cpp	Wed Jun 24 10:50:13 2015 +0100
+++ b/native/PyPluginObject.cpp	Wed Jun 24 14:11:19 2015 +0100
@@ -82,8 +82,9 @@
 PyObject *
 PyPluginObject_From_Plugin(Plugin *plugin)
 {
-    PyPluginObject *pd = 
-        (PyPluginObject *)PyType_GenericAlloc(&Plugin_Type, 0);
+    PyPluginObject *pd = PyObject_New(PyPluginObject, &Plugin_Type);
+    if (!pd) return 0;
+    
     pd->plugin = plugin;
     pd->isInitialised = false;
     pd->channels = 0;
@@ -821,9 +822,9 @@
     0,                                  /*tp_descr_set*/
     0,                                  /*tp_dictoffset*/
     0,                                  /*tp_init*/
-    PyType_GenericAlloc,                /*tp_alloc*/
+    0,                                  /*tp_alloc*/
     0,                                  /*tp_new*/
-    PyObject_Del,                       /*tp_free*/
+    0,                                  /*tp_free*/
     0,                                  /*tp_is_gc*/
 };
 
--- a/native/PyRealTime.cpp	Wed Jun 24 10:50:13 2015 +0100
+++ b/native/PyRealTime.cpp	Wed Jun 24 14:11:19 2015 +0100
@@ -42,36 +42,58 @@
 using namespace std;
 using namespace Vamp;
 
+#if (PY_MAJOR_VERSION >= 3)
+#define PyInt_AS_LONG PyLong_AS_LONG
+#define PyInt_FromSsize_t PyLong_FromSsize_t
+#endif
+
 /* CONSTRUCTOR: New RealTime object from sec and nsec */
 static PyObject*
 RealTime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
 {
-    unsigned int sec = 0;
-    unsigned int nsec = 0;
+    int sec = 0;
+    int nsec = 0;
+    int unaryInt = 0;
     double unary = 0;
     const char *fmt = NULL;
 
-    if (
-        /// new RealTime from ('format',float) e.g. ('seconds',2.34123)   
-        !PyArg_ParseTuple(args, "|sd:RealTime.new ", 
-                          (const char *) &fmt, 
-                          (double *) &unary)    &&
+    if (!PyArg_ParseTuple(args, ":RealTime.new ")) { // zero time
 
-        /// new RealTime from (sec{int},nsec{int}) e.g. (2,34)
-        !PyArg_ParseTuple(args, "|II:RealTime.new ", 
-                          (unsigned int*) &sec, 
-                          (unsigned int*) &nsec) 
-                
-        ) { 
-        PyErr_SetString(PyExc_TypeError, 
-                        "RealTime constructor requires either (sec,nsec) integer tuple, or ('format',float) where 'format' is 'seconds' or 'milliseconds'");
-        return NULL; 
+        PyErr_Clear();
+    
+        /// new RealTime from exact ('format',int) e.g. ('milliseconds',200)
+        if (!PyArg_ParseTuple(args, "si:RealTime.new ", 
+                              (const char *) &fmt, 
+                              (int *) &unaryInt)) {
+
+            PyErr_Clear();
+
+            /// new RealTime from ('format',float) e.g. ('seconds',2.34123)   
+            if (!PyArg_ParseTuple(args, "sd:RealTime.new ", 
+                                  (const char *) &fmt, 
+                                  (double *) &unary)) {
+
+                PyErr_Clear();
+
+                /// new RealTime from (sec{int},nsec{int}) e.g. (2,34)
+                if (!PyArg_ParseTuple(args, "ii:RealTime.new ", 
+                                      (int*) &sec, 
+                                      (int*) &nsec)) {
+
+                    PyErr_SetString(PyExc_TypeError, 
+                                    "RealTime constructor requires either (sec,nsec) integer tuple, or ('format',float) where 'format' is 'seconds' or 'milliseconds'");
+                    return NULL;
+                }
+            }
+        }
     }
 
     PyErr_Clear();
 
-    // RealTimeObject *self = PyObject_New(RealTimeObject, &RealTime_Type); 
-    RealTimeObject *self = (RealTimeObject*)type->tp_alloc(type, 0);
+    // Using PyObject_New because we use PyObject_Del to delete in the
+    // destructor
+    RealTimeObject *self = PyObject_New(RealTimeObject, &RealTime_Type);
+    PyObject_Init((PyObject *)self, &RealTime_Type);
         
     if (self == NULL) return NULL;
 
@@ -84,13 +106,22 @@
     else { 
         /// new RealTime from seconds or milliseconds: i.e. >>>RealTime('seconds',12.3)
         if (!string(fmt).compare("float") ||
-            !string(fmt).compare("seconds"))  
-            self->rt = new RealTime( 
-                RealTime::fromSeconds((double) unary)); 
+            !string(fmt).compare("seconds")) {
 
-        if (!string(fmt).compare("milliseconds")) {
-            self->rt = new RealTime( 
-                RealTime::fromSeconds((double) unary / 1000.0)); }
+            if (unaryInt != 0) {
+                self->rt = new RealTime(RealTime::fromMilliseconds(unaryInt * 1000));
+            } else {
+                self->rt = new RealTime(RealTime::fromSeconds(unary));
+            }
+            
+        } else if (!string(fmt).compare("milliseconds")) {
+            
+            if (unaryInt != 0) {
+                self->rt = new RealTime(RealTime::fromMilliseconds(unaryInt));
+            } else {
+                self->rt = new RealTime(RealTime::fromSeconds(unary / 1000.0));
+            }
+        }
     }
 
     if (!self->rt) { 
@@ -106,10 +137,12 @@
 static void
 RealTimeObject_dealloc(RealTimeObject *self)
 {
-    if (self->rt) delete self->rt;      //delete the C object
-    PyObject_Del(self); //delete the Python object (original)
-    /// this requires PyType_Ready() which fills ob_type
-    // self->ob_type->tp_free((PyObject*)self); 
+    delete self->rt;      // delete the C object
+
+    // "If the type is not subtypable (doesn’t have the
+    // Py_TPFLAGS_BASETYPE flag bit set), it is permissible to call
+    // the object deallocator directly instead of via tp_free"
+    PyObject_Del(self); // delete the Python object (original)
 }
 
 /* RealTime Object's Methods */ 
@@ -161,10 +194,10 @@
 /* Type object's (RealTime) methods table */
 static PyMethodDef RealTime_methods[] = 
 {
-    {"values",  (PyCFunction)RealTime_values,   METH_NOARGS,
+    {"values", (PyCFunction)RealTime_values,   METH_NOARGS,
      PyDoc_STR("values() -> Tuple of sec,nsec representation.")},
 
-    {"to_string",        (PyCFunction)RealTime_toString, METH_NOARGS,
+    {"to_string", (PyCFunction)RealTime_toString, METH_NOARGS,
      PyDoc_STR("to_string() -> Return a user-readable string to the nearest millisecond in a form like HH:MM:SS.mmm")},
 
     {"to_frame", (PyCFunction)RealTime_toFrame,  METH_VARARGS,
@@ -182,21 +215,16 @@
 
 /* Object Protocol */
 
-#if (PY_MAJOR_VERSION >= 3)
-#define PyInt_AS_LONG PyLong_AS_LONG
-#define PyInt_FromSsize_t PyLong_FromSsize_t
-#endif
-
 static int
 RealTime_setattr(RealTimeObject *self, char *name, PyObject *value)
 {
-    if ( !string(name).compare("sec")) {
-        self->rt->sec= (int) PyInt_AS_LONG(value);
+    if (!string(name).compare("sec")) {
+        self->rt->sec = (int) PyInt_AS_LONG(value);
         return 0;
     }
 
-    if ( !string(name).compare("nsec")) { 
-        self->rt->nsec= (int) PyInt_AS_LONG(value);
+    if (!string(name).compare("nsec")) { 
+        self->rt->nsec = (int) PyInt_AS_LONG(value);
         return 0;
     }
 
@@ -279,9 +307,9 @@
 static PyObject *
 RealTime_add(PyObject *s, PyObject *w)
 {
-    RealTimeObject *result = 
-        PyObject_New(RealTimeObject, &RealTime_Type); 
+    RealTimeObject *result = PyObject_New(RealTimeObject, &RealTime_Type); 
     if (result == NULL) return NULL;
+    PyObject_Init((PyObject *)result, &RealTime_Type);
 
     result->rt = new RealTime(
         *((RealTimeObject*)s)->rt + *((RealTimeObject*)w)->rt);
@@ -291,9 +319,9 @@
 static PyObject *
 RealTime_subtract(PyObject *s, PyObject *w)
 {
-    RealTimeObject *result = 
-        PyObject_New(RealTimeObject, &RealTime_Type); 
+    RealTimeObject *result = PyObject_New(RealTimeObject, &RealTime_Type); 
     if (result == NULL) return NULL;
+    PyObject_Init((PyObject *)result, &RealTime_Type);
 
     result->rt = new RealTime(
         *((RealTimeObject*)s)->rt - *((RealTimeObject*)w)->rt);
@@ -331,14 +359,11 @@
 
 /* REAL-TIME TYPE OBJECT */
 
-#define RealTime_alloc PyType_GenericAlloc
-#define RealTime_free PyObject_Del
-
 /* Doc:: 10.3 Type Objects */ /* static */ 
 PyTypeObject RealTime_Type = 
 {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "vampy.RealTime",           /*tp_name*/
+    "vampyhost.RealTime",           /*tp_name*/
     sizeof(RealTimeObject),     /*tp_basicsize*/
     0,                          /*tp_itemsize*/
     /*          methods         */
@@ -374,9 +399,9 @@
     0,                      /*tp_descr_set*/
     0,                      /*tp_dictoffset*/
     0,                      /*tp_init*/
-    RealTime_alloc,         /*tp_alloc*/
+    0,                      /*tp_alloc*/
     RealTime_new,           /*tp_new*/
-    RealTime_free,          /*tp_free*/
+    0,                      /*tp_free*/
     0,                      /*tp_is_gc*/
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test_realtime.py	Wed Jun 24 14:11:19 2015 +0100
@@ -0,0 +1,109 @@
+
+import vamp
+
+def test_basic_conf_compare_sec():
+    r1 = vamp.vampyhost.RealTime('seconds', 0)
+    r2 = vamp.vampyhost.RealTime('seconds', 0)
+    assert r1 == r2
+    r2a = vamp.vampyhost.RealTime()
+    assert r1 == r2a
+    r3 = vamp.vampyhost.RealTime('seconds', 1.5)
+    assert r1 != r3
+    assert r2 != r3
+    assert r1 < r3
+    assert r3 > r2
+    assert r1 <= r3
+    assert r3 >= r2
+    assert r1 >= r2
+
+def test_basic_conf_compare_msec():
+    r1 = vamp.vampyhost.RealTime('milliseconds', 0)
+    r2 = vamp.vampyhost.RealTime('milliseconds', 0)
+    assert r1 == r2
+    r3 = vamp.vampyhost.RealTime('milliseconds', 1500)
+    assert r1 != r3
+    assert r2 != r3
+    assert r1 < r3
+    assert r3 > r2
+    assert r1 <= r3
+    assert r3 >= r2
+    assert r1 >= r2
+
+def test_basic_conf_compare_sec_msec():
+    r1 = vamp.vampyhost.RealTime('milliseconds', 0)
+    r2 = vamp.vampyhost.RealTime('seconds', 0)
+    assert r1 == r2
+    r3 = vamp.vampyhost.RealTime('milliseconds', 1500)
+    r4 = vamp.vampyhost.RealTime('seconds', 1.5)
+    assert r3 == r4
+    assert r1 != r3
+    assert r2 != r3
+    assert r1 < r3
+    assert r3 > r2
+    assert r1 <= r3
+    assert r3 >= r2
+    assert r4 >= r2
+    assert r1 >= r2
+    assert r4 <= r3
+
+def test_basic_conf_compare_int_float():
+    r1 = vamp.vampyhost.RealTime('seconds', 100)
+    r2 = vamp.vampyhost.RealTime('seconds', 100.0)
+    assert r1 == r2
+    r2n = vamp.vampyhost.RealTime('seconds', 100.00001)
+    assert r1 != r2n
+    assert r2 != r2n
+    r1 = vamp.vampyhost.RealTime('milliseconds', 100)
+    r2 = vamp.vampyhost.RealTime('milliseconds', 100.0)
+    r2n = vamp.vampyhost.RealTime('milliseconds', 100.00001)
+    r3 = vamp.vampyhost.RealTime('seconds', 0.1)
+    assert r1 == r2
+    assert r1 != r2n
+    assert r2 != r2n
+    assert r1 == r3
+    assert r2 == r3
+    
+def test_basic_conf_compare_tuple():
+    r1 = vamp.vampyhost.RealTime(0, 0)
+    r2 = vamp.vampyhost.RealTime(0, 0)
+    assert r1 == r2
+    r3 = vamp.vampyhost.RealTime(1, 500000000)
+    r4 = vamp.vampyhost.RealTime('seconds', 1.5)
+    assert r3 == r4
+    assert r1 != r3
+    assert r2 != r3
+    assert r1 < r3
+    assert r3 > r2
+    assert r1 <= r3
+    assert r3 >= r2
+    assert r4 >= r2
+    assert r1 >= r2
+    assert r4 <= r3
+
+def test_conv_float():
+    r = vamp.vampyhost.RealTime('seconds', 0)
+    assert float(r) == 0.0
+
+def test_conv_float():
+    r = vamp.vampyhost.RealTime('seconds', 0)
+    assert float(r) == 0.0
+
+def test_conv_str():    
+    r = vamp.vampyhost.RealTime('seconds', 0)
+    assert str(r) == " 0.000000000"
+    r = vamp.vampyhost.RealTime('seconds', 1.5)
+    assert str(r) == " 1.500000000"
+    r = vamp.vampyhost.RealTime('seconds', -2)
+    assert str(r) == "-2.000000000"
+    r = vamp.vampyhost.RealTime(-1, -500000000)
+    assert str(r) == "-1.500000000"
+
+def test_add_subtract():
+    r1 = vamp.vampyhost.RealTime('milliseconds', 400)
+    r2 = vamp.vampyhost.RealTime('milliseconds', 600)
+    r3 = vamp.vampyhost.RealTime('seconds', 1)
+    assert r1 + r2 == r3
+    assert r3 - r2 - r1 == vamp.vampyhost.RealTime()
+    assert r2 - r1 == vamp.vampyhost.RealTime('milliseconds', 200)
+    assert r1 - r2 == vamp.vampyhost.RealTime('milliseconds', -200)
+    
--- a/vamp/__init__.py	Wed Jun 24 10:50:13 2015 +0100
+++ b/vamp/__init__.py	Wed Jun 24 14:11:19 2015 +0100
@@ -28,8 +28,11 @@
 #   dealings in this Software without prior written authorization.
 
 '''Load and use Vamp plugins for audio feature analysis. This module
-is a high-level interface to the vampyhost extension, for quickly and
-easily running Vamp analysis plugins on buffers of audio data.'''
+is a high-level interface wrapper around a native-code extension, for
+quickly and easily running Vamp analysis plugins on buffers of audio
+data. For low-level plugin loading and manipulation, refer to the
+vamp.vampyhost namespace which contains the native-code extension.
+'''
 
 import vampyhost