annotate bindings/python/pyadbmodule.c @ 718:14568e432e73

segfaults are gone, but the new bits still aren't quite behaving correctly. Also, some weird behavior when the query result is blank
author map01bf
date Thu, 24 Jun 2010 16:38:32 +0000
parents 159becb0701e
children e3f1cf653c30
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@718 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@718 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@716 224 }
map01bf@716 225
map01bf@718 226 if (times){
map01bf@718 227 if (!PyArray_AsCArray(&times, &(ins->times), dims, 1, descr)){
map01bf@716 228 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the times np array as a C array.");
map01bf@716 229 return NULL;
map01bf@716 230 }
map01bf@716 231 }
map01bf@716 232 ins->key = key;
map01bf@716 233 ins->nvectors = (uint32_t)nVect;
map01bf@716 234 ins->dim = (uint32_t)nDims;
map01bf@716 235 //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times);
map01bf@716 236 ok = audiodb_insert_datum(current_db, ins);//(current_db, ins);
map01bf@716 237 return PyBool_FromLong(ok-1);
map01bf@716 238
map01bf@716 239 }
map01bf@716 240
map01bf@621 241 /* insert feature data stored in a file */
map01bf@621 242 /* this is a bit gross, */
map01bf@621 243 /* should be replaced eventually by a numpy based feature.*/
map01bf@621 244 /* api call: */
mas01cr@673 245 // struct adb_insert {
map01bf@621 246 // const char *features;
map01bf@621 247 // const char *power;
map01bf@621 248 // const char *key;
map01bf@621 249 // const char *times;
map01bf@621 250 // };
mas01cr@671 251 // int audiodb_insert(adb_t *mydb, adb_insert_t *ins);
map01bf@621 252 PyObject * _pyadb_insertFromFile(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@621 253 {
mas01cr@671 254 adb_t *current_db;
mas01cr@671 255 adb_insert_t *ins;
map01bf@621 256 int ok;
map01bf@621 257 const char *features;
map01bf@621 258 const char *power = NULL;
map01bf@621 259 const char *key = NULL;
map01bf@621 260 const char *times = NULL;
map01bf@621 261 PyObject * incoming = 0;
map01bf@621 262 static char *kwlist[] = { "db", "features", "power", "key", "times" , NULL};
map01bf@621 263
map01bf@621 264 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|sss", kwlist, &incoming, &features, &power, &key, &times);
map01bf@621 265 if (!ok){return NULL;}
map01bf@621 266
mas01cr@671 267 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
mas01cr@671 268 ins = (adb_insert_t *)malloc(sizeof(adb_insert_t));
map01bf@621 269 ins->features = features;
map01bf@621 270 ins->power = power;
map01bf@621 271 ins->key = key;
map01bf@621 272 ins->times = times;
map01bf@622 273 //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times);
map01bf@621 274 ok = audiodb_insert(current_db, ins);
map01bf@622 275 return PyBool_FromLong(ok-1);
map01bf@621 276
map01bf@621 277 }
map01bf@621 278
map01bf@622 279 /* base query. The nomenclature here is about a far away as pythonic as is possible.
map01bf@622 280 * This should be taken care of via the higher level python structure
map01bf@622 281 * returns a dict that should be result ordered and key = result key
map01bf@622 282 * and value is a list of tuples one per result associated with that key, of the form:
map01bf@622 283 * (dist, qpos, ipos)
map01bf@622 284 * Note as well that this is by no means the most efficient way to cast from C, simply the most direct
map01bf@622 285 * and what it lacks in effeciency it gains in python side access. It remains to be seen if this is
map01bf@622 286 * a sensible trade.
map01bf@622 287 * api call:
map01bf@622 288 * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *);
map01bf@622 289 ***/
map01bf@622 290 PyObject * _pyadb_queryFromKey(PyObject *self, PyObject *args, PyObject *keywds)
map01bf@622 291 {
mas01cr@671 292 adb_t *current_db;
map01bf@622 293 adb_query_spec_t *spec;
map01bf@622 294 adb_query_results_t *result;
map01bf@622 295 int ok, exhaustive, falsePositives;
map01bf@622 296 uint32_t i;
map01bf@622 297 const char *key;
map01bf@622 298 const char *accuMode = "db";
map01bf@622 299 const char *distMode = "dot";
map01bf@624 300 const char *resFmt = "dict";
map01bf@622 301 uint32_t hop = 0;
map01bf@622 302 double radius = 0;
map01bf@622 303 double absThres = 0;
map01bf@622 304 double relThres = 0;
map01bf@622 305 double durRatio = 0;
map01bf@622 306 PyObject *includeKeys = NULL;
map01bf@622 307 PyObject *excludeKeys = NULL;
map01bf@622 308 PyObject *incoming = 0;
map01bf@622 309 PyObject *outgoing = NULL;
map01bf@622 310 PyObject *thisKey = NULL;
map01bf@622 311 PyObject *currentValue = 0;
map01bf@622 312 PyObject *newBits = 0;
map01bf@622 313 static char *kwlist[] = { "db", "key",
map01bf@622 314 "seqLength",
map01bf@622 315 "seqStart",
map01bf@622 316 "exhaustive",
map01bf@622 317 "falsePositives",
map01bf@622 318 "accumulation",
map01bf@622 319 "distance",
map01bf@622 320 "npoints",//nearest neighbor points per track
map01bf@624 321 "ntracks",
map01bf@622 322 "includeKeys",
map01bf@622 323 "excludeKeys",
map01bf@622 324 "radius",
map01bf@622 325 "absThres",
map01bf@622 326 "relThres",
map01bf@622 327 "durRatio",
map01bf@624 328 "hopSize",
map01bf@718 329 "resFmt",
map01bf@718 330 NULL
map01bf@622 331 };
map01bf@622 332 spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
map01bf@622 333 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
map01bf@622 334 result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
map01bf@622 335
map01bf@622 336 spec->qid.sequence_length = 16;
map01bf@622 337 spec->qid.sequence_start = 0;
map01bf@622 338 spec->qid.flags = 0;
map01bf@622 339 spec->params.npoints = 1;
map01bf@622 340 spec->params.ntracks = 100;//number of results returned in db mode
map01bf@622 341 spec->refine.flags = 0;
map01bf@622 342
map01bf@624 343 ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|iiiissIIOOddddIs", kwlist,
map01bf@622 344 &incoming, &key,
map01bf@622 345 &spec->qid.sequence_length,
map01bf@622 346 &spec->qid.sequence_start,
map01bf@622 347 &exhaustive, &falsePositives,
map01bf@622 348 &accuMode,&distMode,
map01bf@622 349 &spec->params.npoints,
map01bf@622 350 &spec->params.ntracks,
map01bf@622 351 &includeKeys, &excludeKeys,
map01bf@624 352 &radius, &absThres, &relThres, &durRatio, &hop,
map01bf@624 353 &resFmt
map01bf@622 354 );
map01bf@622 355
map01bf@622 356 if (!ok) {return NULL;}
mas01cr@671 357 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
map01bf@622 358
map01bf@622 359 if (exhaustive){
map01bf@622 360 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
map01bf@622 361 }
map01bf@622 362 if (falsePositives){
map01bf@622 363 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES;
map01bf@622 364 }
map01bf@622 365
map01bf@622 366 //set up spec->params
map01bf@622 367 if (strcmp(accuMode,"db")){
map01bf@622 368 spec->params.accumulation = ADB_ACCUMULATION_DB;
map01bf@622 369 } else if (strcmp(accuMode,"track")){
map01bf@622 370 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
map01bf@622 371 } else if (strcmp(accuMode,"one2one")){
map01bf@622 372 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE;
map01bf@622 373 } else{
map01bf@624 374 PyErr_SetString(PyExc_ValueError,
map01bf@624 375 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n");
map01bf@622 376 return NULL;
map01bf@622 377 }
map01bf@622 378 if (strcmp(distMode, "dot")){
map01bf@622 379 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
map01bf@622 380 }else if (strcmp(distMode, "eucNorm")){
map01bf@622 381 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
map01bf@622 382 }else if (strcmp(distMode, "euclidean")){
map01bf@622 383 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
map01bf@622 384 }else{
map01bf@624 385 PyErr_SetString(PyExc_ValueError,
map01bf@624 386 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' or \'euclidean\'.\n");
map01bf@622 387 return NULL;
map01bf@622 388 }
map01bf@622 389
map01bf@622 390 //set up spec->refine
map01bf@622 391 //include/exclude keys
map01bf@622 392 if (includeKeys){
map01bf@622 393 if (!PyList_Check(includeKeys)){
map01bf@624 394 PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n");
map01bf@622 395 return NULL;
map01bf@622 396 }
map01bf@622 397 spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST;
map01bf@622 398 spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys);
map01bf@622 399 spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys);
map01bf@622 400 for (i=0;i<spec->refine.include.nkeys;i++){
map01bf@622 401 if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){
map01bf@622 402 spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i));
map01bf@622 403 }else{
map01bf@624 404 PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 405 return NULL;
map01bf@622 406 }
map01bf@622 407 }
map01bf@622 408 }
map01bf@622 409 if (excludeKeys){
map01bf@622 410 if (!PyList_Check(excludeKeys)){
map01bf@624 411 PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n");
map01bf@622 412 return NULL;
map01bf@622 413 }
map01bf@622 414 spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST;
map01bf@622 415 spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys);
map01bf@622 416 spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys);
map01bf@622 417 for (i=0;i<spec->refine.exclude.nkeys;i++){
map01bf@622 418 if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){
map01bf@622 419 spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i));
map01bf@622 420 }else{
map01bf@624 421 PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n");
map01bf@622 422 return NULL;
map01bf@622 423 }
map01bf@622 424 }
map01bf@622 425 }
map01bf@622 426 //the rest of spec->refine
map01bf@622 427 if (radius){
map01bf@622 428 spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS;
map01bf@622 429 spec->refine.radius = radius;
map01bf@622 430 }
map01bf@622 431 if (absThres){
map01bf@622 432 spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD;
map01bf@622 433 spec->refine.absolute_threshold = absThres;
map01bf@622 434 }
map01bf@622 435 if (relThres){
map01bf@622 436 spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD;
map01bf@622 437 spec->refine.relative_threshold = relThres;
map01bf@622 438 }
map01bf@622 439 if (durRatio){
map01bf@622 440 spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO;
map01bf@622 441 spec->refine.duration_ratio = durRatio;
map01bf@622 442 }
map01bf@622 443 if (hop){
map01bf@622 444 spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE;
mas01cr@679 445 /* not ideal but a temporary bandage fix */
mas01cr@679 446 spec->refine.qhopsize = hop;
mas01cr@679 447 spec->refine.ihopsize = hop;
map01bf@622 448 }
map01bf@622 449 //setup the datum
map01bf@622 450 spec->qid.datum->data = NULL;
map01bf@622 451 spec->qid.datum->power = NULL;
map01bf@622 452 spec->qid.datum->times = NULL;
map01bf@622 453 //grab the datum from the key
map01bf@622 454 ok = audiodb_retrieve_datum(current_db, key, spec->qid.datum);
map01bf@622 455 if (ok != 0){
map01bf@622 456 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n");
map01bf@622 457 return NULL;
map01bf@622 458 }
map01bf@622 459 result = audiodb_query_spec(current_db, spec);
map01bf@622 460 if (result == NULL){
map01bf@718 461 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
map01bf@622 462 return NULL;
map01bf@622 463 }
map01bf@624 464 if(strcmp(resFmt, "dict")==0){
map01bf@624 465 outgoing = PyDict_New();
map01bf@624 466 for (i=0;i<result->nresults;i++){
mas01cr@672 467 thisKey = PyString_FromString(result->results[i].ikey);
map01bf@624 468 if (!PyDict_Contains(outgoing, thisKey)){
map01bf@624 469 newBits = Py_BuildValue("[(dII)]",
map01bf@624 470 result->results[i].dist,
map01bf@624 471 result->results[i].qpos,
map01bf@624 472 result->results[i].ipos);
map01bf@624 473 if (PyDict_SetItem(outgoing, thisKey,newBits)){
mas01cr@672 474 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 475 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 476 Py_XDECREF(newBits);
map01bf@624 477 return NULL;
map01bf@624 478 }
map01bf@624 479 Py_DECREF(newBits);
map01bf@624 480 }else {
map01bf@624 481 //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 482 currentValue = PyDict_GetItem(outgoing, thisKey);
map01bf@624 483 if (!PyList_Check(currentValue)){
map01bf@624 484 PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n");
map01bf@624 485 return NULL;
map01bf@624 486 }
map01bf@624 487 newBits = Py_BuildValue("dII",result->results[i].dist,
map01bf@624 488 result->results[i].qpos,
map01bf@624 489 result->results[i].ipos);
map01bf@624 490 if (PyList_Append(currentValue, newBits)){
map01bf@624 491 //error msg here
map01bf@624 492 Py_XDECREF(newBits);
map01bf@624 493 return NULL;
map01bf@624 494 }
map01bf@624 495 if (PyDict_SetItem(outgoing, thisKey, newBits)){
map01bf@624 496 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
map01bf@624 497 Py_XDECREF(newBits);
map01bf@624 498 return NULL;
map01bf@624 499 }
map01bf@624 500 Py_DECREF(newBits);
map01bf@624 501
map01bf@624 502 }
map01bf@624 503 }
map01bf@624 504 }else if(strcmp(resFmt, "list")==0){
map01bf@624 505 outgoing = PyList_New((Py_ssize_t)0);
map01bf@624 506 for (i=0;i<result->nresults;i++){
mas01cr@672 507 newBits = Py_BuildValue("sdII",result->results[i].ikey,
map01bf@622 508 result->results[i].dist,
map01bf@622 509 result->results[i].qpos,
map01bf@622 510 result->results[i].ipos);
map01bf@624 511 if (PyList_Append(outgoing, newBits)){
map01bf@624 512 //error msg here
map01bf@622 513 Py_XDECREF(newBits);
map01bf@622 514 return NULL;
map01bf@622 515 }
map01bf@622 516 Py_DECREF(newBits);
map01bf@624 517 }
map01bf@624 518 if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward.
map01bf@624 519 printf("the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n");
map01bf@624 520 return NULL;
map01bf@622 521 }
map01bf@624 522 }else{
map01bf@624 523 PyErr_SetString(PyExc_ValueError,
map01bf@624 524 "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n");
map01bf@624 525 return NULL;
map01bf@622 526 }
map01bf@628 527 if (audiodb_query_free_results(current_db, spec, result)){
map01bf@622 528 printf("bit of trouble freeing the result and spec...\ncheck for leaks.");
map01bf@622 529 }
map01bf@622 530
map01bf@622 531 return outgoing;
map01bf@622 532
map01bf@622 533
map01bf@622 534
map01bf@622 535 }
map01bf@622 536
map01bf@622 537
map01bf@622 538
map01bf@622 539
map01bf@620 540 /* close a database */
map01bf@620 541 /* api call: */
mas01cr@671 542 // void audiodb_close(adb_t *db);
map01bf@620 543 static void _pyadb_close(void *ptr)
map01bf@620 544 {
mas01cr@671 545 adb_t *stale_database;
mas01cr@671 546 stale_database = (adb_t *)ptr;
map01bf@620 547
map01bf@620 548 audiodb_close(stale_database);
map01bf@620 549 }
map01bf@620 550
map01bf@620 551 static PyMethodDef _pyadbMethods[] =
map01bf@620 552 {
map01bf@620 553 { "_pyadb_create", _pyadb_create, METH_VARARGS,
map01bf@620 554 "_pyadb_create(string path, unsigned datasize, unsigned ntracks, unsigned datadim)->adb object"},
map01bf@620 555 { "_pyadb_open", _pyadb_open, METH_VARARGS,
map01bf@620 556 "_pyadb_open(string path, [\'r\'|\'w\'])->adb object\nNote that specifing \'w\' opens the file in read and write mode. \
map01bf@620 557 There is currently no way to open in write only."},
map01bf@620 558 { "_pyadb_status", _pyadb_status, METH_VARARGS,
mas01cr@671 559 "_status(adb_t *)->(numFiles, dims, dudCount, nullCount, flags, length, data_region_size)"},
map01bf@620 560 { "_pyadb_l2norm", _pyadb_l2norm, METH_VARARGS,
mas01cr@671 561 "_pyadb_l2norm(adb_t *)->int return code (0 for sucess)"},
map01bf@620 562 { "_pyadb_power", _pyadb_power, METH_VARARGS,
mas01cr@671 563 "_pyadb_power(adb_t *)->int return code (0 for sucess)"},
map01bf@716 564 {"_pyadb_insertFromArray", (PyCFunction)_pyadb_insertFromArray, METH_VARARGS | METH_KEYWORDS,
map01bf@718 565 "_pyadb_insertFromArray(adb_t *, features=ndarray, [power=ndarray | key=keystring | times=ndarray])->\
map01bf@718 566 int return code (0 for sucess)\n\
map01bf@718 567 insert feature data from a numpy array\n\
map01bf@716 568 array given should have ndarray.shape = (numDims*numVectors,)\n\
map01bf@716 569 array datatype needs to be doubles (float may work...)\n\
map01bf@716 570 if power is given, must be 1d array of length numVectors\n\
map01bf@716 571 if times is given, must be 1d array of length 2*numVectors like this:\n\
map01bf@716 572 int audiodb_insert_datum(adb_t *, const adb_datum_t *);"},
map01bf@622 573 { "_pyadb_insertFromFile", (PyCFunction)_pyadb_insertFromFile, METH_VARARGS | METH_KEYWORDS,
mas01cr@671 574 "_pyadb_insertFromFile(adb_t *, features=featureFile, [power=powerfile | key=keystring | times=timingFile])->\
map01bf@621 575 int return code (0 for sucess)"},
map01bf@622 576 { "_pyadb_queryFromKey", (PyCFunction)_pyadb_queryFromKey, METH_VARARGS | METH_KEYWORDS,
map01bf@622 577 "base query. The nomenclature here is about a far away as pythonic as is possible.\n\
map01bf@622 578 This should be taken care of via the higher level python structure\n\
map01bf@622 579 returns a dict that should be result ordered and key = result key\n\
map01bf@622 580 and value is a list of tuples one per result associated with that key, of the form:\n\
map01bf@622 581 \t(dist, qpos, ipos)\n\
map01bf@622 582 Note as well that this is by no means the most efficient way to cast from C, simply the most direct\n\
map01bf@622 583 and what it lacks in effeciency it gains in python side access. It remains to be seen if this is\n\
map01bf@622 584 a sensible trade.\n\
mas01cr@671 585 _pyadb_queryFromKey(adb_t *, query key,\n\
map01bf@622 586 [seqLength = Int Sequence Length, \n\
map01bf@622 587 seqStart = Int offset from start for key, \n\
map01bf@622 588 exhaustive = boolean - True for exhaustive (false by default),\n\
map01bf@622 589 falsePositives= boolean - True to keep fps (false by defaults),\n\
map01bf@622 590 accumulation = [\"db\"|\"track\"|\"one2one\"] (\"db\" by default),\n\
map01bf@622 591 distance = [\"dot\"|\"eucNorm\"|\"euclidean\"] (\"dot\" by default),\n\
map01bf@622 592 npoints = int number of points per track,\n\
map01bf@622 593 ntracks = max number of results returned in db accu mode,\n\
map01bf@622 594 includeKeys = list of strings to include (use all by default),\n\
map01bf@622 595 excludeKeys = list of strings to exclude (none by default),\n\
map01bf@622 596 radius = double of nnRadius (1.0 default, overrides npoints if specified),\n\
map01bf@622 597 absThres = double absolute power threshold (db must have power),\n\
map01bf@622 598 relThres = double relative power threshold (db must have power),\n\
map01bf@622 599 durRatio = double time expansion/compresion ratio,\n\
map01bf@628 600 hopSize = int hopsize (1 by default)])->resultDict\n\
map01bf@628 601 resFmt = [\"list\"|\"dict\"](\"dict\" by default)"},
map01bf@620 602 {NULL,NULL, 0, NULL}
map01bf@620 603 };
map01bf@620 604
map01bf@620 605 void init_pyadb()
map01bf@620 606 {
map01bf@718 607 Py_InitModule3("_pyadb", _pyadbMethods, "internal c bindings for audioDB. Use pyadb for pythonic access to adb.");
map01bf@718 608 import_array();
map01bf@620 609 return;
map01bf@620 610 }
map01bf@620 611
map01bf@620 612