Mercurial > hg > audiodb
comparison query.cpp @ 239:2cc06e5b05a5
Merge refactoring branch.
Bug fixes:
* 64-bit powertable bug;
* -inf - -inf bug;
* use new times information;
* plus short track, O2_MAXFILES and structure padding ABI fixes (already
backported)
Major code changes:
* split source into functional units, known as 'files';
* Reporter class for accumulating and reporting on query results;
* much OAOOization, mostly from above: net 800 LOC (25%) shorter.
author | mas01cr |
---|---|
date | Thu, 13 Dec 2007 14:23:32 +0000 |
parents | |
children | 4eb4608e28e1 |
comparison
equal
deleted
inserted
replaced
224:3a81da6fb1d7 | 239:2cc06e5b05a5 |
---|---|
1 #include "audioDB.h" | |
2 | |
3 #include "reporter.h" | |
4 | |
5 bool audioDB::powers_acceptable(double p1, double p2) { | |
6 if (use_absolute_threshold) { | |
7 if ((p1 < absolute_threshold) || (p2 < absolute_threshold)) { | |
8 return false; | |
9 } | |
10 } | |
11 if (use_relative_threshold) { | |
12 if (fabs(p1-p2) > fabs(relative_threshold)) { | |
13 return false; | |
14 } | |
15 } | |
16 return true; | |
17 } | |
18 | |
19 void audioDB::query(const char* dbName, const char* inFile, adb__queryResponse *adbQueryResponse) { | |
20 initTables(dbName, inFile); | |
21 Reporter *r = 0; | |
22 switch (queryType) { | |
23 case O2_POINT_QUERY: | |
24 sequenceLength = 1; | |
25 normalizedDistance = false; | |
26 r = new pointQueryReporter<std::greater < NNresult > >(pointNN); | |
27 break; | |
28 case O2_TRACK_QUERY: | |
29 sequenceLength = 1; | |
30 normalizedDistance = false; | |
31 r = new trackAveragingReporter<std::greater < NNresult > >(pointNN, trackNN, dbH->numFiles); | |
32 break; | |
33 case O2_SEQUENCE_QUERY: | |
34 if(radius == 0) { | |
35 r = new trackAveragingReporter<std::less < NNresult > >(pointNN, trackNN, dbH->numFiles); | |
36 } else { | |
37 r = new trackSequenceQueryRadReporter(trackNN, dbH->numFiles); | |
38 } | |
39 break; | |
40 default: | |
41 error("unrecognized queryType in query()"); | |
42 } | |
43 trackSequenceQueryNN(dbName, inFile, r); | |
44 r->report(fileTable, adbQueryResponse); | |
45 delete r; | |
46 } | |
47 | |
48 // return ordinal position of key in keyTable | |
49 unsigned audioDB::getKeyPos(char* key){ | |
50 for(unsigned k=0; k<dbH->numFiles; k++) | |
51 if(strncmp(fileTable + k*O2_FILETABLESIZE, key, strlen(key))==0) | |
52 return k; | |
53 error("Key not found",key); | |
54 return O2_ERR_KEYNOTFOUND; | |
55 } | |
56 | |
57 // This is a common pattern in sequence queries: what we are doing is | |
58 // taking a window of length seqlen over a buffer of length length, | |
59 // and placing the sum of the elements in that window in the first | |
60 // element of the window: thus replacing all but the last seqlen | |
61 // elements in the buffer with the corresponding windowed sum. | |
62 void audioDB::sequence_sum(double *buffer, int length, int seqlen) { | |
63 double tmp1, tmp2, *ps; | |
64 int j, w; | |
65 | |
66 tmp1 = *buffer; | |
67 j = 1; | |
68 w = seqlen - 1; | |
69 while(w--) { | |
70 *buffer += buffer[j++]; | |
71 } | |
72 ps = buffer + 1; | |
73 w = length - seqlen; // +1 - 1 | |
74 while(w--) { | |
75 tmp2 = *ps; | |
76 if(isfinite(tmp1)) { | |
77 *ps = *(ps - 1) - tmp1 + *(ps + seqlen - 1); | |
78 } else { | |
79 for(int i = 1; i < seqlen; i++) { | |
80 *ps += *(ps + i); | |
81 } | |
82 } | |
83 tmp1 = tmp2; | |
84 ps++; | |
85 } | |
86 } | |
87 | |
88 // In contrast to sequence_sum() above, sequence_sqrt() and | |
89 // sequence_average() below are simple mappers across the sequence. | |
90 void audioDB::sequence_sqrt(double *buffer, int length, int seqlen) { | |
91 int w = length - seqlen + 1; | |
92 while(w--) { | |
93 *buffer = sqrt(*buffer); | |
94 buffer++; | |
95 } | |
96 } | |
97 | |
98 void audioDB::sequence_average(double *buffer, int length, int seqlen) { | |
99 int w = length - seqlen + 1; | |
100 while(w--) { | |
101 *buffer /= seqlen; | |
102 buffer++; | |
103 } | |
104 } | |
105 | |
106 void audioDB::initialize_arrays(int track, unsigned int numVectors, double *query, double *data_buffer, double **D, double **DD) { | |
107 unsigned int j, k, l, w; | |
108 double *dp, *qp, *sp; | |
109 | |
110 const unsigned HOP_SIZE = sequenceHop; | |
111 const unsigned wL = sequenceLength; | |
112 | |
113 for(j = 0; j < numVectors; j++) { | |
114 // Sum products matrix | |
115 D[j] = new double[trackTable[track]]; | |
116 assert(D[j]); | |
117 // Matched filter matrix | |
118 DD[j]=new double[trackTable[track]]; | |
119 assert(DD[j]); | |
120 } | |
121 | |
122 // Dot product | |
123 for(j = 0; j < numVectors; j++) | |
124 for(k = 0; k < trackTable[track]; k++){ | |
125 qp = query + j * dbH->dim; | |
126 sp = data_buffer + k * dbH->dim; | |
127 DD[j][k] = 0.0; // Initialize matched filter array | |
128 dp = &D[j][k]; // point to correlation cell j,k | |
129 *dp = 0.0; // initialize correlation cell | |
130 l = dbH->dim; // size of vectors | |
131 while(l--) | |
132 *dp += *qp++ * *sp++; | |
133 } | |
134 | |
135 // Matched Filter | |
136 // HOP SIZE == 1 | |
137 double* spd; | |
138 if(HOP_SIZE == 1) { // HOP_SIZE = shingleHop | |
139 for(w = 0; w < wL; w++) { | |
140 for(j = 0; j < numVectors - w; j++) { | |
141 sp = DD[j]; | |
142 spd = D[j+w] + w; | |
143 k = trackTable[track] - w; | |
144 while(k--) | |
145 *sp++ += *spd++; | |
146 } | |
147 } | |
148 } else { // HOP_SIZE != 1 | |
149 for(w = 0; w < wL; w++) { | |
150 for(j = 0; j < numVectors - w; j += HOP_SIZE) { | |
151 sp = DD[j]; | |
152 spd = D[j+w]+w; | |
153 for(k = 0; k < trackTable[track] - w; k += HOP_SIZE) { | |
154 *sp += *spd; | |
155 sp += HOP_SIZE; | |
156 spd += HOP_SIZE; | |
157 } | |
158 } | |
159 } | |
160 } | |
161 } | |
162 | |
163 void audioDB::delete_arrays(int track, unsigned int numVectors, double **D, double **DD) { | |
164 if(D != NULL) { | |
165 for(unsigned int j = 0; j < numVectors; j++) { | |
166 delete[] D[j]; | |
167 } | |
168 } | |
169 if(DD != NULL) { | |
170 for(unsigned int j = 0; j < numVectors; j++) { | |
171 delete[] DD[j]; | |
172 } | |
173 } | |
174 } | |
175 | |
176 void audioDB::read_data(int track, double **data_buffer_p, size_t *data_buffer_size_p) { | |
177 if (trackTable[track] * sizeof(double) * dbH->dim > *data_buffer_size_p) { | |
178 if(*data_buffer_p) { | |
179 free(*data_buffer_p); | |
180 } | |
181 { | |
182 *data_buffer_size_p = trackTable[track] * sizeof(double) * dbH->dim; | |
183 void *tmp = malloc(*data_buffer_size_p); | |
184 if (tmp == NULL) { | |
185 error("error allocating data buffer"); | |
186 } | |
187 *data_buffer_p = (double *) tmp; | |
188 } | |
189 } | |
190 | |
191 read(dbfid, *data_buffer_p, trackTable[track] * sizeof(double) * dbH->dim); | |
192 } | |
193 | |
194 // These names deserve some unpicking. The names starting with a "q" | |
195 // are pointers to the query, norm and power vectors; the names | |
196 // starting with "v" are things that will end up pointing to the | |
197 // actual query point's information. -- CSR, 2007-12-05 | |
198 void audioDB::set_up_query(double **qp, double **vqp, double **qnp, double **vqnp, double **qpp, double **vqpp, double *mqdp, unsigned *nvp) { | |
199 *nvp = (statbuf.st_size - sizeof(int)) / (dbH->dim * sizeof(double)); | |
200 | |
201 if(!(dbH->flags & O2_FLAG_L2NORM)) { | |
202 error("Database must be L2 normed for sequence query","use -L2NORM"); | |
203 } | |
204 | |
205 if(*nvp < sequenceLength) { | |
206 error("Query shorter than requested sequence length", "maybe use -l"); | |
207 } | |
208 | |
209 VERB_LOG(1, "performing norms... "); | |
210 | |
211 *qp = new double[*nvp * dbH->dim]; | |
212 memcpy(*qp, indata+sizeof(int), *nvp * dbH->dim * sizeof(double)); | |
213 *qnp = new double[*nvp]; | |
214 unitNorm(*qp, dbH->dim, *nvp, *qnp); | |
215 | |
216 sequence_sum(*qnp, *nvp, sequenceLength); | |
217 sequence_sqrt(*qnp, *nvp, sequenceLength); | |
218 | |
219 if (usingPower) { | |
220 *qpp = new double[*nvp]; | |
221 if (lseek(powerfd, sizeof(int), SEEK_SET) == (off_t) -1) { | |
222 error("error seeking to data", powerFileName, "lseek"); | |
223 } | |
224 int count = read(powerfd, *qpp, *nvp * sizeof(double)); | |
225 if (count == -1) { | |
226 error("error reading data", powerFileName, "read"); | |
227 } | |
228 if ((unsigned) count != *nvp * sizeof(double)) { | |
229 error("short read", powerFileName); | |
230 } | |
231 | |
232 sequence_sum(*qpp, *nvp, sequenceLength); | |
233 sequence_average(*qpp, *nvp, sequenceLength); | |
234 } | |
235 | |
236 if (usingTimes) { | |
237 unsigned int k; | |
238 *mqdp = 0.0; | |
239 double *querydurs = new double[*nvp]; | |
240 double *timesdata = new double[*nvp*2]; | |
241 insertTimeStamps(*nvp, timesFile, timesdata); | |
242 for(k = 0; k < *nvp; k++) { | |
243 querydurs[k] = timesdata[2*k+1] - timesdata[2*k]; | |
244 *mqdp += querydurs[k]; | |
245 } | |
246 *mqdp /= k; | |
247 | |
248 VERB_LOG(1, "mean query file duration: %f\n", *mqdp); | |
249 | |
250 delete [] querydurs; | |
251 delete [] timesdata; | |
252 } | |
253 | |
254 // Defaults, for exhaustive search (!usingQueryPoint) | |
255 *vqp = *qp; | |
256 *vqnp = *qnp; | |
257 *vqpp = *qpp; | |
258 | |
259 if(usingQueryPoint) { | |
260 if(queryPoint > *nvp || queryPoint > *nvp - sequenceLength + 1) { | |
261 error("queryPoint > numVectors-wL+1 in query"); | |
262 } else { | |
263 VERB_LOG(1, "query point: %u\n", queryPoint); | |
264 *vqp = *qp + queryPoint * dbH->dim; | |
265 *vqnp = *qnp + queryPoint; | |
266 if (usingPower) { | |
267 *vqpp = *qpp + queryPoint; | |
268 } | |
269 *nvp = sequenceLength; | |
270 } | |
271 } | |
272 } | |
273 | |
274 // FIXME: this is not the right name; we're not actually setting up | |
275 // the database, but copying various bits of it out of mmap()ed tables | |
276 // in order to reduce seeks. | |
277 void audioDB::set_up_db(double **snp, double **vsnp, double **spp, double **vspp, double **mddp, unsigned int *dvp) { | |
278 *dvp = dbH->length / (dbH->dim * sizeof(double)); | |
279 *snp = new double[*dvp]; | |
280 | |
281 double *snpp = *snp, *sppp = 0; | |
282 memcpy(*snp, l2normTable, *dvp * sizeof(double)); | |
283 | |
284 if (usingPower) { | |
285 if (!(dbH->flags & O2_FLAG_POWER)) { | |
286 error("database not power-enabled", dbName); | |
287 } | |
288 *spp = new double[*dvp]; | |
289 sppp = *spp; | |
290 memcpy(*spp, powerTable, *dvp * sizeof(double)); | |
291 } | |
292 | |
293 for(unsigned int i = 0; i < dbH->numFiles; i++){ | |
294 if(trackTable[i] >= sequenceLength) { | |
295 sequence_sum(snpp, trackTable[i], sequenceLength); | |
296 sequence_sqrt(snpp, trackTable[i], sequenceLength); | |
297 | |
298 if (usingPower) { | |
299 sequence_sum(sppp, trackTable[i], sequenceLength); | |
300 sequence_average(sppp, trackTable[i], sequenceLength); | |
301 } | |
302 } | |
303 snpp += trackTable[i]; | |
304 if (usingPower) { | |
305 sppp += trackTable[i]; | |
306 } | |
307 } | |
308 | |
309 if (usingTimes) { | |
310 if(!(dbH->flags & O2_FLAG_TIMES)) { | |
311 error("query timestamps provided for non-timed database", dbName); | |
312 } | |
313 | |
314 *mddp = new double[dbH->numFiles]; | |
315 | |
316 for(unsigned int k = 0; k < dbH->numFiles; k++) { | |
317 unsigned int j; | |
318 (*mddp)[k] = 0.0; | |
319 for(j = 0; j < trackTable[k]; j++) { | |
320 (*mddp)[k] += timesTable[2*j+1] - timesTable[2*j]; | |
321 } | |
322 (*mddp)[k] /= j; | |
323 } | |
324 } | |
325 | |
326 *vsnp = *snp; | |
327 *vspp = *spp; | |
328 } | |
329 | |
330 void audioDB::trackSequenceQueryNN(const char* dbName, const char* inFile, Reporter *reporter) { | |
331 | |
332 unsigned int numVectors; | |
333 double *query, *query_data; | |
334 double *qNorm, *qnPtr, *qPower = 0, *qpPtr = 0; | |
335 double meanQdur; | |
336 | |
337 set_up_query(&query_data, &query, &qNorm, &qnPtr, &qPower, &qpPtr, &meanQdur, &numVectors); | |
338 | |
339 unsigned int dbVectors; | |
340 double *sNorm, *snPtr, *sPower = 0, *spPtr = 0; | |
341 double *meanDBdur = 0; | |
342 | |
343 set_up_db(&sNorm, &snPtr, &sPower, &spPtr, &meanDBdur, &dbVectors); | |
344 | |
345 VERB_LOG(1, "matching tracks..."); | |
346 | |
347 assert(pointNN>0 && pointNN<=O2_MAXNN); | |
348 assert(trackNN>0 && trackNN<=O2_MAXNN); | |
349 | |
350 unsigned j,k,track,trackOffset=0, HOP_SIZE=sequenceHop, wL=sequenceLength; | |
351 double **D = 0; // Differences query and target | |
352 double **DD = 0; // Matched filter distance | |
353 | |
354 D = new double*[numVectors]; | |
355 DD = new double*[numVectors]; | |
356 | |
357 gettimeofday(&tv1, NULL); | |
358 unsigned processedTracks = 0; | |
359 | |
360 // build track offset table | |
361 off_t *trackOffsetTable = new off_t[dbH->numFiles]; | |
362 unsigned cumTrack=0; | |
363 off_t trackIndexOffset; | |
364 for(k = 0; k < dbH->numFiles; k++){ | |
365 trackOffsetTable[k] = cumTrack; | |
366 cumTrack += trackTable[k] * dbH->dim; | |
367 } | |
368 | |
369 char nextKey[MAXSTR]; | |
370 | |
371 // Track loop | |
372 size_t data_buffer_size = 0; | |
373 double *data_buffer = 0; | |
374 lseek(dbfid, dbH->dataOffset, SEEK_SET); | |
375 | |
376 for(processedTracks=0, track=0 ; processedTracks < dbH->numFiles ; track++, processedTracks++) { | |
377 | |
378 trackOffset = trackOffsetTable[track]; // numDoubles offset | |
379 | |
380 // get trackID from file if using a control file | |
381 if(trackFile) { | |
382 trackFile->getline(nextKey,MAXSTR); | |
383 if(!trackFile->eof()) { | |
384 track = getKeyPos(nextKey); | |
385 trackOffset = trackOffsetTable[track]; | |
386 lseek(dbfid, dbH->dataOffset + trackOffset * sizeof(double), SEEK_SET); | |
387 } else { | |
388 break; | |
389 } | |
390 } | |
391 | |
392 trackIndexOffset=trackOffset/dbH->dim; // numVectors offset | |
393 | |
394 read_data(track, &data_buffer, &data_buffer_size); | |
395 if(sequenceLength <= trackTable[track]) { // test for short sequences | |
396 | |
397 VERB_LOG(7,"%u.%jd.%u | ", track, (intmax_t) trackIndexOffset, trackTable[track]); | |
398 | |
399 initialize_arrays(track, numVectors, query, data_buffer, D, DD); | |
400 | |
401 if(usingTimes) { | |
402 VERB_LOG(3,"meanQdur=%f meanDBdur=%f\n", meanQdur, meanDBdur[track]); | |
403 } | |
404 | |
405 if((!usingTimes) || fabs(meanDBdur[track]-meanQdur) < meanQdur*timesTol) { | |
406 if(usingTimes) { | |
407 VERB_LOG(3,"within duration tolerance.\n"); | |
408 } | |
409 | |
410 // Search for minimum distance by shingles (concatenated vectors) | |
411 for(j = 0; j <= numVectors - wL; j += HOP_SIZE) { | |
412 for(k = 0; k <= trackTable[track] - wL; k += HOP_SIZE) { | |
413 double thisDist; | |
414 if(normalizedDistance) { | |
415 thisDist = 2-(2/(qnPtr[j]*sNorm[trackIndexOffset+k]))*DD[j][k]; | |
416 } else { | |
417 thisDist = DD[j][k]; | |
418 } | |
419 // Power test | |
420 if ((!usingPower) || powers_acceptable(qpPtr[j], sPower[trackIndexOffset + k])) { | |
421 // radius test | |
422 if((!radius) || thisDist < radius) { | |
423 reporter->add_point(track, usingQueryPoint ? queryPoint : j, k, thisDist); | |
424 } | |
425 } | |
426 } | |
427 } | |
428 } // Duration match | |
429 delete_arrays(track, numVectors, D, DD); | |
430 } | |
431 } | |
432 | |
433 free(data_buffer); | |
434 | |
435 gettimeofday(&tv2,NULL); | |
436 VERB_LOG(1,"elapsed time: %ld msec\n", | |
437 (tv2.tv_sec*1000 + tv2.tv_usec/1000) - | |
438 (tv1.tv_sec*1000 + tv1.tv_usec/1000)) | |
439 | |
440 // Clean up | |
441 if(trackOffsetTable) | |
442 delete[] trackOffsetTable; | |
443 if(query_data) | |
444 delete[] query_data; | |
445 if(qNorm) | |
446 delete[] qNorm; | |
447 if(sNorm) | |
448 delete[] sNorm; | |
449 if(qPower) | |
450 delete[] qPower; | |
451 if(sPower) | |
452 delete[] sPower; | |
453 if(D) | |
454 delete[] D; | |
455 if(DD) | |
456 delete[] DD; | |
457 if(meanDBdur) | |
458 delete[] meanDBdur; | |
459 } | |
460 | |
461 // Unit norm block of features | |
462 void audioDB::unitNorm(double* X, unsigned dim, unsigned n, double* qNorm){ | |
463 unsigned d; | |
464 double L2, *p; | |
465 | |
466 VERB_LOG(2, "norming %u vectors...", n); | |
467 while(n--) { | |
468 p = X; | |
469 L2 = 0.0; | |
470 d = dim; | |
471 while(d--) { | |
472 L2 += *p * *p; | |
473 p++; | |
474 } | |
475 if(qNorm) { | |
476 *qNorm++=L2; | |
477 } | |
478 X += dim; | |
479 } | |
480 VERB_LOG(2, "done.\n"); | |
481 } |