map01bf@620: // pyadbmodule.c map01bf@620: // map01bf@620: // the internal portion of the wrapper for audio map01bf@620: // see pyadb.py for the public classes map01bf@620: // map01bf@620: // Created by Benjamin Fields on 2009-09-04. map01bf@716: // Big update for direct data insertion 2010-June (Benjamin Fields) map01bf@716: // Copyleft 2009, 2010 Goldsmith University of London. map01bf@621: // Distributed and licensed under GPL2. See ../../license.txt for details. map01bf@620: // map01bf@620: #include map01bf@621: #include map01bf@620: #include "Python.h" map01bf@622: #include "structmember.h" map01bf@620: #include "audioDB_API.h" map01bf@620: #include "numpy/arrayobject.h" map01bf@620: mas01mc@746: #define ADB_HEADER_FLAG_L2NORM (0x1U) mas01mc@746: #define ADB_HEADER_FLAG_POWER (0x4U) mas01mc@746: #define ADB_HEADER_FLAG_TIMES (0x20U) mas01mc@746: #define ADB_HEADER_FLAG_REFERENCES (0x40U) mas01mc@746: map01bf@620: static void _pyadb_close(void *ptr); map01bf@620: map01bf@620: /* create a new database */ map01bf@620: /* returns a struct or NULL on failure */ map01bf@620: /* api call: */ mas01cr@671: /* adb_t *audiodb_create(const char *path, unsigned datasize, unsigned ntracks, unsigned datadim);*/ map01bf@620: PyObject * _pyadb_create(PyObject *self, PyObject *args) map01bf@620: { map01bf@620: unsigned datasize, ntracks, datadim; map01bf@620: const char *path; map01bf@620: int ok; mas01cr@671: adb_t *new_database; map01bf@620: ok = PyArg_ParseTuple(args, "sIII", &path, &datasize, &ntracks, &datadim); map01bf@620: if (!ok) return 0; map01bf@620: new_database = audiodb_create(path, datasize, ntracks, datadim); map01bf@620: if (!new_database) return 0; map01bf@620: mas01mc@746: return PyCObject_FromVoidPtr( new_database, _pyadb_close); map01bf@620: } map01bf@620: map01bf@620: /* open an existing database */ map01bf@620: /* returns a struct or NULL on failure */ map01bf@620: /* flags expects fcntl flags concerning the opening mode */ map01bf@620: /* api call: */ mas01cr@671: // adb_t *audiodb_open(const char *path, int flags); map01bf@620: PyObject * _pyadb_open(PyObject *self, PyObject *args) map01bf@620: { map01bf@620: const char *path; map01bf@620: char mode; map01bf@620: int ok;//in python layer need to translate boolean flags to byte mask mas01cr@671: adb_t *fresh_database; map01bf@620: ok = PyArg_ParseTuple(args, "sc", &path, &mode); map01bf@620: if (!ok) return 0; map01bf@620: if (mode == 'r'){ map01bf@620: fresh_database = audiodb_open(path, O_RDONLY); map01bf@620: }else if (mode == 'w'){ map01bf@620: fresh_database = audiodb_open(path, O_RDWR); map01bf@620: }else{ map01bf@620: PyErr_SetString(PyExc_ValueError, map01bf@620: "mode must be either \'r\' or \'w\'. It appears to be something else."); map01bf@620: return 0; map01bf@620: } map01bf@620: if (!fresh_database) return 0; map01bf@620: map01bf@620: return PyCObject_FromVoidPtr( fresh_database, _pyadb_close); map01bf@620: } map01bf@620: map01bf@620: /* database status */ map01bf@620: /* api call: */ mas01cr@671: // int audiodb_status(adb_t *mydb, adb_status_ptr status); map01bf@620: PyObject * _pyadb_status(PyObject *self, PyObject *args) map01bf@620: { mas01cr@671: adb_t *check_db; mas01cr@671: adb_status_t *status; map01bf@620: int flags, ok; map01bf@620: PyObject * incoming = 0; mas01cr@671: status = (adb_status_t *)malloc(sizeof(adb_status_t)); map01bf@620: map01bf@620: ok = PyArg_ParseTuple(args, "O", &incoming); map01bf@620: if (!ok) return 0; mas01cr@671: check_db = (adb_t *)PyCObject_AsVoidPtr(incoming); map01bf@620: map01bf@620: map01bf@620: flags = audiodb_status(check_db, status); map01bf@620: return Py_BuildValue("IIIIILL", status->numFiles, map01bf@620: status->dim, map01bf@620: status->dudCount, map01bf@620: status->nullCount, map01bf@620: status->flags, map01bf@620: status->length, map01bf@620: status->data_region_size); map01bf@620: map01bf@620: } map01bf@620: map01bf@620: /*engage l2norm in the referenced db*/ map01bf@620: /*api call:*/ mas01cr@671: //int audiodb_l2norm(adb_t *mydb); map01bf@620: PyObject * _pyadb_l2norm(PyObject *self, PyObject *args) map01bf@620: { mas01cr@671: adb_t *current_db; map01bf@620: int ok; map01bf@620: PyObject * incoming = 0; map01bf@620: map01bf@620: ok = PyArg_ParseTuple(args, "O", &incoming); map01bf@620: if (!ok) return 0; mas01cr@671: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); map01bf@620: map01bf@620: ok = audiodb_l2norm(current_db); map01bf@622: return PyBool_FromLong(ok-1); map01bf@620: map01bf@620: } map01bf@620: map01bf@620: /*engage power thresholding in the referenced db*/ map01bf@620: /*api call:*/ mas01cr@671: // int audiodb_power(adb_t *mydb); map01bf@620: PyObject * _pyadb_power(PyObject *self, PyObject *args) map01bf@620: { mas01cr@671: adb_t *current_db; map01bf@620: int ok; map01bf@620: PyObject * incoming = 0; map01bf@620: map01bf@620: ok = PyArg_ParseTuple(args, "O", &incoming); map01bf@620: if (!ok) return 0; mas01cr@671: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); map01bf@620: map01bf@620: ok = audiodb_power(current_db); map01bf@622: return PyBool_FromLong(ok-1); map01bf@620: map01bf@620: } map01bf@716: map01bf@716: /* insert feature data from a numpy array */ map01bf@720: /* array given should have ndarray.shape = (numVectors, numDims)*/ map01bf@716: /* array datatype needs to be doubles (float may work...)*/ map01bf@716: /* if power is given, must be 1d array of length numVectors*/ map01bf@716: /* if times is given, must be 1d array of length 2*numVectors like this:*/ map01bf@716: map01bf@716: /* api call: */ map01bf@716: // typedef struct adb_datum { map01bf@716: // uint32_t nvectors; map01bf@716: // uint32_t dim; map01bf@716: // const char *key; map01bf@716: // double *data; map01bf@716: // double *power; map01bf@716: // double *times; map01bf@716: // } adb_datum_t; map01bf@716: // int audiodb_insert_datum(adb_t *, const adb_datum_t *); map01bf@716: PyObject * _pyadb_insertFromArray(PyObject *self, PyObject *args, PyObject *keywds) map01bf@716: { map01bf@716: adb_t *current_db; map01bf@716: adb_status_t *status; map01bf@716: adb_datum_t *ins; map01bf@716: int ok; map01bf@716: npy_intp dims[1]; map01bf@716: unsigned int nDims = 0; map01bf@716: unsigned int nVect = 0; map01bf@716: PyObject *incoming = 0; map01bf@718: PyArrayObject *features = 0; map01bf@718: PyArrayObject *power = 0; map01bf@718: PyArrayObject *times = 0; map01bf@716: const char *key = NULL; map01bf@716: PyArray_Descr *descr; map01bf@716: static char *kwlist[] = { "db", "features", "nDim", "nVect", "power", "key", "times" , NULL}; map01bf@716: map01bf@718: ok = PyArg_ParseTupleAndKeywords(args, keywds, "OO!II|O!sO!", kwlist, &incoming, &PyArray_Type, &features, &nDims, &nVect, &PyArray_Type, &power, &key, &PyArray_Type, ×); mas01mc@743: if (!ok){ mas01mc@743: PyErr_SetString(PyExc_TypeError, "Failed at PyArg_ParseTupleAndKeywords"); mas01mc@743: return NULL; mas01mc@743: } map01bf@716: //check our arrays map01bf@718: // if (!PyArray_Check(features)){ map01bf@718: // PyErr_SetString(PyExc_TypeError, "features must be a numpy array (of floats or doubles)"); map01bf@718: // return NULL; map01bf@718: // } map01bf@716: if (!PyArray_ISFLOAT(features)){ map01bf@716: PyErr_SetString(PyExc_TypeError, "features numpy array must contain floats or doubles"); map01bf@716: return NULL; map01bf@716: } map01bf@716: if ((PyArray_NDIM(features) != 1) || (PyArray_DIMS(features)[0] != (nDims * nVect))){ map01bf@716: PyErr_SetString(PyExc_TypeError, "features numpy array must be flattened before call."); map01bf@716: return NULL; map01bf@716: } map01bf@716: descr = PyArray_DescrFromType(NPY_DOUBLE); map01bf@716: map01bf@716: if (power){ map01bf@716: if (!PyArray_Check(power)){ map01bf@716: PyErr_SetString(PyExc_TypeError, "power, if given, must be a numpy array (of floats or doubles)"); map01bf@716: return NULL; map01bf@716: } map01bf@716: if (!PyArray_ISFLOAT(power)){ map01bf@716: PyErr_SetString(PyExc_TypeError, "power numpy array, if given, must contain floats or doubles"); map01bf@716: return NULL; map01bf@716: } map01bf@716: // power = (PyArrayObject *)PyCObject_AsVoidPtr(incomingPow); mas01mc@743: if (PyArray_NDIM(features) != 1 || PyArray_DIMS(power)[0] != nVect){ map01bf@716: PyErr_SetString(PyExc_ValueError, "power, if given must be a 1d numpy array with shape = (numVectors,)"); map01bf@716: return NULL; map01bf@716: } map01bf@716: } map01bf@716: if (times){ map01bf@716: if (!PyArray_Check(times)){ map01bf@716: PyErr_SetString(PyExc_TypeError, "times, if given, must be a numpy array (of floats or doubles)"); map01bf@716: return NULL; map01bf@716: } map01bf@716: if (!PyArray_ISFLOAT(times)){ map01bf@716: PyErr_SetString(PyExc_TypeError, "times numpy array, if given, must contain floats or doubles"); map01bf@716: return NULL; map01bf@716: } map01bf@716: // times = (PyArrayObject *)PyCObject_AsVoidPtr(incomingTime); mas01mc@749: if (PyArray_NDIM(times) != 1 || PyArray_DIMS(times)[0] != (nVect*2)){ mas01mc@749: PyErr_SetString(PyExc_ValueError, "times, if given must be a 1d numpy array with shape = (numVectors*2,)"); map01bf@716: return NULL; map01bf@716: } map01bf@716: } map01bf@716: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); map01bf@716: status = (adb_status_t *)malloc(sizeof(adb_status_t)); map01bf@716: //verify that the data to be inserted is the correct size for the database. map01bf@716: map01bf@716: ins = (adb_datum_t *)malloc(sizeof(adb_datum_t)); mas01mc@758: if (PyArray_AsCArray((PyObject**)&features, &(ins->data), dims, 1, descr)){ map01bf@716: PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the feature np array as a C array."); map01bf@716: return NULL; map01bf@716: } map01bf@716: map01bf@718: if (power){ mas01mc@758: if (PyArray_AsCArray((PyObject**)&power, &(ins->power), dims, 1, descr)){ map01bf@716: PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the power np array as a C array."); map01bf@716: return NULL; map01bf@716: } map01bf@719: }else{ map01bf@719: ins->power=NULL; map01bf@716: } map01bf@716: map01bf@718: if (times){ mas01mc@758: if (PyArray_AsCArray((PyObject**)×, &(ins->times), dims, 1, descr)){ map01bf@716: PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the times np array as a C array."); map01bf@716: return NULL; map01bf@716: } map01bf@719: }else{ map01bf@719: ins->times=NULL; map01bf@716: } map01bf@716: ins->key = key; map01bf@716: ins->nvectors = (uint32_t)nVect; map01bf@716: ins->dim = (uint32_t)nDims; map01bf@716: //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times); map01bf@716: ok = audiodb_insert_datum(current_db, ins);//(current_db, ins); map01bf@719: return PyInt_FromLong(ok); map01bf@716: map01bf@716: } map01bf@716: map01bf@621: /* insert feature data stored in a file */ map01bf@621: /* this is a bit gross, */ map01bf@621: /* should be replaced eventually by a numpy based feature.*/ map01bf@621: /* api call: */ mas01cr@673: // struct adb_insert { map01bf@621: // const char *features; map01bf@621: // const char *power; map01bf@621: // const char *key; map01bf@621: // const char *times; map01bf@621: // }; mas01cr@671: // int audiodb_insert(adb_t *mydb, adb_insert_t *ins); map01bf@621: PyObject * _pyadb_insertFromFile(PyObject *self, PyObject *args, PyObject *keywds) map01bf@621: { mas01cr@671: adb_t *current_db; mas01cr@671: adb_insert_t *ins; map01bf@621: int ok; map01bf@621: const char *features; map01bf@621: const char *power = NULL; map01bf@621: const char *key = NULL; map01bf@621: const char *times = NULL; map01bf@621: PyObject * incoming = 0; map01bf@621: static char *kwlist[] = { "db", "features", "power", "key", "times" , NULL}; map01bf@621: map01bf@621: ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|sss", kwlist, &incoming, &features, &power, &key, ×); map01bf@621: if (!ok){return NULL;} map01bf@621: mas01cr@671: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); mas01cr@671: ins = (adb_insert_t *)malloc(sizeof(adb_insert_t)); map01bf@621: ins->features = features; map01bf@621: ins->power = power; map01bf@621: ins->key = key; map01bf@621: ins->times = times; map01bf@622: //printf("features::%s\npower::%s\nkey::%s\ntimes::%s\n", ins->features, ins->power, ins->key, ins->times); map01bf@621: ok = audiodb_insert(current_db, ins); map01bf@622: return PyBool_FromLong(ok-1); map01bf@621: map01bf@621: } map01bf@621: mas01mc@744: /* liszt - list strings, sizes, and time-points of all database entries mas01mc@744: * mas01mc@744: */ mas01mc@744: PyObject* _pyadb_liszt(PyObject *self, PyObject *args) mas01mc@744: { mas01mc@744: adb_t *current_db; mas01mc@744: int ok,i; mas01mc@744: PyObject * incoming = NULL; mas01mc@744: PyObject * outgoing = NULL; mas01mc@744: PyObject * newBits = NULL; mas01mc@744: mas01mc@744: ok = PyArg_ParseTuple(args, "O", &incoming); mas01mc@744: mas01mc@744: if (!ok) return 0; mas01mc@744: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); mas01mc@744: mas01mc@744: adb_liszt_results_t *liszt = audiodb_liszt(current_db); mas01mc@744: mas01mc@744: outgoing = PyList_New((Py_ssize_t)0); mas01mc@744: for (i=0 ; inresults ; i++){ mas01mc@744: newBits = Py_BuildValue("sI",liszt->entries[i].key,liszt->entries[i].nvectors); mas01mc@744: if (PyList_Append(outgoing, newBits)){ mas01mc@744: //error msg here mas01mc@744: Py_XDECREF(newBits); mas01mc@744: return NULL; mas01mc@744: } mas01mc@744: Py_DECREF(newBits); mas01mc@744: } mas01mc@744: audiodb_liszt_free_results(current_db, liszt); mas01mc@744: return outgoing; mas01mc@744: } mas01mc@744: map01bf@622: /* base query. The nomenclature here is about a far away as pythonic as is possible. map01bf@622: * This should be taken care of via the higher level python structure map01bf@622: * returns a dict that should be result ordered and key = result key map01bf@622: * and value is a list of tuples one per result associated with that key, of the form: map01bf@622: * (dist, qpos, ipos) map01bf@622: * Note as well that this is by no means the most efficient way to cast from C, simply the most direct map01bf@622: * and what it lacks in effeciency it gains in python side access. It remains to be seen if this is map01bf@622: * a sensible trade. map01bf@622: * api call: map01bf@622: * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *); map01bf@622: ***/ map01bf@622: PyObject * _pyadb_queryFromKey(PyObject *self, PyObject *args, PyObject *keywds) map01bf@622: { mas01cr@671: adb_t *current_db; map01bf@622: adb_query_spec_t *spec; map01bf@622: adb_query_results_t *result; map01bf@622: int ok, exhaustive, falsePositives; map01bf@622: uint32_t i; map01bf@622: const char *key; map01bf@622: const char *accuMode = "db"; map01bf@622: const char *distMode = "dot"; map01bf@624: const char *resFmt = "dict"; map01bf@622: uint32_t hop = 0; map01bf@622: double radius = 0; map01bf@622: double absThres = 0; map01bf@622: double relThres = 0; map01bf@622: double durRatio = 0; map01bf@622: PyObject *includeKeys = NULL; map01bf@622: PyObject *excludeKeys = NULL; map01bf@622: PyObject *incoming = 0; map01bf@622: PyObject *outgoing = NULL; map01bf@622: PyObject *thisKey = NULL; map01bf@622: PyObject *currentValue = 0; map01bf@622: PyObject *newBits = 0; map01bf@622: static char *kwlist[] = { "db", "key", map01bf@622: "seqLength", map01bf@622: "seqStart", map01bf@622: "exhaustive", map01bf@622: "falsePositives", map01bf@622: "accumulation", map01bf@622: "distance", map01bf@622: "npoints",//nearest neighbor points per track map01bf@624: "ntracks", map01bf@622: "includeKeys", map01bf@622: "excludeKeys", map01bf@622: "radius", map01bf@622: "absThres", map01bf@622: "relThres", map01bf@622: "durRatio", map01bf@624: "hopSize", map01bf@718: "resFmt", map01bf@718: NULL map01bf@622: }; map01bf@622: spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t)); map01bf@622: spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t)); map01bf@622: result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t)); map01bf@622: map01bf@622: spec->qid.sequence_length = 16; map01bf@622: spec->qid.sequence_start = 0; map01bf@622: spec->qid.flags = 0; map01bf@622: spec->params.npoints = 1; map01bf@622: spec->params.ntracks = 100;//number of results returned in db mode map01bf@622: spec->refine.flags = 0; map01bf@622: map01bf@624: ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|iiiissIIOOddddIs", kwlist, map01bf@622: &incoming, &key, map01bf@622: &spec->qid.sequence_length, map01bf@622: &spec->qid.sequence_start, map01bf@622: &exhaustive, &falsePositives, map01bf@622: &accuMode,&distMode, map01bf@622: &spec->params.npoints, map01bf@622: &spec->params.ntracks, map01bf@622: &includeKeys, &excludeKeys, map01bf@624: &radius, &absThres, &relThres, &durRatio, &hop, map01bf@624: &resFmt map01bf@622: ); map01bf@622: map01bf@622: if (!ok) {return NULL;} mas01cr@671: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); map01bf@622: map01bf@622: if (exhaustive){ map01bf@622: spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE; map01bf@622: } map01bf@622: if (falsePositives){ map01bf@622: spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES; map01bf@622: } map01bf@622: map01bf@622: //set up spec->params mas01mc@750: if (strcmp(accuMode,"db")==0){ map01bf@622: spec->params.accumulation = ADB_ACCUMULATION_DB; mas01mc@750: } else if (strcmp(accuMode,"track")==0){ map01bf@622: spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK; mas01mc@750: } else if (strcmp(accuMode,"one2one")==0){ map01bf@622: spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE; map01bf@622: } else{ map01bf@624: PyErr_SetString(PyExc_ValueError, map01bf@624: "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n"); map01bf@622: return NULL; map01bf@622: } mas01mc@750: if (strcmp(distMode, "dot")==0){ mas01mc@768: spec->params.distance = ADB_DISTANCE_DOT_PRODUCT; mas01mc@750: }else if (strcmp(distMode, "eucNorm")==0){ mas01mc@768: spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED; mas01mc@750: }else if (strcmp(distMode, "euclidean")==0){ mas01mc@768: spec->params.distance = ADB_DISTANCE_EUCLIDEAN; mas01mc@768: }else if (strcmp(distMode, "kullback")==0){ mas01mc@768: spec->params.distance = ADB_DISTANCE_KULLBACK_LEIBLER_DIVERGENCE; map01bf@622: }else{ mas01mc@768: PyErr_SetString(PyExc_ValueError, mas01mc@768: "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' ,\'euclidean\' or \'kullback\'.\n"); mas01mc@768: return NULL; map01bf@622: } map01bf@622: map01bf@622: //set up spec->refine map01bf@622: //include/exclude keys map01bf@622: if (includeKeys){ map01bf@622: if (!PyList_Check(includeKeys)){ map01bf@624: PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n"); map01bf@622: return NULL; map01bf@622: } map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST; map01bf@622: spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys); map01bf@622: spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys); map01bf@622: for (i=0;irefine.include.nkeys;i++){ map01bf@622: if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){ map01bf@622: spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i)); map01bf@622: }else{ map01bf@624: PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n"); map01bf@622: return NULL; map01bf@622: } map01bf@622: } map01bf@622: } map01bf@622: if (excludeKeys){ map01bf@622: if (!PyList_Check(excludeKeys)){ map01bf@624: PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n"); map01bf@622: return NULL; map01bf@622: } map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST; map01bf@622: spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys); map01bf@622: spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys); map01bf@622: for (i=0;irefine.exclude.nkeys;i++){ map01bf@622: if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){ map01bf@622: spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i)); map01bf@622: }else{ map01bf@624: PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n"); map01bf@622: return NULL; map01bf@622: } map01bf@622: } map01bf@622: } map01bf@622: //the rest of spec->refine map01bf@622: if (radius){ map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS; map01bf@622: spec->refine.radius = radius; map01bf@622: } map01bf@622: if (absThres){ map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD; map01bf@622: spec->refine.absolute_threshold = absThres; map01bf@622: } map01bf@622: if (relThres){ map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD; map01bf@622: spec->refine.relative_threshold = relThres; map01bf@622: } map01bf@622: if (durRatio){ map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO; map01bf@622: spec->refine.duration_ratio = durRatio; map01bf@622: } map01bf@622: if (hop){ map01bf@622: spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE; mas01cr@679: /* not ideal but a temporary bandage fix */ mas01cr@679: spec->refine.qhopsize = hop; mas01cr@679: spec->refine.ihopsize = hop; map01bf@622: } map01bf@622: //setup the datum map01bf@622: spec->qid.datum->data = NULL; map01bf@622: spec->qid.datum->power = NULL; map01bf@622: spec->qid.datum->times = NULL; map01bf@622: //grab the datum from the key map01bf@622: ok = audiodb_retrieve_datum(current_db, key, spec->qid.datum); map01bf@622: if (ok != 0){ map01bf@622: PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n"); map01bf@622: return NULL; map01bf@622: } map01bf@622: result = audiodb_query_spec(current_db, spec); map01bf@622: if (result == NULL){ map01bf@718: PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n"); map01bf@622: return NULL; map01bf@622: } map01bf@624: if(strcmp(resFmt, "dict")==0){ map01bf@624: outgoing = PyDict_New(); map01bf@624: for (i=0;inresults;i++){ mas01cr@672: thisKey = PyString_FromString(result->results[i].ikey); map01bf@624: if (!PyDict_Contains(outgoing, thisKey)){ map01bf@624: newBits = Py_BuildValue("[(dII)]", map01bf@624: result->results[i].dist, map01bf@624: result->results[i].qpos, map01bf@624: result->results[i].ipos); map01bf@624: if (PyDict_SetItem(outgoing, thisKey,newBits)){ mas01cr@672: 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: PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n"); map01bf@624: Py_XDECREF(newBits); map01bf@624: return NULL; map01bf@624: } map01bf@624: Py_DECREF(newBits); map01bf@624: }else { map01bf@624: //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: currentValue = PyDict_GetItem(outgoing, thisKey); map01bf@624: if (!PyList_Check(currentValue)){ map01bf@624: PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n"); map01bf@624: return NULL; map01bf@624: } map01bf@624: newBits = Py_BuildValue("dII",result->results[i].dist, map01bf@624: result->results[i].qpos, map01bf@624: result->results[i].ipos); map01bf@624: if (PyList_Append(currentValue, newBits)){ map01bf@624: //error msg here map01bf@624: Py_XDECREF(newBits); map01bf@624: return NULL; map01bf@624: } map01bf@624: if (PyDict_SetItem(outgoing, thisKey, newBits)){ map01bf@624: PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n"); map01bf@624: Py_XDECREF(newBits); map01bf@624: return NULL; map01bf@624: } map01bf@624: Py_DECREF(newBits); map01bf@624: map01bf@624: } map01bf@624: } map01bf@624: }else if(strcmp(resFmt, "list")==0){ map01bf@624: outgoing = PyList_New((Py_ssize_t)0); map01bf@624: for (i=0;inresults;i++){ mas01cr@672: newBits = Py_BuildValue("sdII",result->results[i].ikey, map01bf@622: result->results[i].dist, map01bf@622: result->results[i].qpos, map01bf@622: result->results[i].ipos); map01bf@624: if (PyList_Append(outgoing, newBits)){ map01bf@624: //error msg here map01bf@622: Py_XDECREF(newBits); map01bf@622: return NULL; map01bf@622: } map01bf@622: Py_DECREF(newBits); map01bf@624: } map01bf@624: if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward. map01bf@720: PyErr_SetString(PyExc_RuntimeError, map01bf@720: "the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n"); map01bf@624: return NULL; map01bf@622: } map01bf@624: }else{ map01bf@624: PyErr_SetString(PyExc_ValueError, map01bf@624: "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n"); map01bf@624: return NULL; map01bf@622: } map01bf@628: if (audiodb_query_free_results(current_db, spec, result)){ map01bf@622: printf("bit of trouble freeing the result and spec...\ncheck for leaks."); map01bf@622: } map01bf@622: map01bf@622: return outgoing; map01bf@622: map01bf@622: map01bf@622: map01bf@622: } map01bf@622: mas01mc@750: /* Data query. mas01mc@750: * Returns a dict that is result ordered and key = result key mas01mc@750: * value is a list of tuples one per result associated with that key, of the form: mas01mc@750: * (dist, qpos, ipos) mas01mc@750: * api call: mas01mc@750: * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *); mas01mc@750: ***/ mas01mc@750: PyObject * _pyadb_queryFromData(PyObject *self, PyObject *args, PyObject *keywds) mas01mc@750: { mas01mc@750: adb_t *current_db; mas01mc@750: adb_query_spec_t *spec; mas01mc@750: adb_query_results_t *result; mas01mc@750: int ok, exhaustive, falsePositives; mas01mc@750: uint32_t i; mas01mc@750: const char *accuMode = "db"; mas01mc@750: const char *distMode = "dot"; mas01mc@750: const char *resFmt = "dict"; mas01mc@750: uint32_t hop = 0; mas01mc@750: double radius = 0; mas01mc@750: double absThres = 0; mas01mc@750: double relThres = 0; mas01mc@750: double durRatio = 0; mas01mc@750: PyObject *includeKeys = NULL; mas01mc@750: PyObject *excludeKeys = NULL; mas01mc@750: PyObject *incoming = NULL; mas01mc@750: PyObject *outgoing = NULL; mas01mc@750: PyObject *thisKey = NULL; mas01mc@750: PyObject *currentValue = NULL; mas01mc@750: PyObject *newBits = NULL; mas01mc@750: unsigned int nDims = 0; mas01mc@750: unsigned int nVect = 0; mas01mc@750: PyArrayObject *features = NULL; mas01mc@750: PyArrayObject *power = NULL; mas01mc@750: PyArrayObject *times = NULL; mas01mc@750: adb_status_t *status; mas01mc@750: mas01mc@750: static char *kwlist[] = { "db", "features", mas01mc@750: "seqLength", mas01mc@750: "seqStart", mas01mc@750: "exhaustive", mas01mc@750: "falsePositives", mas01mc@750: "accumulation", mas01mc@750: "distance", mas01mc@750: "npoints",//nearest neighbor points per track mas01mc@750: "ntracks", mas01mc@750: "includeKeys", mas01mc@750: "excludeKeys", mas01mc@750: "radius", mas01mc@750: "absThres", mas01mc@750: "relThres", mas01mc@750: "durRatio", mas01mc@750: "hopSize", mas01mc@750: "resFmt", mas01mc@750: "power", mas01mc@750: "times", mas01mc@750: NULL mas01mc@750: }; mas01mc@750: mas01mc@750: spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t)); mas01mc@750: spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t)); mas01mc@750: result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t)); mas01mc@750: mas01mc@750: spec->qid.sequence_length = 16; mas01mc@750: spec->qid.sequence_start = 0; mas01mc@750: spec->qid.flags = 0; mas01mc@750: spec->params.npoints = 1; mas01mc@750: spec->params.ntracks = 100;//number of results returned in db mode mas01mc@750: spec->refine.flags = 0; mas01mc@750: mas01mc@750: ok = PyArg_ParseTupleAndKeywords(args, keywds, "OO!|iiiissIIOOddddIsO!O!", kwlist, mas01mc@750: &incoming, &PyArray_Type, &features, mas01mc@750: &spec->qid.sequence_length, mas01mc@750: &spec->qid.sequence_start, mas01mc@750: &exhaustive, &falsePositives, mas01mc@750: &accuMode,&distMode, mas01mc@750: &spec->params.npoints, mas01mc@750: &spec->params.ntracks, mas01mc@750: &includeKeys, &excludeKeys, mas01mc@750: &radius, &absThres, &relThres, &durRatio, &hop, mas01mc@750: &resFmt, mas01mc@750: &PyArray_Type, &power, &PyArray_Type, × mas01mc@750: ); mas01mc@750: mas01mc@750: if (!ok) {return NULL;} mas01mc@750: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); mas01mc@750: mas01mc@750: if (!features){ /* Sanity Check */ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: function requires feature data as numpy ndarray. PythonC required keyword check failed.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: /* Check the dimensionality of passed data agrees with the passed database */ mas01mc@750: if(PyArray_NDIM(features)!=2){ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: passed features have incorrect shape, should be (nVecs, nDims).\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: mas01mc@750: if(power && PyArray_NDIM(power)!=1){ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: passed power have incorrect shape, should be (nVecs,).\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: if(times && PyArray_NDIM(times)!=1){ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: passed times have incorrect shape, should be (nVecs,).\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: status = (adb_status_t*) malloc(sizeof(adb_status_t)); mas01mc@750: int errtest = audiodb_status(current_db, status); mas01mc@750: if(errtest){ mas01mc@750: PyErr_SetString(PyExc_TypeError, "queryFromData failed: could not get status of passed ADB database"); mas01mc@750: free(status); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: if(!PyArray_DIMS(features)[1]==status->dim){ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: passed features have incorrect dimensionality.\n"); mas01mc@750: free(status); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: if(power && PyArray_DIMS(power)[0] != PyArray_DIMS(features)[0]){ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: passed power and features have incompatible nVecs dimension.\n"); mas01mc@750: free(status); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: if(times && PyArray_DIMS(times)[0] != PyArray_DIMS(features)[0]){ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "queryFromData: passed times and features have incompatible nVecs dimension.\n"); mas01mc@750: free(status); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: free(status); mas01mc@750: mas01mc@750: if (exhaustive){ mas01mc@750: spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE; mas01mc@750: } mas01mc@750: if (falsePositives){ mas01mc@750: spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES; mas01mc@750: } mas01mc@750: mas01mc@750: //set up spec->params mas01mc@750: if (strcmp(accuMode,"db")==0){ mas01mc@750: spec->params.accumulation = ADB_ACCUMULATION_DB; mas01mc@750: } else if (strcmp(accuMode,"track")==0){ mas01mc@750: spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK; mas01mc@750: } else if (strcmp(accuMode,"one2one")==0){ mas01mc@750: spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE; mas01mc@750: } else{ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: if (strcmp(distMode, "dot")==0){ mas01mc@750: spec->params.distance = ADB_DISTANCE_DOT_PRODUCT; mas01mc@750: }else if (strcmp(distMode, "eucNorm")==0){ mas01mc@750: spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED; mas01mc@750: }else if (strcmp(distMode, "euclidean")==0){ mas01mc@750: spec->params.distance = ADB_DISTANCE_EUCLIDEAN; mas01mc@768: }else if (strcmp(distMode, "kullback")==0){ mas01mc@768: spec->params.distance = ADB_DISTANCE_KULLBACK_LEIBLER_DIVERGENCE; mas01mc@750: }else{ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@768: "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' ,\'euclidean\' or \'kullback\'.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: mas01mc@750: //set up spec->refine mas01mc@750: //include/exclude keys mas01mc@750: if (includeKeys){ mas01mc@750: if (!PyList_Check(includeKeys)){ mas01mc@750: PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST; mas01mc@750: spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys); mas01mc@750: spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys); mas01mc@750: for (i=0;irefine.include.nkeys;i++){ mas01mc@750: if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){ mas01mc@750: spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i)); mas01mc@750: }else{ mas01mc@750: PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: } mas01mc@750: } mas01mc@750: if (excludeKeys){ mas01mc@750: if (!PyList_Check(excludeKeys)){ mas01mc@750: PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST; mas01mc@750: spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys); mas01mc@750: spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys); mas01mc@750: for (i=0;irefine.exclude.nkeys;i++){ mas01mc@750: if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){ mas01mc@750: spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i)); mas01mc@750: }else{ mas01mc@750: PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: } mas01mc@750: } mas01mc@750: //the rest of spec->refine mas01mc@750: if (radius){ mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS; mas01mc@750: spec->refine.radius = radius; mas01mc@750: } mas01mc@750: if (absThres){ mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD; mas01mc@750: spec->refine.absolute_threshold = absThres; mas01mc@750: } mas01mc@750: if (relThres){ mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD; mas01mc@750: spec->refine.relative_threshold = relThres; mas01mc@750: } mas01mc@750: if (durRatio){ mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO; mas01mc@750: spec->refine.duration_ratio = durRatio; mas01mc@750: } mas01mc@750: if (hop){ mas01mc@750: spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE; mas01mc@750: /* not ideal but a temporary bandage fix */ mas01mc@750: spec->refine.qhopsize = hop; mas01mc@750: spec->refine.ihopsize = hop; mas01mc@750: } mas01mc@750: mas01mc@758: spec->qid.datum->data = (double*) features->data; mas01mc@750: mas01mc@750: if (power){ mas01mc@758: spec->qid.datum->power = (double*) power->data; mas01mc@750: }else{ mas01mc@750: spec->qid.datum->power=NULL; mas01mc@750: } mas01mc@750: mas01mc@750: if (times){ mas01mc@758: spec->qid.datum->times = (double*) times->data; mas01mc@750: }else{ mas01mc@750: spec->qid.datum->times=NULL; mas01mc@750: } mas01mc@750: mas01mc@750: nVect = PyArray_DIMS(features)[0]; mas01mc@750: nDims = PyArray_DIMS(features)[1]; mas01mc@750: spec->qid.datum->nvectors = (uint32_t)nVect; mas01mc@750: spec->qid.datum->dim = (uint32_t)nDims; mas01mc@750: mas01mc@750: result = audiodb_query_spec(current_db, spec); mas01mc@750: mas01mc@750: if (result == NULL){ mas01mc@750: PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: if(strcmp(resFmt, "dict")==0){ mas01mc@750: outgoing = PyDict_New(); mas01mc@750: for (i=0;inresults;i++){ mas01mc@750: thisKey = PyString_FromString(result->results[i].ikey); mas01mc@750: if (!PyDict_Contains(outgoing, thisKey)){ mas01mc@750: newBits = Py_BuildValue("[(dII)]", mas01mc@750: result->results[i].dist, mas01mc@750: result->results[i].qpos, mas01mc@750: result->results[i].ipos); mas01mc@750: if (PyDict_SetItem(outgoing, thisKey,newBits)){ mas01mc@750: 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: PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n"); mas01mc@750: Py_XDECREF(newBits); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: Py_DECREF(newBits); mas01mc@750: }else { mas01mc@750: //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: currentValue = PyDict_GetItem(outgoing, thisKey); mas01mc@750: if (!PyList_Check(currentValue)){ mas01mc@750: PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: newBits = Py_BuildValue("dII",result->results[i].dist, mas01mc@750: result->results[i].qpos, mas01mc@750: result->results[i].ipos); mas01mc@750: if (PyList_Append(currentValue, newBits)){ mas01mc@750: //error msg here mas01mc@750: Py_XDECREF(newBits); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: if (PyDict_SetItem(outgoing, thisKey, newBits)){ mas01mc@750: PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n"); mas01mc@750: Py_XDECREF(newBits); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: Py_DECREF(newBits); mas01mc@750: mas01mc@750: } mas01mc@750: } mas01mc@750: }else if(strcmp(resFmt, "list")==0){ mas01mc@750: outgoing = PyList_New((Py_ssize_t)0); mas01mc@750: for (i=0;inresults;i++){ mas01mc@750: newBits = Py_BuildValue("sdII",result->results[i].ikey, mas01mc@750: result->results[i].dist, mas01mc@750: result->results[i].qpos, mas01mc@750: result->results[i].ipos); mas01mc@750: if (PyList_Append(outgoing, newBits)){ mas01mc@750: //error msg here mas01mc@750: Py_XDECREF(newBits); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: Py_DECREF(newBits); mas01mc@750: } mas01mc@750: if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward. mas01mc@750: PyErr_SetString(PyExc_RuntimeError, mas01mc@750: "the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: }else{ mas01mc@750: PyErr_SetString(PyExc_ValueError, mas01mc@750: "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n"); mas01mc@750: return NULL; mas01mc@750: } mas01mc@750: if (audiodb_query_free_results(current_db, spec, result)){ mas01mc@750: printf("bit of trouble freeing the result and spec...\ncheck for leaks."); mas01mc@750: } mas01mc@750: mas01mc@750: return outgoing; mas01mc@750: mas01mc@750: mas01mc@750: mas01mc@750: } mas01mc@750: map01bf@622: mas01mc@746: /* retrieval of inserted data mas01mc@746: * returned numpy array has ndarray.shape = (numVectors, numDims) mas01mc@746: * array datatype needs to be doubles (float may work...) mas01mc@746: * if power reqeusted, it will be a 1d array of length numVectors mas01mc@746: * if times are requested, they will be a 1d array of length 2*nvectors mas01mc@746: */ mas01mc@746: mas01mc@746: // api call: mas01mc@746: // typedef struct adb_datum { mas01mc@746: // uint32_t nvectors; mas01mc@746: // uint32_t dim; mas01mc@746: // const char *key; mas01mc@746: // double *data; mas01mc@746: // double *power; mas01mc@746: // double *times; mas01mc@746: // } adb_datum_t; mas01mc@746: mas01mc@746: //int audiodb_retrieve_datum(adb_t *, const char *, adb_datum_t *); mas01mc@746: //int audiodb_free_datum(adb_t *, adb_datum_t *); mas01mc@746: PyObject * _pyadb_retrieveDatum(PyObject *self, PyObject *args, PyObject *keywds) mas01mc@746: { mas01mc@746: adb_t *current_db = NULL; mas01mc@746: adb_status_t *status = NULL; mas01mc@746: adb_datum_t *ins = NULL; mas01mc@746: int ok=0, errtest=0; mas01mc@746: unsigned features=0, powers=0, times=0; mas01mc@746: PyObject *incoming = 0; // The ADB database mas01mc@746: PyObject *outgoing = 0; // The PyArrayObject mas01mc@746: const char *key = NULL; mas01mc@746: static char *kwlist[] = { "db", "key", "features", "powers", "times", NULL}; mas01mc@758: double * data = NULL; mas01mc@746: int dims = 0; mas01mc@746: npy_intp shape[2] = { 0, 0 }; mas01mc@746: mas01mc@746: ok = PyArg_ParseTupleAndKeywords(args, keywds, "Os|III", kwlist, &incoming, &key, &features, &powers, ×); mas01mc@746: if (!ok){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed at PyArg_ParseTupleAndKeywords"); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: mas01mc@746: if(features+powers+times>1){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed: you must specify only one of features, powers, or times"); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: mas01mc@746: if(!(features||powers||times)){ mas01mc@746: features=1; // default is to return features mas01mc@746: } mas01mc@746: mas01mc@746: current_db = (adb_t *)PyCObject_AsVoidPtr(incoming); mas01mc@746: if (!current_db){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed to convert open database to C-pointer"); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: status = (adb_status_t*) malloc(sizeof(adb_status_t)); mas01mc@746: errtest = audiodb_status(current_db, status); mas01mc@746: if(errtest){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed: could not get status of passed ADB database"); mas01mc@746: free(status); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: mas01mc@746: if(powers && !(status->flags&ADB_HEADER_FLAG_POWER)){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed: powers requested but passed ADB database has no powers"); mas01mc@746: free(status); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: mas01mc@746: if(times && !(status->flags&ADB_HEADER_FLAG_TIMES)){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed: times requested but passed ADB database has no times"); mas01mc@746: free(status); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: mas01mc@746: ins = (adb_datum_t *)malloc(sizeof(adb_datum_t)); mas01mc@746: errtest = audiodb_retrieve_datum(current_db, key, ins); // retrieve data from adb via key mas01mc@746: if (errtest){ mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed to retrieve datum"); mas01mc@746: free(ins); mas01mc@746: return NULL; mas01mc@746: } mas01mc@746: mas01mc@746: if(features){ mas01mc@746: if(ins->dim>1){ mas01mc@746: dims=2; mas01mc@751: shape[1]= ins->dim; mas01mc@746: } mas01mc@746: else{ mas01mc@746: dims=1; mas01mc@746: } mas01mc@746: shape[0]= ins->nvectors; mas01mc@746: data = ins->data; mas01mc@746: } mas01mc@746: else if(powers){ mas01mc@746: dims=1; mas01mc@746: shape[0]= ins->nvectors; mas01mc@746: data = ins->power; mas01mc@746: } mas01mc@746: else if(times){ mas01mc@746: dims=1; mas01mc@746: shape[0]= 2 * ins->nvectors; mas01mc@746: data = ins->times; mas01mc@746: } mas01mc@746: mas01mc@748: outgoing = PyArray_SimpleNew(dims, shape, NPY_DOUBLE); mas01mc@746: if (!outgoing){ mas01mc@748: free(status); mas01mc@748: free(ins); // free the malloced adb_datum_t structure though mas01mc@748: Py_XDECREF(outgoing); mas01mc@746: PyErr_SetString(PyExc_TypeError, "Failed to convert retrieved datum to C-Array"); mas01mc@746: return NULL; mas01mc@748: } mas01mc@747: mas01mc@748: /* Copy the data, this allows us to free the allocated memory and let mas01mc@748: * python do the subsequent garbage collection itself. mas01mc@748: */ mas01mc@751: int num_items = ins->nvectors; mas01mc@751: if(dims>1){ mas01mc@751: num_items *= shape[1]; mas01mc@751: } mas01mc@748: double* p = (double*) PyArray_DATA(outgoing); mas01mc@748: double* d = data; mas01mc@748: while(num_items--) mas01mc@748: *p++ = *d++; mas01mc@748: audiodb_free_datum(current_db, ins); // free the source audiodb_datum mas01mc@748: free(status); // free the malloced status object mas01mc@748: free(ins); // free the malloced adb_datum_t structure though mas01mc@746: return outgoing; mas01mc@746: } map01bf@622: map01bf@622: map01bf@620: /* close a database */ map01bf@620: /* api call: */ mas01cr@671: // void audiodb_close(adb_t *db); map01bf@620: static void _pyadb_close(void *ptr) map01bf@620: { mas01cr@671: adb_t *stale_database; mas01cr@671: stale_database = (adb_t *)ptr; map01bf@620: map01bf@620: audiodb_close(stale_database); map01bf@620: } map01bf@620: map01bf@620: static PyMethodDef _pyadbMethods[] = map01bf@620: { map01bf@620: { "_pyadb_create", _pyadb_create, METH_VARARGS, map01bf@620: "_pyadb_create(string path, unsigned datasize, unsigned ntracks, unsigned datadim)->adb object"}, map01bf@620: { "_pyadb_open", _pyadb_open, METH_VARARGS, map01bf@620: "_pyadb_open(string path, [\'r\'|\'w\'])->adb object\nNote that specifing \'w\' opens the file in read and write mode. \ map01bf@620: There is currently no way to open in write only."}, map01bf@620: { "_pyadb_status", _pyadb_status, METH_VARARGS, mas01cr@671: "_status(adb_t *)->(numFiles, dims, dudCount, nullCount, flags, length, data_region_size)"}, map01bf@620: { "_pyadb_l2norm", _pyadb_l2norm, METH_VARARGS, mas01cr@671: "_pyadb_l2norm(adb_t *)->int return code (0 for sucess)"}, map01bf@620: { "_pyadb_power", _pyadb_power, METH_VARARGS, mas01cr@671: "_pyadb_power(adb_t *)->int return code (0 for sucess)"}, map01bf@716: {"_pyadb_insertFromArray", (PyCFunction)_pyadb_insertFromArray, METH_VARARGS | METH_KEYWORDS, map01bf@718: "_pyadb_insertFromArray(adb_t *, features=ndarray, [power=ndarray | key=keystring | times=ndarray])->\ map01bf@718: int return code (0 for sucess)\n\ map01bf@718: insert feature data from a numpy array\n\ map01bf@716: array given should have ndarray.shape = (numDims*numVectors,)\n\ map01bf@716: array datatype needs to be doubles (float may work...)\n\ map01bf@716: if power is given, must be 1d array of length numVectors\n\ map01bf@716: if times is given, must be 1d array of length 2*numVectors like this:\n\ map01bf@716: int audiodb_insert_datum(adb_t *, const adb_datum_t *);"}, mas01mc@746: {"_pyadb_retrieveDatum", (PyCFunction)_pyadb_retrieveDatum, METH_VARARGS | METH_KEYWORDS, "_pyadb_retrieveDatum(adb_t *, key=keystring"}, map01bf@622: { "_pyadb_insertFromFile", (PyCFunction)_pyadb_insertFromFile, METH_VARARGS | METH_KEYWORDS, mas01cr@671: "_pyadb_insertFromFile(adb_t *, features=featureFile, [power=powerfile | key=keystring | times=timingFile])->\ map01bf@621: int return code (0 for sucess)"}, mas01mc@744: { "_pyadb_liszt", (PyCFunction)_pyadb_liszt, METH_VARARGS, mas01mc@744: "_pyadb_liszt(adb_t*)->[[key1,numvecs1],[key2,numvecs2]...]"}, map01bf@622: { "_pyadb_queryFromKey", (PyCFunction)_pyadb_queryFromKey, METH_VARARGS | METH_KEYWORDS, map01bf@622: "base query. The nomenclature here is about a far away as pythonic as is possible.\n\ map01bf@622: This should be taken care of via the higher level python structure\n\ map01bf@622: returns a dict that should be result ordered and key = result key\n\ map01bf@622: and value is a list of tuples one per result associated with that key, of the form:\n\ map01bf@622: \t(dist, qpos, ipos)\n\ map01bf@622: Note as well that this is by no means the most efficient way to cast from C, simply the most direct\n\ map01bf@622: and what it lacks in effeciency it gains in python side access. It remains to be seen if this is\n\ map01bf@622: a sensible trade.\n\ mas01cr@671: _pyadb_queryFromKey(adb_t *, query key,\n\ map01bf@622: [seqLength = Int Sequence Length, \n\ map01bf@622: seqStart = Int offset from start for key, \n\ map01bf@622: exhaustive = boolean - True for exhaustive (false by default),\n\ map01bf@622: falsePositives= boolean - True to keep fps (false by defaults),\n\ map01bf@622: accumulation = [\"db\"|\"track\"|\"one2one\"] (\"db\" by default),\n\ map01bf@622: distance = [\"dot\"|\"eucNorm\"|\"euclidean\"] (\"dot\" by default),\n\ map01bf@622: npoints = int number of points per track,\n\ map01bf@622: ntracks = max number of results returned in db accu mode,\n\ map01bf@622: includeKeys = list of strings to include (use all by default),\n\ map01bf@622: excludeKeys = list of strings to exclude (none by default),\n\ map01bf@622: radius = double of nnRadius (1.0 default, overrides npoints if specified),\n\ map01bf@622: absThres = double absolute power threshold (db must have power),\n\ map01bf@622: relThres = double relative power threshold (db must have power),\n\ map01bf@622: durRatio = double time expansion/compresion ratio,\n\ map01bf@628: hopSize = int hopsize (1 by default)])->resultDict\n\ map01bf@628: resFmt = [\"list\"|\"dict\"](\"dict\" by default)"}, mas01mc@750: {"_pyadb_queryFromData", (PyCFunction)_pyadb_queryFromData, METH_VARARGS | METH_KEYWORDS, mas01mc@750: "data query. Required features=F (numpy ndarray). Optional: power=P (numpy 1d array), times=T (numpy 1d array)"}, map01bf@620: {NULL,NULL, 0, NULL} map01bf@620: }; map01bf@620: mas01mc@758: void init_pyadb(void) map01bf@620: { map01bf@718: Py_InitModule3("_pyadb", _pyadbMethods, "internal c bindings for audioDB. Use pyadb for pythonic access to adb."); map01bf@718: import_array(); map01bf@620: return; map01bf@620: } map01bf@620: map01bf@620: