annotate bindings/python/pyadbmodule.c @ 770:c54bc2ffbf92 tip

update tags
author convert-repo
date Fri, 16 Dec 2011 11:34:01 +0000
parents b9dbe4611dde
children
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));
mas01mc@758 221 if (PyArray_AsCArray((PyObject**)&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){
mas01mc@758 227 if (PyArray_AsCArray((PyObject**)&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){
mas01mc@758 236 if (PyArray_AsCArray((PyObject**)&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
mas01mc@750 410 if (strcmp(accuMode,"db")==0){
map01bf@622 411 spec->params.accumulation = ADB_ACCUMULATION_DB;
mas01mc@750 412 } else if (strcmp(accuMode,"track")==0){
map01bf@622 413 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
mas01mc@750 414 } else if (strcmp(accuMode,"one2one")==0){
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 }
mas01mc@750 421 if (strcmp(distMode, "dot")==0){
mas01mc@768 422 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
mas01mc@750 423 }else if (strcmp(distMode, "eucNorm")==0){
mas01mc@768 424 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
mas01mc@750 425 }else if (strcmp(distMode, "euclidean")==0){
mas01mc@768 426 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
mas01mc@768 427 }else if (strcmp(distMode, "kullback")==0){
mas01mc@768 428 spec->params.distance = ADB_DISTANCE_KULLBACK_LEIBLER_DIVERGENCE;
map01bf@622 429 }else{
mas01mc@768 430 PyErr_SetString(PyExc_ValueError,
mas01mc@768 431 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' ,\'euclidean\' or \'kullback\'.\n");
mas01mc@768 432 return NULL;
map01bf@622 433 }
map01bf@622 434
map01bf@622 435 //set up spec->refine
map01bf@622 436 //include/exclude keys
map01bf@622 437 if (includeKeys){
map01bf@622 438 if (!PyList_Check(includeKeys)){
map01bf@624 439 PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n");
map01bf@622 440 return NULL;
map01bf@622 441 }
map01bf@622 442 spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST;
map01bf@622 443 spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys);
map01bf@622 444 spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys);
map01bf@622 445 for (i=0;i<spec->refine.include.nkeys;i++){
map01bf@622 446 if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){
map01bf@622 447 spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i));
map01bf@622 448 }else{
map01bf@624 449 PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 450 return NULL;
map01bf@622 451 }
map01bf@622 452 }
map01bf@622 453 }
map01bf@622 454 if (excludeKeys){
map01bf@622 455 if (!PyList_Check(excludeKeys)){
map01bf@624 456 PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n");
map01bf@622 457 return NULL;
map01bf@622 458 }
map01bf@622 459 spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST;
map01bf@622 460 spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys);
map01bf@622 461 spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys);
map01bf@622 462 for (i=0;i<spec->refine.exclude.nkeys;i++){
map01bf@622 463 if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){
map01bf@622 464 spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i));
map01bf@622 465 }else{
map01bf@624 466 PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 467 return NULL;
map01bf@622 468 }
map01bf@622 469 }
map01bf@622 470 }
map01bf@622 471 //the rest of spec->refine
map01bf@622 472 if (radius){
map01bf@622 473 spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS;
map01bf@622 474 spec->refine.radius = radius;
map01bf@622 475 }
map01bf@622 476 if (absThres){
map01bf@622 477 spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD;
map01bf@622 478 spec->refine.absolute_threshold = absThres;
map01bf@622 479 }
map01bf@622 480 if (relThres){
map01bf@622 481 spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD;
map01bf@622 482 spec->refine.relative_threshold = relThres;
map01bf@622 483 }
map01bf@622 484 if (durRatio){
map01bf@622 485 spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO;
map01bf@622 486 spec->refine.duration_ratio = durRatio;
map01bf@622 487 }
map01bf@622 488 if (hop){
map01bf@622 489 spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE;
mas01cr@679 490 /* not ideal but a temporary bandage fix */
mas01cr@679 491 spec->refine.qhopsize = hop;
mas01cr@679 492 spec->refine.ihopsize = hop;
map01bf@622 493 }
map01bf@622 494 //setup the datum
map01bf@622 495 spec->qid.datum->data = NULL;
map01bf@622 496 spec->qid.datum->power = NULL;
map01bf@622 497 spec->qid.datum->times = NULL;
map01bf@622 498 //grab the datum from the key
map01bf@622 499 ok = audiodb_retrieve_datum(current_db, key, spec->qid.datum);
map01bf@622 500 if (ok != 0){
map01bf@622 501 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n");
map01bf@622 502 return NULL;
map01bf@622 503 }
map01bf@622 504 result = audiodb_query_spec(current_db, spec);
map01bf@622 505 if (result == NULL){
map01bf@718 506 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
map01bf@622 507 return NULL;
map01bf@622 508 }
map01bf@624 509 if(strcmp(resFmt, "dict")==0){
map01bf@624 510 outgoing = PyDict_New();
map01bf@624 511 for (i=0;i<result->nresults;i++){
mas01cr@672 512 thisKey = PyString_FromString(result->results[i].ikey);
map01bf@624 513 if (!PyDict_Contains(outgoing, thisKey)){
map01bf@624 514 newBits = Py_BuildValue("[(dII)]",
map01bf@624 515 result->results[i].dist,
map01bf@624 516 result->results[i].qpos,
map01bf@624 517 result->results[i].ipos);
map01bf@624 518 if (PyDict_SetItem(outgoing, thisKey,newBits)){
mas01cr@672 519 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 520 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 521 Py_XDECREF(newBits);
map01bf@624 522 return NULL;
map01bf@624 523 }
map01bf@624 524 Py_DECREF(newBits);
map01bf@624 525 }else {
map01bf@624 526 //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 527 currentValue = PyDict_GetItem(outgoing, thisKey);
map01bf@624 528 if (!PyList_Check(currentValue)){
map01bf@624 529 PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n");
map01bf@624 530 return NULL;
map01bf@624 531 }
map01bf@624 532 newBits = Py_BuildValue("dII",result->results[i].dist,
map01bf@624 533 result->results[i].qpos,
map01bf@624 534 result->results[i].ipos);
map01bf@624 535 if (PyList_Append(currentValue, newBits)){
map01bf@624 536 //error msg here
map01bf@624 537 Py_XDECREF(newBits);
map01bf@624 538 return NULL;
map01bf@624 539 }
map01bf@624 540 if (PyDict_SetItem(outgoing, thisKey, newBits)){
map01bf@624 541 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 542 Py_XDECREF(newBits);
map01bf@624 543 return NULL;
map01bf@624 544 }
map01bf@624 545 Py_DECREF(newBits);
map01bf@624 546
map01bf@624 547 }
map01bf@624 548 }
map01bf@624 549 }else if(strcmp(resFmt, "list")==0){
map01bf@624 550 outgoing = PyList_New((Py_ssize_t)0);
map01bf@624 551 for (i=0;i<result->nresults;i++){
mas01cr@672 552 newBits = Py_BuildValue("sdII",result->results[i].ikey,
map01bf@622 553 result->results[i].dist,
map01bf@622 554 result->results[i].qpos,
map01bf@622 555 result->results[i].ipos);
map01bf@624 556 if (PyList_Append(outgoing, newBits)){
map01bf@624 557 //error msg here
map01bf@622 558 Py_XDECREF(newBits);
map01bf@622 559 return NULL;
map01bf@622 560 }
map01bf@622 561 Py_DECREF(newBits);
map01bf@624 562 }
map01bf@624 563 if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward.
map01bf@720 564 PyErr_SetString(PyExc_RuntimeError,
map01bf@720 565 "the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n");
map01bf@624 566 return NULL;
map01bf@622 567 }
map01bf@624 568 }else{
map01bf@624 569 PyErr_SetString(PyExc_ValueError,
map01bf@624 570 "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n");
map01bf@624 571 return NULL;
map01bf@622 572 }
map01bf@628 573 if (audiodb_query_free_results(current_db, spec, result)){
map01bf@622 574 printf("bit of trouble freeing the result and spec...\ncheck for leaks.");
map01bf@622 575 }
map01bf@622 576
map01bf@622 577 return outgoing;
map01bf@622 578
map01bf@622 579
map01bf@622 580
map01bf@622 581 }
map01bf@622 582
mas01mc@750 583 /* Data query.
mas01mc@750 584 * Returns a dict that is result ordered and key = result key
mas01mc@750 585 * value is a list of tuples one per result associated with that key, of the form:
mas01mc@750 586 * (dist, qpos, ipos)
mas01mc@750 587 * api call:
mas01mc@750 588 * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *);
mas01mc@750 589 ***/
mas01mc@750 590 PyObject * _pyadb_queryFromData(PyObject *self, PyObject *args, PyObject *keywds)
mas01mc@750 591 {
mas01mc@750 592 adb_t *current_db;
mas01mc@750 593 adb_query_spec_t *spec;
mas01mc@750 594 adb_query_results_t *result;
mas01mc@750 595 int ok, exhaustive, falsePositives;
mas01mc@750 596 uint32_t i;
mas01mc@750 597 const char *accuMode = "db";
mas01mc@750 598 const char *distMode = "dot";
mas01mc@750 599 const char *resFmt = "dict";
mas01mc@750 600 uint32_t hop = 0;
mas01mc@750 601 double radius = 0;
mas01mc@750 602 double absThres = 0;
mas01mc@750 603 double relThres = 0;
mas01mc@750 604 double durRatio = 0;
mas01mc@750 605 PyObject *includeKeys = NULL;
mas01mc@750 606 PyObject *excludeKeys = NULL;
mas01mc@750 607 PyObject *incoming = NULL;
mas01mc@750 608 PyObject *outgoing = NULL;
mas01mc@750 609 PyObject *thisKey = NULL;
mas01mc@750 610 PyObject *currentValue = NULL;
mas01mc@750 611 PyObject *newBits = NULL;
mas01mc@750 612 unsigned int nDims = 0;
mas01mc@750 613 unsigned int nVect = 0;
mas01mc@750 614 PyArrayObject *features = NULL;
mas01mc@750 615 PyArrayObject *power = NULL;
mas01mc@750 616 PyArrayObject *times = NULL;
mas01mc@750 617 adb_status_t *status;
mas01mc@750 618
mas01mc@750 619 static char *kwlist[] = { "db", "features",
mas01mc@750 620 "seqLength",
mas01mc@750 621 "seqStart",
mas01mc@750 622 "exhaustive",
mas01mc@750 623 "falsePositives",
mas01mc@750 624 "accumulation",
mas01mc@750 625 "distance",
mas01mc@750 626 "npoints",//nearest neighbor points per track
mas01mc@750 627 "ntracks",
mas01mc@750 628 "includeKeys",
mas01mc@750 629 "excludeKeys",
mas01mc@750 630 "radius",
mas01mc@750 631 "absThres",
mas01mc@750 632 "relThres",
mas01mc@750 633 "durRatio",
mas01mc@750 634 "hopSize",
mas01mc@750 635 "resFmt",
mas01mc@750 636 "power",
mas01mc@750 637 "times",
mas01mc@750 638 NULL
mas01mc@750 639 };
mas01mc@750 640
mas01mc@750 641 spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
mas01mc@750 642 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
mas01mc@750 643 result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
mas01mc@750 644
mas01mc@750 645 spec->qid.sequence_length = 16;
mas01mc@750 646 spec->qid.sequence_start = 0;
mas01mc@750 647 spec->qid.flags = 0;
mas01mc@750 648 spec->params.npoints = 1;
mas01mc@750 649 spec->params.ntracks = 100;//number of results returned in db mode
mas01mc@750 650 spec->refine.flags = 0;
mas01mc@750 651
mas01mc@750 652 ok = PyArg_ParseTupleAndKeywords(args, keywds, "OO!|iiiissIIOOddddIsO!O!", kwlist,
mas01mc@750 653 &incoming, &PyArray_Type, &features,
mas01mc@750 654 &spec->qid.sequence_length,
mas01mc@750 655 &spec->qid.sequence_start,
mas01mc@750 656 &exhaustive, &falsePositives,
mas01mc@750 657 &accuMode,&distMode,
mas01mc@750 658 &spec->params.npoints,
mas01mc@750 659 &spec->params.ntracks,
mas01mc@750 660 &includeKeys, &excludeKeys,
mas01mc@750 661 &radius, &absThres, &relThres, &durRatio, &hop,
mas01mc@750 662 &resFmt,
mas01mc@750 663 &PyArray_Type, &power, &PyArray_Type, &times
mas01mc@750 664 );
mas01mc@750 665
mas01mc@750 666 if (!ok) {return NULL;}
mas01mc@750 667 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01mc@750 668
mas01mc@750 669 if (!features){ /* Sanity Check */
mas01mc@750 670 PyErr_SetString(PyExc_ValueError,
mas01mc@750 671 "queryFromData: function requires feature data as numpy ndarray. PythonC required keyword check failed.\n");
mas01mc@750 672 return NULL;
mas01mc@750 673 }
mas01mc@750 674
mas01mc@750 675 /* Check the dimensionality of passed data agrees with the passed database */
mas01mc@750 676 if(PyArray_NDIM(features)!=2){
mas01mc@750 677 PyErr_SetString(PyExc_ValueError,
mas01mc@750 678 "queryFromData: passed features have incorrect shape, should be (nVecs, nDims).\n");
mas01mc@750 679 return NULL;
mas01mc@750 680 }
mas01mc@750 681
mas01mc@750 682
mas01mc@750 683 if(power && PyArray_NDIM(power)!=1){
mas01mc@750 684 PyErr_SetString(PyExc_ValueError,
mas01mc@750 685 "queryFromData: passed power have incorrect shape, should be (nVecs,).\n");
mas01mc@750 686 return NULL;
mas01mc@750 687 }
mas01mc@750 688
mas01mc@750 689 if(times && PyArray_NDIM(times)!=1){
mas01mc@750 690 PyErr_SetString(PyExc_ValueError,
mas01mc@750 691 "queryFromData: passed times have incorrect shape, should be (nVecs,).\n");
mas01mc@750 692 return NULL;
mas01mc@750 693 }
mas01mc@750 694
mas01mc@750 695 status = (adb_status_t*) malloc(sizeof(adb_status_t));
mas01mc@750 696 int errtest = audiodb_status(current_db, status);
mas01mc@750 697 if(errtest){
mas01mc@750 698 PyErr_SetString(PyExc_TypeError, "queryFromData failed: could not get status of passed ADB database");
mas01mc@750 699 free(status);
mas01mc@750 700 return NULL;
mas01mc@750 701 }
mas01mc@750 702
mas01mc@750 703 if(!PyArray_DIMS(features)[1]==status->dim){
mas01mc@750 704 PyErr_SetString(PyExc_ValueError,
mas01mc@750 705 "queryFromData: passed features have incorrect dimensionality.\n");
mas01mc@750 706 free(status);
mas01mc@750 707 return NULL;
mas01mc@750 708 }
mas01mc@750 709
mas01mc@750 710 if(power && PyArray_DIMS(power)[0] != PyArray_DIMS(features)[0]){
mas01mc@750 711 PyErr_SetString(PyExc_ValueError,
mas01mc@750 712 "queryFromData: passed power and features have incompatible nVecs dimension.\n");
mas01mc@750 713 free(status);
mas01mc@750 714 return NULL;
mas01mc@750 715 }
mas01mc@750 716
mas01mc@750 717 if(times && PyArray_DIMS(times)[0] != PyArray_DIMS(features)[0]){
mas01mc@750 718 PyErr_SetString(PyExc_ValueError,
mas01mc@750 719 "queryFromData: passed times and features have incompatible nVecs dimension.\n");
mas01mc@750 720 free(status);
mas01mc@750 721 return NULL;
mas01mc@750 722 }
mas01mc@750 723
mas01mc@750 724 free(status);
mas01mc@750 725
mas01mc@750 726 if (exhaustive){
mas01mc@750 727 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
mas01mc@750 728 }
mas01mc@750 729 if (falsePositives){
mas01mc@750 730 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES;
mas01mc@750 731 }
mas01mc@750 732
mas01mc@750 733 //set up spec->params
mas01mc@750 734 if (strcmp(accuMode,"db")==0){
mas01mc@750 735 spec->params.accumulation = ADB_ACCUMULATION_DB;
mas01mc@750 736 } else if (strcmp(accuMode,"track")==0){
mas01mc@750 737 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
mas01mc@750 738 } else if (strcmp(accuMode,"one2one")==0){
mas01mc@750 739 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE;
mas01mc@750 740 } else{
mas01mc@750 741 PyErr_SetString(PyExc_ValueError,
mas01mc@750 742 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n");
mas01mc@750 743 return NULL;
mas01mc@750 744 }
mas01mc@750 745 if (strcmp(distMode, "dot")==0){
mas01mc@750 746 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
mas01mc@750 747 }else if (strcmp(distMode, "eucNorm")==0){
mas01mc@750 748 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
mas01mc@750 749 }else if (strcmp(distMode, "euclidean")==0){
mas01mc@750 750 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
mas01mc@768 751 }else if (strcmp(distMode, "kullback")==0){
mas01mc@768 752 spec->params.distance = ADB_DISTANCE_KULLBACK_LEIBLER_DIVERGENCE;
mas01mc@750 753 }else{
mas01mc@750 754 PyErr_SetString(PyExc_ValueError,
mas01mc@768 755 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' ,\'euclidean\' or \'kullback\'.\n");
mas01mc@750 756 return NULL;
mas01mc@750 757 }
mas01mc@750 758
mas01mc@750 759 //set up spec->refine
mas01mc@750 760 //include/exclude keys
mas01mc@750 761 if (includeKeys){
mas01mc@750 762 if (!PyList_Check(includeKeys)){
mas01mc@750 763 PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n");
mas01mc@750 764 return NULL;
mas01mc@750 765 }
mas01mc@750 766 spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST;
mas01mc@750 767 spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys);
mas01mc@750 768 spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys);
mas01mc@750 769 for (i=0;i<spec->refine.include.nkeys;i++){
mas01mc@750 770 if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){
mas01mc@750 771 spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i));
mas01mc@750 772 }else{
mas01mc@750 773 PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n");
mas01mc@750 774 return NULL;
mas01mc@750 775 }
mas01mc@750 776 }
mas01mc@750 777 }
mas01mc@750 778 if (excludeKeys){
mas01mc@750 779 if (!PyList_Check(excludeKeys)){
mas01mc@750 780 PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n");
mas01mc@750 781 return NULL;
mas01mc@750 782 }
mas01mc@750 783 spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST;
mas01mc@750 784 spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys);
mas01mc@750 785 spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys);
mas01mc@750 786 for (i=0;i<spec->refine.exclude.nkeys;i++){
mas01mc@750 787 if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){
mas01mc@750 788 spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i));
mas01mc@750 789 }else{
mas01mc@750 790 PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n");
mas01mc@750 791 return NULL;
mas01mc@750 792 }
mas01mc@750 793 }
mas01mc@750 794 }
mas01mc@750 795 //the rest of spec->refine
mas01mc@750 796 if (radius){
mas01mc@750 797 spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS;
mas01mc@750 798 spec->refine.radius = radius;
mas01mc@750 799 }
mas01mc@750 800 if (absThres){
mas01mc@750 801 spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD;
mas01mc@750 802 spec->refine.absolute_threshold = absThres;
mas01mc@750 803 }
mas01mc@750 804 if (relThres){
mas01mc@750 805 spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD;
mas01mc@750 806 spec->refine.relative_threshold = relThres;
mas01mc@750 807 }
mas01mc@750 808 if (durRatio){
mas01mc@750 809 spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO;
mas01mc@750 810 spec->refine.duration_ratio = durRatio;
mas01mc@750 811 }
mas01mc@750 812 if (hop){
mas01mc@750 813 spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE;
mas01mc@750 814 /* not ideal but a temporary bandage fix */
mas01mc@750 815 spec->refine.qhopsize = hop;
mas01mc@750 816 spec->refine.ihopsize = hop;
mas01mc@750 817 }
mas01mc@750 818
mas01mc@758 819 spec->qid.datum->data = (double*) features->data;
mas01mc@750 820
mas01mc@750 821 if (power){
mas01mc@758 822 spec->qid.datum->power = (double*) power->data;
mas01mc@750 823 }else{
mas01mc@750 824 spec->qid.datum->power=NULL;
mas01mc@750 825 }
mas01mc@750 826
mas01mc@750 827 if (times){
mas01mc@758 828 spec->qid.datum->times = (double*) times->data;
mas01mc@750 829 }else{
mas01mc@750 830 spec->qid.datum->times=NULL;
mas01mc@750 831 }
mas01mc@750 832
mas01mc@750 833 nVect = PyArray_DIMS(features)[0];
mas01mc@750 834 nDims = PyArray_DIMS(features)[1];
mas01mc@750 835 spec->qid.datum->nvectors = (uint32_t)nVect;
mas01mc@750 836 spec->qid.datum->dim = (uint32_t)nDims;
mas01mc@750 837
mas01mc@750 838 result = audiodb_query_spec(current_db, spec);
mas01mc@750 839
mas01mc@750 840 if (result == NULL){
mas01mc@750 841 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
mas01mc@750 842 return NULL;
mas01mc@750 843 }
mas01mc@750 844 if(strcmp(resFmt, "dict")==0){
mas01mc@750 845 outgoing = PyDict_New();
mas01mc@750 846 for (i=0;i<result->nresults;i++){
mas01mc@750 847 thisKey = PyString_FromString(result->results[i].ikey);
mas01mc@750 848 if (!PyDict_Contains(outgoing, thisKey)){
mas01mc@750 849 newBits = Py_BuildValue("[(dII)]",
mas01mc@750 850 result->results[i].dist,
mas01mc@750 851 result->results[i].qpos,
mas01mc@750 852 result->results[i].ipos);
mas01mc@750 853 if (PyDict_SetItem(outgoing, thisKey,newBits)){
mas01mc@750 854 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);
mas01mc@750 855 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
mas01mc@750 856 Py_XDECREF(newBits);
mas01mc@750 857 return NULL;
mas01mc@750 858 }
mas01mc@750 859 Py_DECREF(newBits);
mas01mc@750 860 }else {
mas01mc@750 861 //the key already has a value, so we need to fetch the value, confirm it's a list and append another tuple to it.
mas01mc@750 862 currentValue = PyDict_GetItem(outgoing, thisKey);
mas01mc@750 863 if (!PyList_Check(currentValue)){
mas01mc@750 864 PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n");
mas01mc@750 865 return NULL;
mas01mc@750 866 }
mas01mc@750 867 newBits = Py_BuildValue("dII",result->results[i].dist,
mas01mc@750 868 result->results[i].qpos,
mas01mc@750 869 result->results[i].ipos);
mas01mc@750 870 if (PyList_Append(currentValue, newBits)){
mas01mc@750 871 //error msg here
mas01mc@750 872 Py_XDECREF(newBits);
mas01mc@750 873 return NULL;
mas01mc@750 874 }
mas01mc@750 875 if (PyDict_SetItem(outgoing, thisKey, newBits)){
mas01mc@750 876 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
mas01mc@750 877 Py_XDECREF(newBits);
mas01mc@750 878 return NULL;
mas01mc@750 879 }
mas01mc@750 880 Py_DECREF(newBits);
mas01mc@750 881
mas01mc@750 882 }
mas01mc@750 883 }
mas01mc@750 884 }else if(strcmp(resFmt, "list")==0){
mas01mc@750 885 outgoing = PyList_New((Py_ssize_t)0);
mas01mc@750 886 for (i=0;i<result->nresults;i++){
mas01mc@750 887 newBits = Py_BuildValue("sdII",result->results[i].ikey,
mas01mc@750 888 result->results[i].dist,
mas01mc@750 889 result->results[i].qpos,
mas01mc@750 890 result->results[i].ipos);
mas01mc@750 891 if (PyList_Append(outgoing, newBits)){
mas01mc@750 892 //error msg here
mas01mc@750 893 Py_XDECREF(newBits);
mas01mc@750 894 return NULL;
mas01mc@750 895 }
mas01mc@750 896 Py_DECREF(newBits);
mas01mc@750 897 }
mas01mc@750 898 if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward.
mas01mc@750 899 PyErr_SetString(PyExc_RuntimeError,
mas01mc@750 900 "the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n");
mas01mc@750 901 return NULL;
mas01mc@750 902 }
mas01mc@750 903 }else{
mas01mc@750 904 PyErr_SetString(PyExc_ValueError,
mas01mc@750 905 "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n");
mas01mc@750 906 return NULL;
mas01mc@750 907 }
mas01mc@750 908 if (audiodb_query_free_results(current_db, spec, result)){
mas01mc@750 909 printf("bit of trouble freeing the result and spec...\ncheck for leaks.");
mas01mc@750 910 }
mas01mc@750 911
mas01mc@750 912 return outgoing;
mas01mc@750 913
mas01mc@750 914
mas01mc@750 915
mas01mc@750 916 }
mas01mc@750 917
map01bf@622 918
mas01mc@746 919 /* retrieval of inserted data
mas01mc@746 920 * returned numpy array has ndarray.shape = (numVectors, numDims)
mas01mc@746 921 * array datatype needs to be doubles (float may work...)
mas01mc@746 922 * if power reqeusted, it will be a 1d array of length numVectors
mas01mc@746 923 * if times are requested, they will be a 1d array of length 2*nvectors
mas01mc@746 924 */
mas01mc@746 925
mas01mc@746 926 // api call:
mas01mc@746 927 // typedef struct adb_datum {
mas01mc@746 928 // uint32_t nvectors;
mas01mc@746 929 // uint32_t dim;
mas01mc@746 930 // const char *key;
mas01mc@746 931 // double *data;
mas01mc@746 932 // double *power;
mas01mc@746 933 // double *times;
mas01mc@746 934 // } adb_datum_t;
mas01mc@746 935
mas01mc@746 936 //int audiodb_retrieve_datum(adb_t *, const char *, adb_datum_t *);
mas01mc@746 937 //int audiodb_free_datum(adb_t *, adb_datum_t *);
mas01mc@746 938 PyObject * _pyadb_retrieveDatum(PyObject *self, PyObject *args, PyObject *keywds)
mas01mc@746 939 {
mas01mc@746 940 adb_t *current_db = NULL;
mas01mc@746 941 adb_status_t *status = NULL;
mas01mc@746 942 adb_datum_t *ins = NULL;
mas01mc@746 943 int ok=0, errtest=0;
mas01mc@746 944 unsigned features=0, powers=0, times=0;
mas01mc@746 945 PyObject *incoming = 0; // The ADB database
mas01mc@746 946 PyObject *outgoing = 0; // The PyArrayObject
mas01mc@746 947 const char *key = NULL;
mas01mc@746 948 static char *kwlist[] = { "db", "key", "features", "powers", "times", NULL};
mas01mc@758 949 double * data = NULL;
mas01mc@746 950 int dims = 0;
mas01mc@746 951 npy_intp shape[2] = { 0, 0 };
mas01mc@746 952
mas01mc@746 953 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|III", kwlist, &incoming, &key, &features, &powers, &times);
mas01mc@746 954 if (!ok){
mas01mc@746 955 PyErr_SetString(PyExc_TypeError, "Failed at PyArg_ParseTupleAndKeywords");
mas01mc@746 956 return NULL;
mas01mc@746 957 }
mas01mc@746 958
mas01mc@746 959 if(features+powers+times>1){
mas01mc@746 960 PyErr_SetString(PyExc_TypeError, "Failed: you must specify only one of features, powers, or times");
mas01mc@746 961 return NULL;
mas01mc@746 962 }
mas01mc@746 963
mas01mc@746 964 if(!(features||powers||times)){
mas01mc@746 965 features=1; // default is to return features
mas01mc@746 966 }
mas01mc@746 967
mas01mc@746 968 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01mc@746 969 if (!current_db){
mas01mc@746 970 PyErr_SetString(PyExc_TypeError, "Failed to convert open database to C-pointer");
mas01mc@746 971 return NULL;
mas01mc@746 972 }
mas01mc@746 973 status = (adb_status_t*) malloc(sizeof(adb_status_t));
mas01mc@746 974 errtest = audiodb_status(current_db, status);
mas01mc@746 975 if(errtest){
mas01mc@746 976 PyErr_SetString(PyExc_TypeError, "Failed: could not get status of passed ADB database");
mas01mc@746 977 free(status);
mas01mc@746 978 return NULL;
mas01mc@746 979 }
mas01mc@746 980
mas01mc@746 981 if(powers && !(status->flags&ADB_HEADER_FLAG_POWER)){
mas01mc@746 982 PyErr_SetString(PyExc_TypeError, "Failed: powers requested but passed ADB database has no powers");
mas01mc@746 983 free(status);
mas01mc@746 984 return NULL;
mas01mc@746 985 }
mas01mc@746 986
mas01mc@746 987 if(times && !(status->flags&ADB_HEADER_FLAG_TIMES)){
mas01mc@746 988 PyErr_SetString(PyExc_TypeError, "Failed: times requested but passed ADB database has no times");
mas01mc@746 989 free(status);
mas01mc@746 990 return NULL;
mas01mc@746 991 }
mas01mc@746 992
mas01mc@746 993 ins = (adb_datum_t *)malloc(sizeof(adb_datum_t));
mas01mc@746 994 errtest = audiodb_retrieve_datum(current_db, key, ins); // retrieve data from adb via key
mas01mc@746 995 if (errtest){
mas01mc@746 996 PyErr_SetString(PyExc_TypeError, "Failed to retrieve datum");
mas01mc@746 997 free(ins);
mas01mc@746 998 return NULL;
mas01mc@746 999 }
mas01mc@746 1000
mas01mc@746 1001 if(features){
mas01mc@746 1002 if(ins->dim>1){
mas01mc@746 1003 dims=2;
mas01mc@751 1004 shape[1]= ins->dim;
mas01mc@746 1005 }
mas01mc@746 1006 else{
mas01mc@746 1007 dims=1;
mas01mc@746 1008 }
mas01mc@746 1009 shape[0]= ins->nvectors;
mas01mc@746 1010 data = ins->data;
mas01mc@746 1011 }
mas01mc@746 1012 else if(powers){
mas01mc@746 1013 dims=1;
mas01mc@746 1014 shape[0]= ins->nvectors;
mas01mc@746 1015 data = ins->power;
mas01mc@746 1016 }
mas01mc@746 1017 else if(times){
mas01mc@746 1018 dims=1;
mas01mc@746 1019 shape[0]= 2 * ins->nvectors;
mas01mc@746 1020 data = ins->times;
mas01mc@746 1021 }
mas01mc@746 1022
mas01mc@748 1023 outgoing = PyArray_SimpleNew(dims, shape, NPY_DOUBLE);
mas01mc@746 1024 if (!outgoing){
mas01mc@748 1025 free(status);
mas01mc@748 1026 free(ins); // free the malloced adb_datum_t structure though
mas01mc@748 1027 Py_XDECREF(outgoing);
mas01mc@746 1028 PyErr_SetString(PyExc_TypeError, "Failed to convert retrieved datum to C-Array");
mas01mc@746 1029 return NULL;
mas01mc@748 1030 }
mas01mc@747 1031
mas01mc@748 1032 /* Copy the data, this allows us to free the allocated memory and let
mas01mc@748 1033 * python do the subsequent garbage collection itself.
mas01mc@748 1034 */
mas01mc@751 1035 int num_items = ins->nvectors;
mas01mc@751 1036 if(dims>1){
mas01mc@751 1037 num_items *= shape[1];
mas01mc@751 1038 }
mas01mc@748 1039 double* p = (double*) PyArray_DATA(outgoing);
mas01mc@748 1040 double* d = data;
mas01mc@748 1041 while(num_items--)
mas01mc@748 1042 *p++ = *d++;
mas01mc@748 1043 audiodb_free_datum(current_db, ins); // free the source audiodb_datum
mas01mc@748 1044 free(status); // free the malloced status object
mas01mc@748 1045 free(ins); // free the malloced adb_datum_t structure though
mas01mc@746 1046 return outgoing;
mas01mc@746 1047 }
map01bf@622 1048
map01bf@622 1049
map01bf@620 1050 /* close a database */
map01bf@620 1051 /* api call: */
mas01cr@671 1052 // void audiodb_close(adb_t *db);
map01bf@620 1053 static void _pyadb_close(void *ptr)
map01bf@620 1054 {
mas01cr@671 1055 adb_t *stale_database;
mas01cr@671 1056 stale_database = (adb_t *)ptr;
map01bf@620 1057
map01bf@620 1058 audiodb_close(stale_database);
map01bf@620 1059 }
map01bf@620 1060
map01bf@620 1061 static PyMethodDef _pyadbMethods[] =
map01bf@620 1062 {
map01bf@620 1063 { "_pyadb_create", _pyadb_create, METH_VARARGS,
map01bf@620 1064 "_pyadb_create(string path, unsigned datasize, unsigned ntracks, unsigned datadim)->adb object"},
map01bf@620 1065 { "_pyadb_open", _pyadb_open, METH_VARARGS,
map01bf@620 1066 "_pyadb_open(string path, [\'r\'|\'w\'])->adb object\nNote that specifing \'w\' opens the file in read and write mode. \
map01bf@620 1067 There is currently no way to open in write only."},
map01bf@620 1068 { "_pyadb_status", _pyadb_status, METH_VARARGS,
mas01cr@671 1069 "_status(adb_t *)->(numFiles, dims, dudCount, nullCount, flags, length, data_region_size)"},
map01bf@620 1070 { "_pyadb_l2norm", _pyadb_l2norm, METH_VARARGS,
mas01cr@671 1071 "_pyadb_l2norm(adb_t *)->int return code (0 for sucess)"},
map01bf@620 1072 { "_pyadb_power", _pyadb_power, METH_VARARGS,
mas01cr@671 1073 "_pyadb_power(adb_t *)->int return code (0 for sucess)"},
map01bf@716 1074 {"_pyadb_insertFromArray", (PyCFunction)_pyadb_insertFromArray, METH_VARARGS | METH_KEYWORDS,
map01bf@718 1075 "_pyadb_insertFromArray(adb_t *, features=ndarray, [power=ndarray | key=keystring | times=ndarray])->\
map01bf@718 1076 int return code (0 for sucess)\n\
map01bf@718 1077 insert feature data from a numpy array\n\
map01bf@716 1078 array given should have ndarray.shape = (numDims*numVectors,)\n\
map01bf@716 1079 array datatype needs to be doubles (float may work...)\n\
map01bf@716 1080 if power is given, must be 1d array of length numVectors\n\
map01bf@716 1081 if times is given, must be 1d array of length 2*numVectors like this:\n\
map01bf@716 1082 int audiodb_insert_datum(adb_t *, const adb_datum_t *);"},
mas01mc@746 1083 {"_pyadb_retrieveDatum", (PyCFunction)_pyadb_retrieveDatum, METH_VARARGS | METH_KEYWORDS, "_pyadb_retrieveDatum(adb_t *, key=keystring"},
map01bf@622 1084 { "_pyadb_insertFromFile", (PyCFunction)_pyadb_insertFromFile, METH_VARARGS | METH_KEYWORDS,
mas01cr@671 1085 "_pyadb_insertFromFile(adb_t *, features=featureFile, [power=powerfile | key=keystring | times=timingFile])->\
map01bf@621 1086 int return code (0 for sucess)"},
mas01mc@744 1087 { "_pyadb_liszt", (PyCFunction)_pyadb_liszt, METH_VARARGS,
mas01mc@744 1088 "_pyadb_liszt(adb_t*)->[[key1,numvecs1],[key2,numvecs2]...]"},
map01bf@622 1089 { "_pyadb_queryFromKey", (PyCFunction)_pyadb_queryFromKey, METH_VARARGS | METH_KEYWORDS,
map01bf@622 1090 "base query. The nomenclature here is about a far away as pythonic as is possible.\n\
map01bf@622 1091 This should be taken care of via the higher level python structure\n\
map01bf@622 1092 returns a dict that should be result ordered and key = result key\n\
map01bf@622 1093 and value is a list of tuples one per result associated with that key, of the form:\n\
map01bf@622 1094 \t(dist, qpos, ipos)\n\
map01bf@622 1095 Note as well that this is by no means the most efficient way to cast from C, simply the most direct\n\
map01bf@622 1096 and what it lacks in effeciency it gains in python side access. It remains to be seen if this is\n\
map01bf@622 1097 a sensible trade.\n\
mas01cr@671 1098 _pyadb_queryFromKey(adb_t *, query key,\n\
map01bf@622 1099 [seqLength = Int Sequence Length, \n\
map01bf@622 1100 seqStart = Int offset from start for key, \n\
map01bf@622 1101 exhaustive = boolean - True for exhaustive (false by default),\n\
map01bf@622 1102 falsePositives= boolean - True to keep fps (false by defaults),\n\
map01bf@622 1103 accumulation = [\"db\"|\"track\"|\"one2one\"] (\"db\" by default),\n\
map01bf@622 1104 distance = [\"dot\"|\"eucNorm\"|\"euclidean\"] (\"dot\" by default),\n\
map01bf@622 1105 npoints = int number of points per track,\n\
map01bf@622 1106 ntracks = max number of results returned in db accu mode,\n\
map01bf@622 1107 includeKeys = list of strings to include (use all by default),\n\
map01bf@622 1108 excludeKeys = list of strings to exclude (none by default),\n\
map01bf@622 1109 radius = double of nnRadius (1.0 default, overrides npoints if specified),\n\
map01bf@622 1110 absThres = double absolute power threshold (db must have power),\n\
map01bf@622 1111 relThres = double relative power threshold (db must have power),\n\
map01bf@622 1112 durRatio = double time expansion/compresion ratio,\n\
map01bf@628 1113 hopSize = int hopsize (1 by default)])->resultDict\n\
map01bf@628 1114 resFmt = [\"list\"|\"dict\"](\"dict\" by default)"},
mas01mc@750 1115 {"_pyadb_queryFromData", (PyCFunction)_pyadb_queryFromData, METH_VARARGS | METH_KEYWORDS,
mas01mc@750 1116 "data query. Required features=F (numpy ndarray). Optional: power=P (numpy 1d array), times=T (numpy 1d array)"},
map01bf@620 1117 {NULL,NULL, 0, NULL}
map01bf@620 1118 };
map01bf@620 1119
mas01mc@758 1120 void init_pyadb(void)
map01bf@620 1121 {
map01bf@718 1122 Py_InitModule3("_pyadb", _pyadbMethods, "internal c bindings for audioDB. Use pyadb for pythonic access to adb.");
map01bf@718 1123 import_array();
map01bf@620 1124 return;
map01bf@620 1125 }
map01bf@620 1126
map01bf@620 1127