annotate bindings/python/pyadbmodule.c @ 719:e3f1cf653c30

wooo! direct insert works! at least for the rather limited cases I've tests. Bad news is that I seem to have found a rather nasty bug in the query code I wrote back in september. (segfaults around line 471 if the query returns no results...)
author map01bf
date Fri, 25 Jun 2010 09:08:56 +0000
parents 14568e432e73
children 2fad8cfdb2d8
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
map01bf@620 18 static void _pyadb_close(void *ptr);
map01bf@620 19
map01bf@620 20
map01bf@620 21 /* create a new database */
map01bf@620 22 /* returns a struct or NULL on failure */
map01bf@620 23 /* api call: */
mas01cr@671 24 /* adb_t *audiodb_create(const char *path, unsigned datasize, unsigned ntracks, unsigned datadim);*/
map01bf@620 25 PyObject * _pyadb_create(PyObject *self, PyObject *args)
map01bf@620 26 {
map01bf@620 27 unsigned datasize, ntracks, datadim;
map01bf@620 28 const char *path;
map01bf@620 29 int ok;
mas01cr@671 30 adb_t *new_database;
map01bf@620 31 ok = PyArg_ParseTuple(args, "sIII", &path, &datasize, &ntracks, &datadim);
map01bf@620 32 if (!ok) return 0;
map01bf@620 33 new_database = audiodb_create(path, datasize, ntracks, datadim);
map01bf@620 34 if (!new_database) return 0;
map01bf@620 35
map01bf@620 36 return PyCObject_FromVoidPtr( new_database, _pyadb_close);
map01bf@620 37 }
map01bf@620 38
map01bf@620 39 /* open an existing database */
map01bf@620 40 /* returns a struct or NULL on failure */
map01bf@620 41 /* flags expects fcntl flags concerning the opening mode */
map01bf@620 42 /* api call: */
mas01cr@671 43 // adb_t *audiodb_open(const char *path, int flags);
map01bf@620 44 PyObject * _pyadb_open(PyObject *self, PyObject *args)
map01bf@620 45 {
map01bf@620 46 const char *path;
map01bf@620 47 char mode;
map01bf@620 48 int ok;//in python layer need to translate boolean flags to byte mask
mas01cr@671 49 adb_t *fresh_database;
map01bf@620 50 ok = PyArg_ParseTuple(args, "sc", &path, &mode);
map01bf@620 51 if (!ok) return 0;
map01bf@620 52 if (mode == 'r'){
map01bf@620 53 fresh_database = audiodb_open(path, O_RDONLY);
map01bf@620 54 }else if (mode == 'w'){
map01bf@620 55 fresh_database = audiodb_open(path, O_RDWR);
map01bf@620 56 }else{
map01bf@620 57 PyErr_SetString(PyExc_ValueError,
map01bf@620 58 "mode must be either \'r\' or \'w\'. It appears to be something else.");
map01bf@620 59 return 0;
map01bf@620 60 }
map01bf@620 61 if (!fresh_database) return 0;
map01bf@620 62
map01bf@620 63 return PyCObject_FromVoidPtr( fresh_database, _pyadb_close);
map01bf@620 64 }
map01bf@620 65
map01bf@620 66 /* database status */
map01bf@620 67 /* api call: */
mas01cr@671 68 // int audiodb_status(adb_t *mydb, adb_status_ptr status);
map01bf@620 69 PyObject * _pyadb_status(PyObject *self, PyObject *args)
map01bf@620 70 {
mas01cr@671 71 adb_t *check_db;
mas01cr@671 72 adb_status_t *status;
map01bf@620 73 int flags, ok;
map01bf@620 74 PyObject * incoming = 0;
mas01cr@671 75 status = (adb_status_t *)malloc(sizeof(adb_status_t));
map01bf@620 76
map01bf@620 77 ok = PyArg_ParseTuple(args, "O", &incoming);
map01bf@620 78 if (!ok) return 0;
mas01cr@671 79 check_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@620 80
map01bf@620 81
map01bf@620 82 flags = audiodb_status(check_db, status);
map01bf@620 83 return Py_BuildValue("IIIIILL", status->numFiles,
map01bf@620 84 status->dim,
map01bf@620 85 status->dudCount,
map01bf@620 86 status->nullCount,
map01bf@620 87 status->flags,
map01bf@620 88 status->length,
map01bf@620 89 status->data_region_size);
map01bf@620 90
map01bf@620 91 }
map01bf@620 92
map01bf@620 93 /*engage l2norm in the referenced db*/
map01bf@620 94 /*api call:*/
mas01cr@671 95 //int audiodb_l2norm(adb_t *mydb);
map01bf@620 96 PyObject * _pyadb_l2norm(PyObject *self, PyObject *args)
map01bf@620 97 {
mas01cr@671 98 adb_t *current_db;
map01bf@620 99 int ok;
map01bf@620 100 PyObject * incoming = 0;
map01bf@620 101
map01bf@620 102 ok = PyArg_ParseTuple(args, "O", &incoming);
map01bf@620 103 if (!ok) return 0;
mas01cr@671 104 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@620 105
map01bf@620 106 ok = audiodb_l2norm(current_db);
map01bf@622 107 return PyBool_FromLong(ok-1);
map01bf@620 108
map01bf@620 109 }
map01bf@620 110
map01bf@620 111 /*engage power thresholding in the referenced db*/
map01bf@620 112 /*api call:*/
mas01cr@671 113 // int audiodb_power(adb_t *mydb);
map01bf@620 114 PyObject * _pyadb_power(PyObject *self, PyObject *args)
map01bf@620 115 {
mas01cr@671 116 adb_t *current_db;
map01bf@620 117 int ok;
map01bf@620 118 PyObject * incoming = 0;
map01bf@620 119
map01bf@620 120 ok = PyArg_ParseTuple(args, "O", &incoming);
map01bf@620 121 if (!ok) return 0;
mas01cr@671 122 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@620 123
map01bf@620 124 ok = audiodb_power(current_db);
map01bf@622 125 return PyBool_FromLong(ok-1);
map01bf@620 126
map01bf@620 127 }
map01bf@716 128
map01bf@716 129 /* insert feature data from a numpy array */
map01bf@716 130 /* array given should have ndarray.shape = (numDims, numVectors)*/
map01bf@716 131 /* array datatype needs to be doubles (float may work...)*/
map01bf@716 132 /* if power is given, must be 1d array of length numVectors*/
map01bf@716 133 /* if times is given, must be 1d array of length 2*numVectors like this:*/
map01bf@716 134
map01bf@716 135 /* api call: */
map01bf@716 136 // typedef struct adb_datum {
map01bf@716 137 // uint32_t nvectors;
map01bf@716 138 // uint32_t dim;
map01bf@716 139 // const char *key;
map01bf@716 140 // double *data;
map01bf@716 141 // double *power;
map01bf@716 142 // double *times;
map01bf@716 143 // } adb_datum_t;
map01bf@716 144 // int audiodb_insert_datum(adb_t *, const adb_datum_t *);
map01bf@716 145 PyObject * _pyadb_insertFromArray(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@716 146 {
map01bf@716 147 adb_t *current_db;
map01bf@716 148 adb_status_t *status;
map01bf@716 149 adb_datum_t *ins;
map01bf@716 150 int ok;
map01bf@716 151 npy_intp dims[1];
map01bf@716 152 unsigned int nDims = 0;
map01bf@716 153 unsigned int nVect = 0;
map01bf@716 154 PyObject *incoming = 0;
map01bf@718 155 PyArrayObject *features = 0;
map01bf@718 156 PyArrayObject *power = 0;
map01bf@718 157 PyArrayObject *times = 0;
map01bf@716 158 const char *key = NULL;
map01bf@716 159 PyArray_Descr *descr;
map01bf@716 160 static char *kwlist[] = { "db", "features", "nDim", "nVect", "power", "key", "times" , NULL};
map01bf@716 161
map01bf@718 162 ok = PyArg_ParseTupleAndKeywords(args, keywds, "OO!II|O!sO!", kwlist, &incoming, &PyArray_Type, &features, &nDims, &nVect, &PyArray_Type, &power, &key, &PyArray_Type, &times);
map01bf@716 163 if (!ok){return NULL;}
map01bf@716 164 //check our arrays
map01bf@718 165 // if (!PyArray_Check(features)){
map01bf@718 166 // PyErr_SetString(PyExc_TypeError, "features must be a numpy array (of floats or doubles)");
map01bf@718 167 // return NULL;
map01bf@718 168 // }
map01bf@716 169 if (!PyArray_ISFLOAT(features)){
map01bf@716 170 PyErr_SetString(PyExc_TypeError, "features numpy array must contain floats or doubles");
map01bf@716 171 return NULL;
map01bf@716 172 }
map01bf@716 173 if ((PyArray_NDIM(features) != 1) || (PyArray_DIMS(features)[0] != (nDims * nVect))){
map01bf@716 174 PyErr_SetString(PyExc_TypeError, "features numpy array must be flattened before call.");
map01bf@716 175 return NULL;
map01bf@716 176 }
map01bf@716 177 descr = PyArray_DescrFromType(NPY_DOUBLE);
map01bf@716 178
map01bf@716 179 if (power){
map01bf@716 180 if (!PyArray_Check(power)){
map01bf@716 181 PyErr_SetString(PyExc_TypeError, "power, if given, must be a numpy array (of floats or doubles)");
map01bf@716 182 return NULL;
map01bf@716 183 }
map01bf@716 184 if (!PyArray_ISFLOAT(power)){
map01bf@716 185 PyErr_SetString(PyExc_TypeError, "power numpy array, if given, must contain floats or doubles");
map01bf@716 186 return NULL;
map01bf@716 187 }
map01bf@716 188 // power = (PyArrayObject *)PyCObject_AsVoidPtr(incomingPow);
map01bf@716 189 if (PyArray_NDIM(features) != 1 || PyArray_DIMS(power)[0] == nVect){
map01bf@716 190 PyErr_SetString(PyExc_ValueError, "power, if given must be a 1d numpy array with shape = (numVectors,)");
map01bf@716 191 return NULL;
map01bf@716 192 }
map01bf@716 193 }
map01bf@716 194 if (times){
map01bf@716 195 if (!PyArray_Check(times)){
map01bf@716 196 PyErr_SetString(PyExc_TypeError, "times, if given, must be a numpy array (of floats or doubles)");
map01bf@716 197 return NULL;
map01bf@716 198 }
map01bf@716 199 if (!PyArray_ISFLOAT(times)){
map01bf@716 200 PyErr_SetString(PyExc_TypeError, "times numpy array, if given, must contain floats or doubles");
map01bf@716 201 return NULL;
map01bf@716 202 }
map01bf@716 203 // times = (PyArrayObject *)PyCObject_AsVoidPtr(incomingTime);
map01bf@716 204 if (PyArray_NDIM(times) != 1 || PyArray_DIMS(times)[0] == (nVect*2)){
map01bf@716 205 PyErr_SetString(PyExc_ValueError, "times, if given must be a 1d numpy array with shape = (numVectors,)");
map01bf@716 206 return NULL;
map01bf@716 207 }
map01bf@716 208 }
map01bf@716 209 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@716 210 status = (adb_status_t *)malloc(sizeof(adb_status_t));
map01bf@716 211 //verify that the data to be inserted is the correct size for the database.
map01bf@716 212
map01bf@716 213 ins = (adb_datum_t *)malloc(sizeof(adb_datum_t));
map01bf@719 214 if (PyArray_AsCArray(&features, &(ins->data), dims, 1, descr)){
map01bf@716 215 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the feature np array as a C array.");
map01bf@716 216 return NULL;
map01bf@716 217 }
map01bf@716 218
map01bf@718 219 if (power){
map01bf@719 220 if (PyArray_AsCArray(&power, &(ins->power), dims, 1, descr)){
map01bf@716 221 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the power np array as a C array.");
map01bf@716 222 return NULL;
map01bf@716 223 }
map01bf@719 224 }else{
map01bf@719 225 ins->power=NULL;
map01bf@716 226 }
map01bf@716 227
map01bf@718 228 if (times){
map01bf@719 229 if (PyArray_AsCArray(&times, &(ins->times), dims, 1, descr)){
map01bf@716 230 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the times np array as a C array.");
map01bf@716 231 return NULL;
map01bf@716 232 }
map01bf@719 233 }else{
map01bf@719 234 ins->times=NULL;
map01bf@716 235 }
map01bf@716 236 ins->key = key;
map01bf@716 237 ins->nvectors = (uint32_t)nVect;
map01bf@716 238 ins->dim = (uint32_t)nDims;
map01bf@716 239 //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times);
map01bf@716 240 ok = audiodb_insert_datum(current_db, ins);//(current_db, ins);
map01bf@719 241 return PyInt_FromLong(ok);
map01bf@716 242
map01bf@716 243 }
map01bf@716 244
map01bf@621 245 /* insert feature data stored in a file */
map01bf@621 246 /* this is a bit gross, */
map01bf@621 247 /* should be replaced eventually by a numpy based feature.*/
map01bf@621 248 /* api call: */
mas01cr@673 249 // struct adb_insert {
map01bf@621 250 // const char *features;
map01bf@621 251 // const char *power;
map01bf@621 252 // const char *key;
map01bf@621 253 // const char *times;
map01bf@621 254 // };
mas01cr@671 255 // int audiodb_insert(adb_t *mydb, adb_insert_t *ins);
map01bf@621 256 PyObject * _pyadb_insertFromFile(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@621 257 {
mas01cr@671 258 adb_t *current_db;
mas01cr@671 259 adb_insert_t *ins;
map01bf@621 260 int ok;
map01bf@621 261 const char *features;
map01bf@621 262 const char *power = NULL;
map01bf@621 263 const char *key = NULL;
map01bf@621 264 const char *times = NULL;
map01bf@621 265 PyObject * incoming = 0;
map01bf@621 266 static char *kwlist[] = { "db", "features", "power", "key", "times" , NULL};
map01bf@621 267
map01bf@621 268 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|sss", kwlist, &incoming, &features, &power, &key, &times);
map01bf@621 269 if (!ok){return NULL;}
map01bf@621 270
mas01cr@671 271 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01cr@671 272 ins = (adb_insert_t *)malloc(sizeof(adb_insert_t));
map01bf@621 273 ins->features = features;
map01bf@621 274 ins->power = power;
map01bf@621 275 ins->key = key;
map01bf@621 276 ins->times = times;
map01bf@622 277 //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times);
map01bf@621 278 ok = audiodb_insert(current_db, ins);
map01bf@622 279 return PyBool_FromLong(ok-1);
map01bf@621 280
map01bf@621 281 }
map01bf@621 282
map01bf@622 283 /* base query. The nomenclature here is about a far away as pythonic as is possible.
map01bf@622 284 * This should be taken care of via the higher level python structure
map01bf@622 285 * returns a dict that should be result ordered and key = result key
map01bf@622 286 * and value is a list of tuples one per result associated with that key, of the form:
map01bf@622 287 * (dist, qpos, ipos)
map01bf@622 288 * Note as well that this is by no means the most efficient way to cast from C, simply the most direct
map01bf@622 289 * and what it lacks in effeciency it gains in python side access. It remains to be seen if this is
map01bf@622 290 * a sensible trade.
map01bf@622 291 * api call:
map01bf@622 292 * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *);
map01bf@622 293 ***/
map01bf@622 294 PyObject * _pyadb_queryFromKey(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@622 295 {
mas01cr@671 296 adb_t *current_db;
map01bf@622 297 adb_query_spec_t *spec;
map01bf@622 298 adb_query_results_t *result;
map01bf@622 299 int ok, exhaustive, falsePositives;
map01bf@622 300 uint32_t i;
map01bf@622 301 const char *key;
map01bf@622 302 const char *accuMode = "db";
map01bf@622 303 const char *distMode = "dot";
map01bf@624 304 const char *resFmt = "dict";
map01bf@622 305 uint32_t hop = 0;
map01bf@622 306 double radius = 0;
map01bf@622 307 double absThres = 0;
map01bf@622 308 double relThres = 0;
map01bf@622 309 double durRatio = 0;
map01bf@622 310 PyObject *includeKeys = NULL;
map01bf@622 311 PyObject *excludeKeys = NULL;
map01bf@622 312 PyObject *incoming = 0;
map01bf@622 313 PyObject *outgoing = NULL;
map01bf@622 314 PyObject *thisKey = NULL;
map01bf@622 315 PyObject *currentValue = 0;
map01bf@622 316 PyObject *newBits = 0;
map01bf@622 317 static char *kwlist[] = { "db", "key",
map01bf@622 318 "seqLength",
map01bf@622 319 "seqStart",
map01bf@622 320 "exhaustive",
map01bf@622 321 "falsePositives",
map01bf@622 322 "accumulation",
map01bf@622 323 "distance",
map01bf@622 324 "npoints",//nearest neighbor points per track
map01bf@624 325 "ntracks",
map01bf@622 326 "includeKeys",
map01bf@622 327 "excludeKeys",
map01bf@622 328 "radius",
map01bf@622 329 "absThres",
map01bf@622 330 "relThres",
map01bf@622 331 "durRatio",
map01bf@624 332 "hopSize",
map01bf@718 333 "resFmt",
map01bf@718 334 NULL
map01bf@622 335 };
map01bf@622 336 spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
map01bf@622 337 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
map01bf@622 338 result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
map01bf@622 339
map01bf@622 340 spec->qid.sequence_length = 16;
map01bf@622 341 spec->qid.sequence_start = 0;
map01bf@622 342 spec->qid.flags = 0;
map01bf@622 343 spec->params.npoints = 1;
map01bf@622 344 spec->params.ntracks = 100;//number of results returned in db mode
map01bf@622 345 spec->refine.flags = 0;
map01bf@622 346
map01bf@624 347 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|iiiissIIOOddddIs", kwlist,
map01bf@622 348 &incoming, &key,
map01bf@622 349 &spec->qid.sequence_length,
map01bf@622 350 &spec->qid.sequence_start,
map01bf@622 351 &exhaustive, &falsePositives,
map01bf@622 352 &accuMode,&distMode,
map01bf@622 353 &spec->params.npoints,
map01bf@622 354 &spec->params.ntracks,
map01bf@622 355 &includeKeys, &excludeKeys,
map01bf@624 356 &radius, &absThres, &relThres, &durRatio, &hop,
map01bf@624 357 &resFmt
map01bf@622 358 );
map01bf@622 359
map01bf@622 360 if (!ok) {return NULL;}
mas01cr@671 361 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@622 362
map01bf@622 363 if (exhaustive){
map01bf@622 364 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
map01bf@622 365 }
map01bf@622 366 if (falsePositives){
map01bf@622 367 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES;
map01bf@622 368 }
map01bf@622 369
map01bf@622 370 //set up spec->params
map01bf@622 371 if (strcmp(accuMode,"db")){
map01bf@622 372 spec->params.accumulation = ADB_ACCUMULATION_DB;
map01bf@622 373 } else if (strcmp(accuMode,"track")){
map01bf@622 374 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
map01bf@622 375 } else if (strcmp(accuMode,"one2one")){
map01bf@622 376 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE;
map01bf@622 377 } else{
map01bf@624 378 PyErr_SetString(PyExc_ValueError,
map01bf@624 379 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n");
map01bf@622 380 return NULL;
map01bf@622 381 }
map01bf@622 382 if (strcmp(distMode, "dot")){
map01bf@622 383 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
map01bf@622 384 }else if (strcmp(distMode, "eucNorm")){
map01bf@622 385 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
map01bf@622 386 }else if (strcmp(distMode, "euclidean")){
map01bf@622 387 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
map01bf@622 388 }else{
map01bf@624 389 PyErr_SetString(PyExc_ValueError,
map01bf@624 390 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' or \'euclidean\'.\n");
map01bf@622 391 return NULL;
map01bf@622 392 }
map01bf@622 393
map01bf@622 394 //set up spec->refine
map01bf@622 395 //include/exclude keys
map01bf@622 396 if (includeKeys){
map01bf@622 397 if (!PyList_Check(includeKeys)){
map01bf@624 398 PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n");
map01bf@622 399 return NULL;
map01bf@622 400 }
map01bf@622 401 spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST;
map01bf@622 402 spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys);
map01bf@622 403 spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys);
map01bf@622 404 for (i=0;i<spec->refine.include.nkeys;i++){
map01bf@622 405 if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){
map01bf@622 406 spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i));
map01bf@622 407 }else{
map01bf@624 408 PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 409 return NULL;
map01bf@622 410 }
map01bf@622 411 }
map01bf@622 412 }
map01bf@622 413 if (excludeKeys){
map01bf@622 414 if (!PyList_Check(excludeKeys)){
map01bf@624 415 PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n");
map01bf@622 416 return NULL;
map01bf@622 417 }
map01bf@622 418 spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST;
map01bf@622 419 spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys);
map01bf@622 420 spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys);
map01bf@622 421 for (i=0;i<spec->refine.exclude.nkeys;i++){
map01bf@622 422 if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){
map01bf@622 423 spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i));
map01bf@622 424 }else{
map01bf@624 425 PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 426 return NULL;
map01bf@622 427 }
map01bf@622 428 }
map01bf@622 429 }
map01bf@622 430 //the rest of spec->refine
map01bf@622 431 if (radius){
map01bf@622 432 spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS;
map01bf@622 433 spec->refine.radius = radius;
map01bf@622 434 }
map01bf@622 435 if (absThres){
map01bf@622 436 spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD;
map01bf@622 437 spec->refine.absolute_threshold = absThres;
map01bf@622 438 }
map01bf@622 439 if (relThres){
map01bf@622 440 spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD;
map01bf@622 441 spec->refine.relative_threshold = relThres;
map01bf@622 442 }
map01bf@622 443 if (durRatio){
map01bf@622 444 spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO;
map01bf@622 445 spec->refine.duration_ratio = durRatio;
map01bf@622 446 }
map01bf@622 447 if (hop){
map01bf@622 448 spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE;
mas01cr@679 449 /* not ideal but a temporary bandage fix */
mas01cr@679 450 spec->refine.qhopsize = hop;
mas01cr@679 451 spec->refine.ihopsize = hop;
map01bf@622 452 }
map01bf@622 453 //setup the datum
map01bf@622 454 spec->qid.datum->data = NULL;
map01bf@622 455 spec->qid.datum->power = NULL;
map01bf@622 456 spec->qid.datum->times = NULL;
map01bf@622 457 //grab the datum from the key
map01bf@622 458 ok = audiodb_retrieve_datum(current_db, key, spec->qid.datum);
map01bf@622 459 if (ok != 0){
map01bf@622 460 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n");
map01bf@622 461 return NULL;
map01bf@622 462 }
map01bf@622 463 result = audiodb_query_spec(current_db, spec);
map01bf@622 464 if (result == NULL){
map01bf@718 465 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
map01bf@622 466 return NULL;
map01bf@622 467 }
map01bf@624 468 if(strcmp(resFmt, "dict")==0){
map01bf@624 469 outgoing = PyDict_New();
map01bf@624 470 for (i=0;i<result->nresults;i++){
mas01cr@672 471 thisKey = PyString_FromString(result->results[i].ikey);
map01bf@624 472 if (!PyDict_Contains(outgoing, thisKey)){
map01bf@624 473 newBits = Py_BuildValue("[(dII)]",
map01bf@624 474 result->results[i].dist,
map01bf@624 475 result->results[i].qpos,
map01bf@624 476 result->results[i].ipos);
map01bf@624 477 if (PyDict_SetItem(outgoing, thisKey,newBits)){
mas01cr@672 478 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 479 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 480 Py_XDECREF(newBits);
map01bf@624 481 return NULL;
map01bf@624 482 }
map01bf@624 483 Py_DECREF(newBits);
map01bf@624 484 }else {
map01bf@624 485 //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 486 currentValue = PyDict_GetItem(outgoing, thisKey);
map01bf@624 487 if (!PyList_Check(currentValue)){
map01bf@624 488 PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n");
map01bf@624 489 return NULL;
map01bf@624 490 }
map01bf@624 491 newBits = Py_BuildValue("dII",result->results[i].dist,
map01bf@624 492 result->results[i].qpos,
map01bf@624 493 result->results[i].ipos);
map01bf@624 494 if (PyList_Append(currentValue, newBits)){
map01bf@624 495 //error msg here
map01bf@624 496 Py_XDECREF(newBits);
map01bf@624 497 return NULL;
map01bf@624 498 }
map01bf@624 499 if (PyDict_SetItem(outgoing, thisKey, newBits)){
map01bf@624 500 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 501 Py_XDECREF(newBits);
map01bf@624 502 return NULL;
map01bf@624 503 }
map01bf@624 504 Py_DECREF(newBits);
map01bf@624 505
map01bf@624 506 }
map01bf@624 507 }
map01bf@624 508 }else if(strcmp(resFmt, "list")==0){
map01bf@624 509 outgoing = PyList_New((Py_ssize_t)0);
map01bf@624 510 for (i=0;i<result->nresults;i++){
mas01cr@672 511 newBits = Py_BuildValue("sdII",result->results[i].ikey,
map01bf@622 512 result->results[i].dist,
map01bf@622 513 result->results[i].qpos,
map01bf@622 514 result->results[i].ipos);
map01bf@624 515 if (PyList_Append(outgoing, newBits)){
map01bf@624 516 //error msg here
map01bf@622 517 Py_XDECREF(newBits);
map01bf@622 518 return NULL;
map01bf@622 519 }
map01bf@622 520 Py_DECREF(newBits);
map01bf@624 521 }
map01bf@624 522 if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward.
map01bf@624 523 printf("the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n");
map01bf@624 524 return NULL;
map01bf@622 525 }
map01bf@624 526 }else{
map01bf@624 527 PyErr_SetString(PyExc_ValueError,
map01bf@624 528 "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n");
map01bf@624 529 return NULL;
map01bf@622 530 }
map01bf@628 531 if (audiodb_query_free_results(current_db, spec, result)){
map01bf@622 532 printf("bit of trouble freeing the result and spec...\ncheck for leaks.");
map01bf@622 533 }
map01bf@622 534
map01bf@622 535 return outgoing;
map01bf@622 536
map01bf@622 537
map01bf@622 538
map01bf@622 539 }
map01bf@622 540
map01bf@622 541
map01bf@622 542
map01bf@622 543
map01bf@620 544 /* close a database */
map01bf@620 545 /* api call: */
mas01cr@671 546 // void audiodb_close(adb_t *db);
map01bf@620 547 static void _pyadb_close(void *ptr)
map01bf@620 548 {
mas01cr@671 549 adb_t *stale_database;
mas01cr@671 550 stale_database = (adb_t *)ptr;
map01bf@620 551
map01bf@620 552 audiodb_close(stale_database);
map01bf@620 553 }
map01bf@620 554
map01bf@620 555 static PyMethodDef _pyadbMethods[] =
map01bf@620 556 {
map01bf@620 557 { "_pyadb_create", _pyadb_create, METH_VARARGS,
map01bf@620 558 "_pyadb_create(string path, unsigned datasize, unsigned ntracks, unsigned datadim)->adb object"},
map01bf@620 559 { "_pyadb_open", _pyadb_open, METH_VARARGS,
map01bf@620 560 "_pyadb_open(string path, [\'r\'|\'w\'])->adb object\nNote that specifing \'w\' opens the file in read and write mode. \
map01bf@620 561 There is currently no way to open in write only."},
map01bf@620 562 { "_pyadb_status", _pyadb_status, METH_VARARGS,
mas01cr@671 563 "_status(adb_t *)->(numFiles, dims, dudCount, nullCount, flags, length, data_region_size)"},
map01bf@620 564 { "_pyadb_l2norm", _pyadb_l2norm, METH_VARARGS,
mas01cr@671 565 "_pyadb_l2norm(adb_t *)->int return code (0 for sucess)"},
map01bf@620 566 { "_pyadb_power", _pyadb_power, METH_VARARGS,
mas01cr@671 567 "_pyadb_power(adb_t *)->int return code (0 for sucess)"},
map01bf@716 568 {"_pyadb_insertFromArray", (PyCFunction)_pyadb_insertFromArray, METH_VARARGS | METH_KEYWORDS,
map01bf@718 569 "_pyadb_insertFromArray(adb_t *, features=ndarray, [power=ndarray | key=keystring | times=ndarray])->\
map01bf@718 570 int return code (0 for sucess)\n\
map01bf@718 571 insert feature data from a numpy array\n\
map01bf@716 572 array given should have ndarray.shape = (numDims*numVectors,)\n\
map01bf@716 573 array datatype needs to be doubles (float may work...)\n\
map01bf@716 574 if power is given, must be 1d array of length numVectors\n\
map01bf@716 575 if times is given, must be 1d array of length 2*numVectors like this:\n\
map01bf@716 576 int audiodb_insert_datum(adb_t *, const adb_datum_t *);"},
map01bf@622 577 { "_pyadb_insertFromFile", (PyCFunction)_pyadb_insertFromFile, METH_VARARGS | METH_KEYWORDS,
mas01cr@671 578 "_pyadb_insertFromFile(adb_t *, features=featureFile, [power=powerfile | key=keystring | times=timingFile])->\
map01bf@621 579 int return code (0 for sucess)"},
map01bf@622 580 { "_pyadb_queryFromKey", (PyCFunction)_pyadb_queryFromKey, METH_VARARGS | METH_KEYWORDS,
map01bf@622 581 "base query. The nomenclature here is about a far away as pythonic as is possible.\n\
map01bf@622 582 This should be taken care of via the higher level python structure\n\
map01bf@622 583 returns a dict that should be result ordered and key = result key\n\
map01bf@622 584 and value is a list of tuples one per result associated with that key, of the form:\n\
map01bf@622 585 \t(dist, qpos, ipos)\n\
map01bf@622 586 Note as well that this is by no means the most efficient way to cast from C, simply the most direct\n\
map01bf@622 587 and what it lacks in effeciency it gains in python side access. It remains to be seen if this is\n\
map01bf@622 588 a sensible trade.\n\
mas01cr@671 589 _pyadb_queryFromKey(adb_t *, query key,\n\
map01bf@622 590 [seqLength = Int Sequence Length, \n\
map01bf@622 591 seqStart = Int offset from start for key, \n\
map01bf@622 592 exhaustive = boolean - True for exhaustive (false by default),\n\
map01bf@622 593 falsePositives= boolean - True to keep fps (false by defaults),\n\
map01bf@622 594 accumulation = [\"db\"|\"track\"|\"one2one\"] (\"db\" by default),\n\
map01bf@622 595 distance = [\"dot\"|\"eucNorm\"|\"euclidean\"] (\"dot\" by default),\n\
map01bf@622 596 npoints = int number of points per track,\n\
map01bf@622 597 ntracks = max number of results returned in db accu mode,\n\
map01bf@622 598 includeKeys = list of strings to include (use all by default),\n\
map01bf@622 599 excludeKeys = list of strings to exclude (none by default),\n\
map01bf@622 600 radius = double of nnRadius (1.0 default, overrides npoints if specified),\n\
map01bf@622 601 absThres = double absolute power threshold (db must have power),\n\
map01bf@622 602 relThres = double relative power threshold (db must have power),\n\
map01bf@622 603 durRatio = double time expansion/compresion ratio,\n\
map01bf@628 604 hopSize = int hopsize (1 by default)])->resultDict\n\
map01bf@628 605 resFmt = [\"list\"|\"dict\"](\"dict\" by default)"},
map01bf@620 606 {NULL,NULL, 0, NULL}
map01bf@620 607 };
map01bf@620 608
map01bf@620 609 void init_pyadb()
map01bf@620 610 {
map01bf@718 611 Py_InitModule3("_pyadb", _pyadbMethods, "internal c bindings for audioDB. Use pyadb for pythonic access to adb.");
map01bf@718 612 import_array();
map01bf@620 613 return;
map01bf@620 614 }
map01bf@620 615
map01bf@620 616