annotate bindings/python/pyadbmodule.c @ 749:dd4b9fec8d85

added support for timesData
author mas01mc
date Wed, 24 Nov 2010 13:50:05 +0000
parents e5f96902afaf
children d93292ae7c1b
rev   line source
map01bf@620 1 // pyadbmodule.c
map01bf@620 2 //
map01bf@620 3 // the internal portion of the wrapper for audio
map01bf@620 4 // see pyadb.py for the public classes
map01bf@620 5 //
map01bf@620 6 // Created by Benjamin Fields on 2009-09-04.
map01bf@716 7 // Big update for direct data insertion 2010-June (Benjamin Fields)
map01bf@716 8 // Copyleft 2009, 2010 Goldsmith University of London.
map01bf@621 9 // Distributed and licensed under GPL2. See ../../license.txt for details.
map01bf@620 10 //
map01bf@620 11 #include <fcntl.h>
map01bf@621 12 #include <string.h>
map01bf@620 13 #include "Python.h"
map01bf@622 14 #include "structmember.h"
map01bf@620 15 #include "audioDB_API.h"
map01bf@620 16 #include "numpy/arrayobject.h"
map01bf@620 17
mas01mc@746 18 #define ADB_HEADER_FLAG_L2NORM (0x1U)
mas01mc@746 19 #define ADB_HEADER_FLAG_POWER (0x4U)
mas01mc@746 20 #define ADB_HEADER_FLAG_TIMES (0x20U)
mas01mc@746 21 #define ADB_HEADER_FLAG_REFERENCES (0x40U)
mas01mc@746 22
map01bf@620 23 static void _pyadb_close(void *ptr);
map01bf@620 24
map01bf@620 25 /* create a new database */
map01bf@620 26 /* returns a struct or NULL on failure */
map01bf@620 27 /* api call: */
mas01cr@671 28 /* adb_t *audiodb_create(const char *path, unsigned datasize, unsigned ntracks, unsigned datadim);*/
map01bf@620 29 PyObject * _pyadb_create(PyObject *self, PyObject *args)
map01bf@620 30 {
map01bf@620 31 unsigned datasize, ntracks, datadim;
map01bf@620 32 const char *path;
map01bf@620 33 int ok;
mas01cr@671 34 adb_t *new_database;
map01bf@620 35 ok = PyArg_ParseTuple(args, "sIII", &path, &datasize, &ntracks, &datadim);
map01bf@620 36 if (!ok) return 0;
map01bf@620 37 new_database = audiodb_create(path, datasize, ntracks, datadim);
map01bf@620 38 if (!new_database) return 0;
map01bf@620 39
mas01mc@746 40 return PyCObject_FromVoidPtr( new_database, _pyadb_close);
map01bf@620 41 }
map01bf@620 42
map01bf@620 43 /* open an existing database */
map01bf@620 44 /* returns a struct or NULL on failure */
map01bf@620 45 /* flags expects fcntl flags concerning the opening mode */
map01bf@620 46 /* api call: */
mas01cr@671 47 // adb_t *audiodb_open(const char *path, int flags);
map01bf@620 48 PyObject * _pyadb_open(PyObject *self, PyObject *args)
map01bf@620 49 {
map01bf@620 50 const char *path;
map01bf@620 51 char mode;
map01bf@620 52 int ok;//in python layer need to translate boolean flags to byte mask
mas01cr@671 53 adb_t *fresh_database;
map01bf@620 54 ok = PyArg_ParseTuple(args, "sc", &path, &mode);
map01bf@620 55 if (!ok) return 0;
map01bf@620 56 if (mode == 'r'){
map01bf@620 57 fresh_database = audiodb_open(path, O_RDONLY);
map01bf@620 58 }else if (mode == 'w'){
map01bf@620 59 fresh_database = audiodb_open(path, O_RDWR);
map01bf@620 60 }else{
map01bf@620 61 PyErr_SetString(PyExc_ValueError,
map01bf@620 62 "mode must be either \'r\' or \'w\'. It appears to be something else.");
map01bf@620 63 return 0;
map01bf@620 64 }
map01bf@620 65 if (!fresh_database) return 0;
map01bf@620 66
map01bf@620 67 return PyCObject_FromVoidPtr( fresh_database, _pyadb_close);
map01bf@620 68 }
map01bf@620 69
map01bf@620 70 /* database status */
map01bf@620 71 /* api call: */
mas01cr@671 72 // int audiodb_status(adb_t *mydb, adb_status_ptr status);
map01bf@620 73 PyObject * _pyadb_status(PyObject *self, PyObject *args)
map01bf@620 74 {
mas01cr@671 75 adb_t *check_db;
mas01cr@671 76 adb_status_t *status;
map01bf@620 77 int flags, ok;
map01bf@620 78 PyObject * incoming = 0;
mas01cr@671 79 status = (adb_status_t *)malloc(sizeof(adb_status_t));
map01bf@620 80
map01bf@620 81 ok = PyArg_ParseTuple(args, "O", &incoming);
map01bf@620 82 if (!ok) return 0;
mas01cr@671 83 check_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@620 84
map01bf@620 85
map01bf@620 86 flags = audiodb_status(check_db, status);
map01bf@620 87 return Py_BuildValue("IIIIILL", status->numFiles,
map01bf@620 88 status->dim,
map01bf@620 89 status->dudCount,
map01bf@620 90 status->nullCount,
map01bf@620 91 status->flags,
map01bf@620 92 status->length,
map01bf@620 93 status->data_region_size);
map01bf@620 94
map01bf@620 95 }
map01bf@620 96
map01bf@620 97 /*engage l2norm in the referenced db*/
map01bf@620 98 /*api call:*/
mas01cr@671 99 //int audiodb_l2norm(adb_t *mydb);
map01bf@620 100 PyObject * _pyadb_l2norm(PyObject *self, PyObject *args)
map01bf@620 101 {
mas01cr@671 102 adb_t *current_db;
map01bf@620 103 int ok;
map01bf@620 104 PyObject * incoming = 0;
map01bf@620 105
map01bf@620 106 ok = PyArg_ParseTuple(args, "O", &incoming);
map01bf@620 107 if (!ok) return 0;
mas01cr@671 108 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@620 109
map01bf@620 110 ok = audiodb_l2norm(current_db);
map01bf@622 111 return PyBool_FromLong(ok-1);
map01bf@620 112
map01bf@620 113 }
map01bf@620 114
map01bf@620 115 /*engage power thresholding in the referenced db*/
map01bf@620 116 /*api call:*/
mas01cr@671 117 // int audiodb_power(adb_t *mydb);
map01bf@620 118 PyObject * _pyadb_power(PyObject *self, PyObject *args)
map01bf@620 119 {
mas01cr@671 120 adb_t *current_db;
map01bf@620 121 int ok;
map01bf@620 122 PyObject * incoming = 0;
map01bf@620 123
map01bf@620 124 ok = PyArg_ParseTuple(args, "O", &incoming);
map01bf@620 125 if (!ok) return 0;
mas01cr@671 126 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@620 127
map01bf@620 128 ok = audiodb_power(current_db);
map01bf@622 129 return PyBool_FromLong(ok-1);
map01bf@620 130
map01bf@620 131 }
map01bf@716 132
map01bf@716 133 /* insert feature data from a numpy array */
map01bf@720 134 /* array given should have ndarray.shape = (numVectors, numDims)*/
map01bf@716 135 /* array datatype needs to be doubles (float may work...)*/
map01bf@716 136 /* if power is given, must be 1d array of length numVectors*/
map01bf@716 137 /* if times is given, must be 1d array of length 2*numVectors like this:*/
map01bf@716 138
map01bf@716 139 /* api call: */
map01bf@716 140 // typedef struct adb_datum {
map01bf@716 141 // uint32_t nvectors;
map01bf@716 142 // uint32_t dim;
map01bf@716 143 // const char *key;
map01bf@716 144 // double *data;
map01bf@716 145 // double *power;
map01bf@716 146 // double *times;
map01bf@716 147 // } adb_datum_t;
map01bf@716 148 // int audiodb_insert_datum(adb_t *, const adb_datum_t *);
map01bf@716 149 PyObject * _pyadb_insertFromArray(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@716 150 {
map01bf@716 151 adb_t *current_db;
map01bf@716 152 adb_status_t *status;
map01bf@716 153 adb_datum_t *ins;
map01bf@716 154 int ok;
map01bf@716 155 npy_intp dims[1];
map01bf@716 156 unsigned int nDims = 0;
map01bf@716 157 unsigned int nVect = 0;
map01bf@716 158 PyObject *incoming = 0;
map01bf@718 159 PyArrayObject *features = 0;
map01bf@718 160 PyArrayObject *power = 0;
map01bf@718 161 PyArrayObject *times = 0;
map01bf@716 162 const char *key = NULL;
map01bf@716 163 PyArray_Descr *descr;
map01bf@716 164 static char *kwlist[] = { "db", "features", "nDim", "nVect", "power", "key", "times" , NULL};
map01bf@716 165
map01bf@718 166 ok = PyArg_ParseTupleAndKeywords(args, keywds, "OO!II|O!sO!", kwlist, &incoming, &PyArray_Type, &features, &nDims, &nVect, &PyArray_Type, &power, &key, &PyArray_Type, &times);
mas01mc@743 167 if (!ok){
mas01mc@743 168 PyErr_SetString(PyExc_TypeError, "Failed at PyArg_ParseTupleAndKeywords");
mas01mc@743 169 return NULL;
mas01mc@743 170 }
map01bf@716 171 //check our arrays
map01bf@718 172 // if (!PyArray_Check(features)){
map01bf@718 173 // PyErr_SetString(PyExc_TypeError, "features must be a numpy array (of floats or doubles)");
map01bf@718 174 // return NULL;
map01bf@718 175 // }
map01bf@716 176 if (!PyArray_ISFLOAT(features)){
map01bf@716 177 PyErr_SetString(PyExc_TypeError, "features numpy array must contain floats or doubles");
map01bf@716 178 return NULL;
map01bf@716 179 }
map01bf@716 180 if ((PyArray_NDIM(features) != 1) || (PyArray_DIMS(features)[0] != (nDims * nVect))){
map01bf@716 181 PyErr_SetString(PyExc_TypeError, "features numpy array must be flattened before call.");
map01bf@716 182 return NULL;
map01bf@716 183 }
map01bf@716 184 descr = PyArray_DescrFromType(NPY_DOUBLE);
map01bf@716 185
map01bf@716 186 if (power){
map01bf@716 187 if (!PyArray_Check(power)){
map01bf@716 188 PyErr_SetString(PyExc_TypeError, "power, if given, must be a numpy array (of floats or doubles)");
map01bf@716 189 return NULL;
map01bf@716 190 }
map01bf@716 191 if (!PyArray_ISFLOAT(power)){
map01bf@716 192 PyErr_SetString(PyExc_TypeError, "power numpy array, if given, must contain floats or doubles");
map01bf@716 193 return NULL;
map01bf@716 194 }
map01bf@716 195 // power = (PyArrayObject *)PyCObject_AsVoidPtr(incomingPow);
mas01mc@743 196 if (PyArray_NDIM(features) != 1 || PyArray_DIMS(power)[0] != nVect){
map01bf@716 197 PyErr_SetString(PyExc_ValueError, "power, if given must be a 1d numpy array with shape = (numVectors,)");
map01bf@716 198 return NULL;
map01bf@716 199 }
map01bf@716 200 }
map01bf@716 201 if (times){
map01bf@716 202 if (!PyArray_Check(times)){
map01bf@716 203 PyErr_SetString(PyExc_TypeError, "times, if given, must be a numpy array (of floats or doubles)");
map01bf@716 204 return NULL;
map01bf@716 205 }
map01bf@716 206 if (!PyArray_ISFLOAT(times)){
map01bf@716 207 PyErr_SetString(PyExc_TypeError, "times numpy array, if given, must contain floats or doubles");
map01bf@716 208 return NULL;
map01bf@716 209 }
map01bf@716 210 // times = (PyArrayObject *)PyCObject_AsVoidPtr(incomingTime);
mas01mc@749 211 if (PyArray_NDIM(times) != 1 || PyArray_DIMS(times)[0] != (nVect*2)){
mas01mc@749 212 PyErr_SetString(PyExc_ValueError, "times, if given must be a 1d numpy array with shape = (numVectors*2,)");
map01bf@716 213 return NULL;
map01bf@716 214 }
map01bf@716 215 }
map01bf@716 216 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@716 217 status = (adb_status_t *)malloc(sizeof(adb_status_t));
map01bf@716 218 //verify that the data to be inserted is the correct size for the database.
map01bf@716 219
map01bf@716 220 ins = (adb_datum_t *)malloc(sizeof(adb_datum_t));
map01bf@719 221 if (PyArray_AsCArray(&features, &(ins->data), dims, 1, descr)){
map01bf@716 222 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the feature np array as a C array.");
map01bf@716 223 return NULL;
map01bf@716 224 }
map01bf@716 225
map01bf@718 226 if (power){
map01bf@719 227 if (PyArray_AsCArray(&power, &(ins->power), dims, 1, descr)){
map01bf@716 228 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the power np array as a C array.");
map01bf@716 229 return NULL;
map01bf@716 230 }
map01bf@719 231 }else{
map01bf@719 232 ins->power=NULL;
map01bf@716 233 }
map01bf@716 234
map01bf@718 235 if (times){
map01bf@719 236 if (PyArray_AsCArray(&times, &(ins->times), dims, 1, descr)){
map01bf@716 237 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the times np array as a C array.");
map01bf@716 238 return NULL;
map01bf@716 239 }
map01bf@719 240 }else{
map01bf@719 241 ins->times=NULL;
map01bf@716 242 }
map01bf@716 243 ins->key = key;
map01bf@716 244 ins->nvectors = (uint32_t)nVect;
map01bf@716 245 ins->dim = (uint32_t)nDims;
map01bf@716 246 //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times);
map01bf@716 247 ok = audiodb_insert_datum(current_db, ins);//(current_db, ins);
map01bf@719 248 return PyInt_FromLong(ok);
map01bf@716 249
map01bf@716 250 }
map01bf@716 251
map01bf@621 252 /* insert feature data stored in a file */
map01bf@621 253 /* this is a bit gross, */
map01bf@621 254 /* should be replaced eventually by a numpy based feature.*/
map01bf@621 255 /* api call: */
mas01cr@673 256 // struct adb_insert {
map01bf@621 257 // const char *features;
map01bf@621 258 // const char *power;
map01bf@621 259 // const char *key;
map01bf@621 260 // const char *times;
map01bf@621 261 // };
mas01cr@671 262 // int audiodb_insert(adb_t *mydb, adb_insert_t *ins);
map01bf@621 263 PyObject * _pyadb_insertFromFile(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@621 264 {
mas01cr@671 265 adb_t *current_db;
mas01cr@671 266 adb_insert_t *ins;
map01bf@621 267 int ok;
map01bf@621 268 const char *features;
map01bf@621 269 const char *power = NULL;
map01bf@621 270 const char *key = NULL;
map01bf@621 271 const char *times = NULL;
map01bf@621 272 PyObject * incoming = 0;
map01bf@621 273 static char *kwlist[] = { "db", "features", "power", "key", "times" , NULL};
map01bf@621 274
map01bf@621 275 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|sss", kwlist, &incoming, &features, &power, &key, &times);
map01bf@621 276 if (!ok){return NULL;}
map01bf@621 277
mas01cr@671 278 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01cr@671 279 ins = (adb_insert_t *)malloc(sizeof(adb_insert_t));
map01bf@621 280 ins->features = features;
map01bf@621 281 ins->power = power;
map01bf@621 282 ins->key = key;
map01bf@621 283 ins->times = times;
map01bf@622 284 //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times);
map01bf@621 285 ok = audiodb_insert(current_db, ins);
map01bf@622 286 return PyBool_FromLong(ok-1);
map01bf@621 287
map01bf@621 288 }
map01bf@621 289
mas01mc@744 290 /* liszt - list strings, sizes, and time-points of all database entries
mas01mc@744 291 *
mas01mc@744 292 */
mas01mc@744 293 PyObject* _pyadb_liszt(PyObject *self, PyObject *args)
mas01mc@744 294 {
mas01mc@744 295 adb_t *current_db;
mas01mc@744 296 int ok,i;
mas01mc@744 297 PyObject * incoming = NULL;
mas01mc@744 298 PyObject * outgoing = NULL;
mas01mc@744 299 PyObject * newBits = NULL;
mas01mc@744 300
mas01mc@744 301 ok = PyArg_ParseTuple(args, "O", &incoming);
mas01mc@744 302
mas01mc@744 303 if (!ok) return 0;
mas01mc@744 304 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01mc@744 305
mas01mc@744 306 adb_liszt_results_t *liszt = audiodb_liszt(current_db);
mas01mc@744 307
mas01mc@744 308 outgoing = PyList_New((Py_ssize_t)0);
mas01mc@744 309 for (i=0 ; i<liszt->nresults ; i++){
mas01mc@744 310 newBits = Py_BuildValue("sI",liszt->entries[i].key,liszt->entries[i].nvectors);
mas01mc@744 311 if (PyList_Append(outgoing, newBits)){
mas01mc@744 312 //error msg here
mas01mc@744 313 Py_XDECREF(newBits);
mas01mc@744 314 return NULL;
mas01mc@744 315 }
mas01mc@744 316 Py_DECREF(newBits);
mas01mc@744 317 }
mas01mc@744 318 audiodb_liszt_free_results(current_db, liszt);
mas01mc@744 319 return outgoing;
mas01mc@744 320 }
mas01mc@744 321
map01bf@622 322 /* base query. The nomenclature here is about a far away as pythonic as is possible.
map01bf@622 323 * This should be taken care of via the higher level python structure
map01bf@622 324 * returns a dict that should be result ordered and key = result key
map01bf@622 325 * and value is a list of tuples one per result associated with that key, of the form:
map01bf@622 326 * (dist, qpos, ipos)
map01bf@622 327 * Note as well that this is by no means the most efficient way to cast from C, simply the most direct
map01bf@622 328 * and what it lacks in effeciency it gains in python side access. It remains to be seen if this is
map01bf@622 329 * a sensible trade.
map01bf@622 330 * api call:
map01bf@622 331 * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *);
map01bf@622 332 ***/
map01bf@622 333 PyObject * _pyadb_queryFromKey(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@622 334 {
mas01cr@671 335 adb_t *current_db;
map01bf@622 336 adb_query_spec_t *spec;
map01bf@622 337 adb_query_results_t *result;
map01bf@622 338 int ok, exhaustive, falsePositives;
map01bf@622 339 uint32_t i;
map01bf@622 340 const char *key;
map01bf@622 341 const char *accuMode = "db";
map01bf@622 342 const char *distMode = "dot";
map01bf@624 343 const char *resFmt = "dict";
map01bf@622 344 uint32_t hop = 0;
map01bf@622 345 double radius = 0;
map01bf@622 346 double absThres = 0;
map01bf@622 347 double relThres = 0;
map01bf@622 348 double durRatio = 0;
map01bf@622 349 PyObject *includeKeys = NULL;
map01bf@622 350 PyObject *excludeKeys = NULL;
map01bf@622 351 PyObject *incoming = 0;
map01bf@622 352 PyObject *outgoing = NULL;
map01bf@622 353 PyObject *thisKey = NULL;
map01bf@622 354 PyObject *currentValue = 0;
map01bf@622 355 PyObject *newBits = 0;
map01bf@622 356 static char *kwlist[] = { "db", "key",
map01bf@622 357 "seqLength",
map01bf@622 358 "seqStart",
map01bf@622 359 "exhaustive",
map01bf@622 360 "falsePositives",
map01bf@622 361 "accumulation",
map01bf@622 362 "distance",
map01bf@622 363 "npoints",//nearest neighbor points per track
map01bf@624 364 "ntracks",
map01bf@622 365 "includeKeys",
map01bf@622 366 "excludeKeys",
map01bf@622 367 "radius",
map01bf@622 368 "absThres",
map01bf@622 369 "relThres",
map01bf@622 370 "durRatio",
map01bf@624 371 "hopSize",
map01bf@718 372 "resFmt",
map01bf@718 373 NULL
map01bf@622 374 };
map01bf@622 375 spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
map01bf@622 376 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
map01bf@622 377 result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
map01bf@622 378
map01bf@622 379 spec->qid.sequence_length = 16;
map01bf@622 380 spec->qid.sequence_start = 0;
map01bf@622 381 spec->qid.flags = 0;
map01bf@622 382 spec->params.npoints = 1;
map01bf@622 383 spec->params.ntracks = 100;//number of results returned in db mode
map01bf@622 384 spec->refine.flags = 0;
map01bf@622 385
map01bf@624 386 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|iiiissIIOOddddIs", kwlist,
map01bf@622 387 &incoming, &key,
map01bf@622 388 &spec->qid.sequence_length,
map01bf@622 389 &spec->qid.sequence_start,
map01bf@622 390 &exhaustive, &falsePositives,
map01bf@622 391 &accuMode,&distMode,
map01bf@622 392 &spec->params.npoints,
map01bf@622 393 &spec->params.ntracks,
map01bf@622 394 &includeKeys, &excludeKeys,
map01bf@624 395 &radius, &absThres, &relThres, &durRatio, &hop,
map01bf@624 396 &resFmt
map01bf@622 397 );
map01bf@622 398
map01bf@622 399 if (!ok) {return NULL;}
mas01cr@671 400 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@622 401
map01bf@622 402 if (exhaustive){
map01bf@622 403 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
map01bf@622 404 }
map01bf@622 405 if (falsePositives){
map01bf@622 406 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES;
map01bf@622 407 }
map01bf@622 408
map01bf@622 409 //set up spec->params
map01bf@622 410 if (strcmp(accuMode,"db")){
map01bf@622 411 spec->params.accumulation = ADB_ACCUMULATION_DB;
map01bf@622 412 } else if (strcmp(accuMode,"track")){
map01bf@622 413 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
map01bf@622 414 } else if (strcmp(accuMode,"one2one")){
map01bf@622 415 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE;
map01bf@622 416 } else{
map01bf@624 417 PyErr_SetString(PyExc_ValueError,
map01bf@624 418 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n");
map01bf@622 419 return NULL;
map01bf@622 420 }
map01bf@622 421 if (strcmp(distMode, "dot")){
map01bf@622 422 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
map01bf@622 423 }else if (strcmp(distMode, "eucNorm")){
map01bf@622 424 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
map01bf@622 425 }else if (strcmp(distMode, "euclidean")){
map01bf@622 426 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
map01bf@622 427 }else{
map01bf@624 428 PyErr_SetString(PyExc_ValueError,
map01bf@624 429 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' or \'euclidean\'.\n");
map01bf@622 430 return NULL;
map01bf@622 431 }
map01bf@622 432
map01bf@622 433 //set up spec->refine
map01bf@622 434 //include/exclude keys
map01bf@622 435 if (includeKeys){
map01bf@622 436 if (!PyList_Check(includeKeys)){
map01bf@624 437 PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n");
map01bf@622 438 return NULL;
map01bf@622 439 }
map01bf@622 440 spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST;
map01bf@622 441 spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys);
map01bf@622 442 spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys);
map01bf@622 443 for (i=0;i<spec->refine.include.nkeys;i++){
map01bf@622 444 if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){
map01bf@622 445 spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i));
map01bf@622 446 }else{
map01bf@624 447 PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 448 return NULL;
map01bf@622 449 }
map01bf@622 450 }
map01bf@622 451 }
map01bf@622 452 if (excludeKeys){
map01bf@622 453 if (!PyList_Check(excludeKeys)){
map01bf@624 454 PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n");
map01bf@622 455 return NULL;
map01bf@622 456 }
map01bf@622 457 spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST;
map01bf@622 458 spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys);
map01bf@622 459 spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys);
map01bf@622 460 for (i=0;i<spec->refine.exclude.nkeys;i++){
map01bf@622 461 if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){
map01bf@622 462 spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i));
map01bf@622 463 }else{
map01bf@624 464 PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 465 return NULL;
map01bf@622 466 }
map01bf@622 467 }
map01bf@622 468 }
map01bf@622 469 //the rest of spec->refine
map01bf@622 470 if (radius){
map01bf@622 471 spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS;
map01bf@622 472 spec->refine.radius = radius;
map01bf@622 473 }
map01bf@622 474 if (absThres){
map01bf@622 475 spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD;
map01bf@622 476 spec->refine.absolute_threshold = absThres;
map01bf@622 477 }
map01bf@622 478 if (relThres){
map01bf@622 479 spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD;
map01bf@622 480 spec->refine.relative_threshold = relThres;
map01bf@622 481 }
map01bf@622 482 if (durRatio){
map01bf@622 483 spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO;
map01bf@622 484 spec->refine.duration_ratio = durRatio;
map01bf@622 485 }
map01bf@622 486 if (hop){
map01bf@622 487 spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE;
mas01cr@679 488 /* not ideal but a temporary bandage fix */
mas01cr@679 489 spec->refine.qhopsize = hop;
mas01cr@679 490 spec->refine.ihopsize = hop;
map01bf@622 491 }
map01bf@622 492 //setup the datum
map01bf@622 493 spec->qid.datum->data = NULL;
map01bf@622 494 spec->qid.datum->power = NULL;
map01bf@622 495 spec->qid.datum->times = NULL;
map01bf@622 496 //grab the datum from the key
map01bf@622 497 ok = audiodb_retrieve_datum(current_db, key, spec->qid.datum);
map01bf@622 498 if (ok != 0){
map01bf@622 499 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n");
map01bf@622 500 return NULL;
map01bf@622 501 }
map01bf@622 502 result = audiodb_query_spec(current_db, spec);
map01bf@622 503 if (result == NULL){
map01bf@718 504 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
map01bf@622 505 return NULL;
map01bf@622 506 }
map01bf@624 507 if(strcmp(resFmt, "dict")==0){
map01bf@624 508 outgoing = PyDict_New();
map01bf@624 509 for (i=0;i<result->nresults;i++){
mas01cr@672 510 thisKey = PyString_FromString(result->results[i].ikey);
map01bf@624 511 if (!PyDict_Contains(outgoing, thisKey)){
map01bf@624 512 newBits = Py_BuildValue("[(dII)]",
map01bf@624 513 result->results[i].dist,
map01bf@624 514 result->results[i].qpos,
map01bf@624 515 result->results[i].ipos);
map01bf@624 516 if (PyDict_SetItem(outgoing, thisKey,newBits)){
mas01cr@672 517 printf("key : %s\ndist : %f\nqpos : %i\nipos : %i\n", result->results[i].ikey, result->results[i].dist, result->results[i].qpos, result->results[i].ipos);
map01bf@624 518 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 519 Py_XDECREF(newBits);
map01bf@624 520 return NULL;
map01bf@624 521 }
map01bf@624 522 Py_DECREF(newBits);
map01bf@624 523 }else {
map01bf@624 524 //the key already has a value, so we need to fetch the value, confirm it's a list and append another tuple to it.
map01bf@624 525 currentValue = PyDict_GetItem(outgoing, thisKey);
map01bf@624 526 if (!PyList_Check(currentValue)){
map01bf@624 527 PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n");
map01bf@624 528 return NULL;
map01bf@624 529 }
map01bf@624 530 newBits = Py_BuildValue("dII",result->results[i].dist,
map01bf@624 531 result->results[i].qpos,
map01bf@624 532 result->results[i].ipos);
map01bf@624 533 if (PyList_Append(currentValue, newBits)){
map01bf@624 534 //error msg here
map01bf@624 535 Py_XDECREF(newBits);
map01bf@624 536 return NULL;
map01bf@624 537 }
map01bf@624 538 if (PyDict_SetItem(outgoing, thisKey, newBits)){
map01bf@624 539 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 540 Py_XDECREF(newBits);
map01bf@624 541 return NULL;
map01bf@624 542 }
map01bf@624 543 Py_DECREF(newBits);
map01bf@624 544
map01bf@624 545 }
map01bf@624 546 }
map01bf@624 547 }else if(strcmp(resFmt, "list")==0){
map01bf@624 548 outgoing = PyList_New((Py_ssize_t)0);
map01bf@624 549 for (i=0;i<result->nresults;i++){
mas01cr@672 550 newBits = Py_BuildValue("sdII",result->results[i].ikey,
map01bf@622 551 result->results[i].dist,
map01bf@622 552 result->results[i].qpos,
map01bf@622 553 result->results[i].ipos);
map01bf@624 554 if (PyList_Append(outgoing, newBits)){
map01bf@624 555 //error msg here
map01bf@622 556 Py_XDECREF(newBits);
map01bf@622 557 return NULL;
map01bf@622 558 }
map01bf@622 559 Py_DECREF(newBits);
map01bf@624 560 }
map01bf@624 561 if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward.
map01bf@720 562 PyErr_SetString(PyExc_RuntimeError,
map01bf@720 563 "the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n");
map01bf@624 564 return NULL;
map01bf@622 565 }
map01bf@624 566 }else{
map01bf@624 567 PyErr_SetString(PyExc_ValueError,
map01bf@624 568 "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n");
map01bf@624 569 return NULL;
map01bf@622 570 }
map01bf@628 571 if (audiodb_query_free_results(current_db, spec, result)){
map01bf@622 572 printf("bit of trouble freeing the result and spec...\ncheck for leaks.");
map01bf@622 573 }
map01bf@622 574
map01bf@622 575 return outgoing;
map01bf@622 576
map01bf@622 577
map01bf@622 578
map01bf@622 579 }
map01bf@622 580
map01bf@622 581
mas01mc@746 582 /* retrieval of inserted data
mas01mc@746 583 * returned numpy array has ndarray.shape = (numVectors, numDims)
mas01mc@746 584 * array datatype needs to be doubles (float may work...)
mas01mc@746 585 * if power reqeusted, it will be a 1d array of length numVectors
mas01mc@746 586 * if times are requested, they will be a 1d array of length 2*nvectors
mas01mc@746 587 */
mas01mc@746 588
mas01mc@746 589 // api call:
mas01mc@746 590 // typedef struct adb_datum {
mas01mc@746 591 // uint32_t nvectors;
mas01mc@746 592 // uint32_t dim;
mas01mc@746 593 // const char *key;
mas01mc@746 594 // double *data;
mas01mc@746 595 // double *power;
mas01mc@746 596 // double *times;
mas01mc@746 597 // } adb_datum_t;
mas01mc@746 598
mas01mc@746 599 //int audiodb_retrieve_datum(adb_t *, const char *, adb_datum_t *);
mas01mc@746 600 //int audiodb_free_datum(adb_t *, adb_datum_t *);
mas01mc@746 601 PyObject * _pyadb_retrieveDatum(PyObject *self, PyObject *args, PyObject *keywds)
mas01mc@746 602 {
mas01mc@746 603 adb_t *current_db = NULL;
mas01mc@746 604 adb_status_t *status = NULL;
mas01mc@746 605 adb_datum_t *ins = NULL;
mas01mc@746 606 int ok=0, errtest=0;
mas01mc@746 607 unsigned features=0, powers=0, times=0;
mas01mc@746 608 PyObject *incoming = 0; // The ADB database
mas01mc@746 609 PyObject *outgoing = 0; // The PyArrayObject
mas01mc@746 610 const char *key = NULL;
mas01mc@746 611 static char *kwlist[] = { "db", "key", "features", "powers", "times", NULL};
mas01mc@746 612 double * data;
mas01mc@746 613 int dims = 0;
mas01mc@746 614 npy_intp shape[2] = { 0, 0 };
mas01mc@746 615
mas01mc@746 616 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|III", kwlist, &incoming, &key, &features, &powers, &times);
mas01mc@746 617 if (!ok){
mas01mc@746 618 PyErr_SetString(PyExc_TypeError, "Failed at PyArg_ParseTupleAndKeywords");
mas01mc@746 619 return NULL;
mas01mc@746 620 }
mas01mc@746 621
mas01mc@746 622 if(features+powers+times>1){
mas01mc@746 623 PyErr_SetString(PyExc_TypeError, "Failed: you must specify only one of features, powers, or times");
mas01mc@746 624 return NULL;
mas01mc@746 625 }
mas01mc@746 626
mas01mc@746 627 if(!(features||powers||times)){
mas01mc@746 628 features=1; // default is to return features
mas01mc@746 629 }
mas01mc@746 630
mas01mc@746 631 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01mc@746 632 if (!current_db){
mas01mc@746 633 PyErr_SetString(PyExc_TypeError, "Failed to convert open database to C-pointer");
mas01mc@746 634 return NULL;
mas01mc@746 635 }
mas01mc@746 636 status = (adb_status_t*) malloc(sizeof(adb_status_t));
mas01mc@746 637 errtest = audiodb_status(current_db, status);
mas01mc@746 638 if(errtest){
mas01mc@746 639 PyErr_SetString(PyExc_TypeError, "Failed: could not get status of passed ADB database");
mas01mc@746 640 free(status);
mas01mc@746 641 return NULL;
mas01mc@746 642 }
mas01mc@746 643
mas01mc@746 644 if(powers && !(status->flags&ADB_HEADER_FLAG_POWER)){
mas01mc@746 645 PyErr_SetString(PyExc_TypeError, "Failed: powers requested but passed ADB database has no powers");
mas01mc@746 646 free(status);
mas01mc@746 647 return NULL;
mas01mc@746 648 }
mas01mc@746 649
mas01mc@746 650 if(times && !(status->flags&ADB_HEADER_FLAG_TIMES)){
mas01mc@746 651 PyErr_SetString(PyExc_TypeError, "Failed: times requested but passed ADB database has no times");
mas01mc@746 652 free(status);
mas01mc@746 653 return NULL;
mas01mc@746 654 }
mas01mc@746 655
mas01mc@746 656 ins = (adb_datum_t *)malloc(sizeof(adb_datum_t));
mas01mc@746 657 errtest = audiodb_retrieve_datum(current_db, key, ins); // retrieve data from adb via key
mas01mc@746 658 if (errtest){
mas01mc@746 659 PyErr_SetString(PyExc_TypeError, "Failed to retrieve datum");
mas01mc@746 660 free(ins);
mas01mc@746 661 return NULL;
mas01mc@746 662 }
mas01mc@746 663
mas01mc@746 664 if(features){
mas01mc@746 665 if(ins->dim>1){
mas01mc@746 666 dims=2;
mas01mc@746 667 shape[1]= ins->dim;
mas01mc@746 668 }
mas01mc@746 669 else{
mas01mc@746 670 dims=1;
mas01mc@746 671 }
mas01mc@746 672 shape[0]= ins->nvectors;
mas01mc@746 673 data = ins->data;
mas01mc@746 674 }
mas01mc@746 675 else if(powers){
mas01mc@746 676 dims=1;
mas01mc@746 677 shape[0]= ins->nvectors;
mas01mc@746 678 data = ins->power;
mas01mc@746 679 }
mas01mc@746 680 else if(times){
mas01mc@746 681 dims=1;
mas01mc@746 682 shape[0]= 2 * ins->nvectors;
mas01mc@746 683 data = ins->times;
mas01mc@746 684 }
mas01mc@746 685
mas01mc@748 686 outgoing = PyArray_SimpleNew(dims, shape, NPY_DOUBLE);
mas01mc@746 687 if (!outgoing){
mas01mc@748 688 free(status);
mas01mc@748 689 free(ins); // free the malloced adb_datum_t structure though
mas01mc@748 690 Py_XDECREF(outgoing);
mas01mc@746 691 PyErr_SetString(PyExc_TypeError, "Failed to convert retrieved datum to C-Array");
mas01mc@746 692 return NULL;
mas01mc@748 693 }
mas01mc@747 694
mas01mc@748 695 /* Copy the data, this allows us to free the allocated memory and let
mas01mc@748 696 * python do the subsequent garbage collection itself.
mas01mc@748 697 */
mas01mc@748 698 int num_items = ins->nvectors * ins->dim;
mas01mc@748 699 double* p = (double*) PyArray_DATA(outgoing);
mas01mc@748 700 double* d = data;
mas01mc@748 701 while(num_items--)
mas01mc@748 702 *p++ = *d++;
mas01mc@748 703 audiodb_free_datum(current_db, ins); // free the source audiodb_datum
mas01mc@748 704 free(status); // free the malloced status object
mas01mc@748 705 free(ins); // free the malloced adb_datum_t structure though
mas01mc@746 706 return outgoing;
mas01mc@746 707 }
map01bf@622 708
map01bf@622 709
map01bf@620 710 /* close a database */
map01bf@620 711 /* api call: */
mas01cr@671 712 // void audiodb_close(adb_t *db);
map01bf@620 713 static void _pyadb_close(void *ptr)
map01bf@620 714 {
mas01cr@671 715 adb_t *stale_database;
mas01cr@671 716 stale_database = (adb_t *)ptr;
map01bf@620 717
map01bf@620 718 audiodb_close(stale_database);
map01bf@620 719 }
map01bf@620 720
map01bf@620 721 static PyMethodDef _pyadbMethods[] =
map01bf@620 722 {
map01bf@620 723 { "_pyadb_create", _pyadb_create, METH_VARARGS,
map01bf@620 724 "_pyadb_create(string path, unsigned datasize, unsigned ntracks, unsigned datadim)->adb object"},
map01bf@620 725 { "_pyadb_open", _pyadb_open, METH_VARARGS,
map01bf@620 726 "_pyadb_open(string path, [\'r\'|\'w\'])->adb object\nNote that specifing \'w\' opens the file in read and write mode. \
map01bf@620 727 There is currently no way to open in write only."},
map01bf@620 728 { "_pyadb_status", _pyadb_status, METH_VARARGS,
mas01cr@671 729 "_status(adb_t *)->(numFiles, dims, dudCount, nullCount, flags, length, data_region_size)"},
map01bf@620 730 { "_pyadb_l2norm", _pyadb_l2norm, METH_VARARGS,
mas01cr@671 731 "_pyadb_l2norm(adb_t *)->int return code (0 for sucess)"},
map01bf@620 732 { "_pyadb_power", _pyadb_power, METH_VARARGS,
mas01cr@671 733 "_pyadb_power(adb_t *)->int return code (0 for sucess)"},
map01bf@716 734 {"_pyadb_insertFromArray", (PyCFunction)_pyadb_insertFromArray, METH_VARARGS | METH_KEYWORDS,
map01bf@718 735 "_pyadb_insertFromArray(adb_t *, features=ndarray, [power=ndarray | key=keystring | times=ndarray])->\
map01bf@718 736 int return code (0 for sucess)\n\
map01bf@718 737 insert feature data from a numpy array\n\
map01bf@716 738 array given should have ndarray.shape = (numDims*numVectors,)\n\
map01bf@716 739 array datatype needs to be doubles (float may work...)\n\
map01bf@716 740 if power is given, must be 1d array of length numVectors\n\
map01bf@716 741 if times is given, must be 1d array of length 2*numVectors like this:\n\
map01bf@716 742 int audiodb_insert_datum(adb_t *, const adb_datum_t *);"},
mas01mc@746 743 {"_pyadb_retrieveDatum", (PyCFunction)_pyadb_retrieveDatum, METH_VARARGS | METH_KEYWORDS, "_pyadb_retrieveDatum(adb_t *, key=keystring"},
map01bf@622 744 { "_pyadb_insertFromFile", (PyCFunction)_pyadb_insertFromFile, METH_VARARGS | METH_KEYWORDS,
mas01cr@671 745 "_pyadb_insertFromFile(adb_t *, features=featureFile, [power=powerfile | key=keystring | times=timingFile])->\
map01bf@621 746 int return code (0 for sucess)"},
mas01mc@744 747 { "_pyadb_liszt", (PyCFunction)_pyadb_liszt, METH_VARARGS,
mas01mc@744 748 "_pyadb_liszt(adb_t*)->[[key1,numvecs1],[key2,numvecs2]...]"},
map01bf@622 749 { "_pyadb_queryFromKey", (PyCFunction)_pyadb_queryFromKey, METH_VARARGS | METH_KEYWORDS,
map01bf@622 750 "base query. The nomenclature here is about a far away as pythonic as is possible.\n\
map01bf@622 751 This should be taken care of via the higher level python structure\n\
map01bf@622 752 returns a dict that should be result ordered and key = result key\n\
map01bf@622 753 and value is a list of tuples one per result associated with that key, of the form:\n\
map01bf@622 754 \t(dist, qpos, ipos)\n\
map01bf@622 755 Note as well that this is by no means the most efficient way to cast from C, simply the most direct\n\
map01bf@622 756 and what it lacks in effeciency it gains in python side access. It remains to be seen if this is\n\
map01bf@622 757 a sensible trade.\n\
mas01cr@671 758 _pyadb_queryFromKey(adb_t *, query key,\n\
map01bf@622 759 [seqLength = Int Sequence Length, \n\
map01bf@622 760 seqStart = Int offset from start for key, \n\
map01bf@622 761 exhaustive = boolean - True for exhaustive (false by default),\n\
map01bf@622 762 falsePositives= boolean - True to keep fps (false by defaults),\n\
map01bf@622 763 accumulation = [\"db\"|\"track\"|\"one2one\"] (\"db\" by default),\n\
map01bf@622 764 distance = [\"dot\"|\"eucNorm\"|\"euclidean\"] (\"dot\" by default),\n\
map01bf@622 765 npoints = int number of points per track,\n\
map01bf@622 766 ntracks = max number of results returned in db accu mode,\n\
map01bf@622 767 includeKeys = list of strings to include (use all by default),\n\
map01bf@622 768 excludeKeys = list of strings to exclude (none by default),\n\
map01bf@622 769 radius = double of nnRadius (1.0 default, overrides npoints if specified),\n\
map01bf@622 770 absThres = double absolute power threshold (db must have power),\n\
map01bf@622 771 relThres = double relative power threshold (db must have power),\n\
map01bf@622 772 durRatio = double time expansion/compresion ratio,\n\
map01bf@628 773 hopSize = int hopsize (1 by default)])->resultDict\n\
map01bf@628 774 resFmt = [\"list\"|\"dict\"](\"dict\" by default)"},
map01bf@620 775 {NULL,NULL, 0, NULL}
map01bf@620 776 };
map01bf@620 777
map01bf@620 778 void init_pyadb()
map01bf@620 779 {
map01bf@718 780 Py_InitModule3("_pyadb", _pyadbMethods, "internal c bindings for audioDB. Use pyadb for pythonic access to adb.");
map01bf@718 781 import_array();
map01bf@620 782 return;
map01bf@620 783 }
map01bf@620 784
map01bf@620 785