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