changeset 78:06922d637752 audiodb-debian

Merge trunk changes -r52:93 onto audiodb-debian branch
author mas01cr
date Mon, 01 Oct 2007 14:40:08 +0000
parents 531c627e0810
children a1d462d592dd
files Makefile audioDB.cpp audioDB.h audioDBws.h debian/changelog gengetopt.in tests/0001/run-test.sh tests/0001/short-description tests/0002/run-test.sh tests/0002/short-description tests/0003/run-test.sh tests/0003/short-description tests/0004/run-test.sh tests/0004/short-description tests/0005/run-test.sh tests/0005/short-description tests/0006/run-test.sh tests/0006/short-description tests/0007/run-test.sh tests/0007/short-description tests/0008/run-test.sh tests/0008/short-description tests/0009/run-test.sh tests/0009/short-description tests/0010/run-test.sh tests/0010/short-description tests/clean.sh tests/run-tests.sh tests/test-utils.sh
diffstat 29 files changed, 722 insertions(+), 379 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Aug 29 16:24:29 2007 +0000
+++ b/Makefile	Mon Oct 01 14:40:08 2007 +0000
@@ -3,6 +3,8 @@
 
 EXECUTABLE=audioDB
 
+.PHONY: all clean test
+
 all: ${EXECUTABLE}
 
 ${EXECUTABLE}.1: ${EXECUTABLE}
@@ -17,12 +19,17 @@
 soapServer.cpp soapClient.cpp soapC.cpp: audioDBws.h
 	soapcpp2 audioDBws.h
 
-${EXECUTABLE}: audioDB.h audioDB.cpp soapServer.cpp soapClient.cpp soapC.cpp cmdline.c cmdline.h
-	g++ -o ${EXECUTABLE} ${CFLAGS} audioDB.cpp soapServer.cpp soapClient.cpp soapC.cpp cmdline.c ${LIBS}
+${EXECUTABLE}: audioDB.cpp soapServer.cpp soapClient.cpp soapC.cpp cmdline.c cmdline.h
+	g++ -c ${CFLAGS} -Wall -Werror audioDB.cpp
+	g++ -o ${EXECUTABLE} ${CFLAGS} audioDB.o soapServer.cpp soapClient.cpp soapC.cpp cmdline.c ${LIBS}
 
 clean:
 	-rm cmdline.c cmdline.h
 	-rm soapServer.cpp soapClient.cpp soapC.cpp soapObject.h soapStub.h soapProxy.h soapH.h soapServerLib.cpp soapClientLib.cpp
 	-rm adb.nsmap adb.xsd adb.wsdl adb.query.req.xml adb.query.res.xml adb.status.req.xml adb.status.res.xml
 	-rm README.txt
-	-rm ${EXECUTABLE} ${EXECUTABLE}.1
+	-rm ${EXECUTABLE} ${EXECUTABLE}.1 audioDB.o
+	-sh -c "cd tests && sh ./clean.sh"
+
+test: ${EXECUTABLE}
+	-sh -c "cd tests && sh ./run-tests.sh"
--- a/audioDB.cpp	Wed Aug 29 16:24:29 2007 +0000
+++ b/audioDB.cpp	Mon Oct 01 14:40:08 2007 +0000
@@ -1,146 +1,62 @@
-/* audioDB.cpp
-
-audioDB version 1.0
-
-A feature vector database management system for content-based retrieval.
-
-Usage: audioDB [OPTIONS]...
-
-      --full-help              Print help, including hidden options, and exit
-  -V, --version                Print version and exit
-  -H, --help                   print help on audioDB usage and exit.
-  -v, --verbosity=detail       level of detail of operational information.  
-                                 (default=`1')
-
-Database Setup:
-  All database operations require a database argument.
-  
-  Database commands are UPPER CASE. Command options are lower case.
-
-  -d, --database=filename      database file required by Database commands.
-  -N, --NEW                    make a new (initially empty) database.
-  -S, --STATUS                 output database information to stdout.
-  -D, --DUMP                   output all entries: index key size.
-  -L, --L2NORM                 unit norm vectors and norm all future inserts.
-
-Database Insertion:
-  The following commands insert feature files, with optional keys and 
-  timestamps.
-
-  -I, --INSERT                 add feature vectors to an existing database.
-  -U, --UPDATE                 replace inserted vectors associated with key 
-                                 with new input vectors.
-  -f, --features=filename      binary series of vectors file {int sz:ieee 
-                                 double[][sz]:eof}.
-  -t, --times=filename         list of time points (ascii) for feature vectors.
-  -k, --key=identifier         unique identifier associated with features.
-  
-  -B, --BATCHINSERT            add feature vectors named in a --featureList 
-                                 file (with optional keys in a --keyList file) 
-                                 to the named database.
-  -F, --featureList=filename   text file containing list of binary feature 
-                                 vector files to process
-  -T, --timesList=filename     text file containing list of ascii --times for 
-                                 each --features file in --featureList.
-  -K, --keyList=filename       text file containing list of unique identifiers 
-                                 associated with --features.
-
-Database Search:
-  Thse commands control the retrieval behaviour.
-
-  -Q, --QUERY=searchtype       content-based search on --database using 
-                                 --features as a query. Optionally restrict the 
-                                 search to those tracks identified in a 
-                                 --keyList.  (possible values="point", 
-                                 "track", "sequence")
-  -p, --qpoint=position        ordinal position of query start point in 
-                                 --features file.  (default=`0')
-  -e, --exhaustive             exhaustive search: iterate through all query 
-                                 vectors in search. Overrides --qpoint.  
-                                 (default=off)
-  -n, --pointnn=numpoints      number of point nearest neighbours to use in 
-                                 retrieval.  (default=`10')
-  -R, --radius=DOUBLE          radius search, returns all 
-                                 points/tracks/sequences inside given radius. 
-                                  (default=`1.0')
-  -x, --expandfactor=DOUBLE    time compress/expand factor of result length to 
-                                 query length [1.0 .. 100.0].  (default=`1.1')
-  -o, --rotate                 rotate query vectors for rotationally invariant 
-                                 search.  (default=off)
-  -r, --resultlength=length    maximum length of the result list.  
-                                 (default=`10')
-  -l, --sequencelength=length  length of sequences for sequence search.  
-                                 (default=`16')
-  -h, --sequencehop=hop        hop size of sequence window for sequence search. 
-                                  (default=`1')
-
-Web Services:
-  These commands enable the database process to establish a connection via the 
-  internet and operate as separate client and server processes.
-
-  -s, --SERVER=port            run as standalone web service on named port.  
-                                 (default=`80011')
-  -c, --client=hostname:port   run as a client using named host service.
-  
-  Copyright (C) 2007 Michael Casey, Goldsmiths, University of London
-  
-  outputs:
-  
-  key1 distance1 qpos1 spos1
-  key2 distance2 qpos2 spos2
-  ...
-  keyN distanceN qposN sposN
-  
-*/
-
 #include "audioDB.h"
 
 #define O2_DEBUG
 
 void audioDB::error(const char* a, const char* b, const char *sysFunc) {
-  cerr << a << ": " << b << endl;
-  if (sysFunc) {
-    perror(sysFunc);
+  if(isServer) {
+    char *err = new char[256]; /* FIXME: overflows */
+    snprintf(err, 255, "%s: %s\n%s", a, b, sysFunc ? strerror(errno) : "");
+    /* FIXME: actually we could usefully do with a properly structured
+       type, so that we can throw separate faultstring and details.
+       -- CSR, 2007-10-01 */
+    throw(err);
+  } else {
+    cerr << a << ": " << b << endl;
+    if (sysFunc) {
+      perror(sysFunc);
+    }
+    exit(1);
   }
-  exit(1);
 }
 
-audioDB::audioDB(const unsigned argc, char* const argv[], adb__queryResult *adbQueryResult):
-  dim(0),
-  dbName(0),
-  inFile(0),
-  key(0),
-  trackFile(0),
-  trackFileName(0),
-  timesFile(0),
-  timesFileName(0),
-  usingTimes(0),
-  command(0),
-  dbfid(0),
-  db(0),
-  dbH(0),
-  infid(0),
-  indata(0),
-  queryType(O2_FLAG_POINT_QUERY),
-  verbosity(1),
-  pointNN(O2_DEFAULT_POINTNN),
-  trackNN(O2_DEFAULT_TRACKNN),
-  trackTable(0),
-  fileTable(0),
-  dataBuf(0),
-  l2normTable(0),
-  timesTable(0),
-  qNorm(0),
-  sequenceLength(16),
-  sequenceHop(1),
-  queryPoint(0),
-  usingQueryPoint(0),
-  isClient(0),
-  isServer(0),
-  port(0),
-  timesTol(0.1),
-  radius(0){
-  
+#define O2_AUDIODB_INITIALIZERS \
+  dim(0), \
+  dbName(0), \
+  inFile(0), \
+  key(0), \
+  trackFileName(0), \
+  trackFile(0), \
+  command(0), \
+  timesFileName(0), \
+  timesFile(0), \
+  dbfid(0), \
+  infid(0), \
+  db(0), \
+  indata(0), \
+  dbH(0), \
+  fileTable(0), \
+  trackTable(0), \
+  dataBuf(0), \
+  l2normTable(0), \
+  qNorm(0), \
+  timesTable(0), \
+  verbosity(1), \
+  queryType(O2_FLAG_POINT_QUERY), \
+  pointNN(O2_DEFAULT_POINTNN), \
+  trackNN(O2_DEFAULT_TRACKNN), \
+  sequenceLength(16), \
+  sequenceHop(1), \
+  queryPoint(0), \
+  usingQueryPoint(0), \
+  usingTimes(0), \
+  isClient(0), \
+  isServer(0), \
+  port(0), \
+  timesTol(0.1), \
+  radius(0)
+
+audioDB::audioDB(const unsigned argc, char* const argv[]): O2_AUDIODB_INITIALIZERS
+{
   if(processArgs(argc, argv)<0){
     printf("No command found.\n");
     cmdline_parser_print_version ();
@@ -152,7 +68,7 @@
     printf("%s\n", gengetopt_args_info_help[0]);
     exit(1);
   }
-  
+
   if(O2_ACTION(COM_SERVER))
     startServer();
 
@@ -169,7 +85,7 @@
     if(isClient)
       ws_query(dbName, inFile, (char*)hostport);
     else
-      query(dbName, inFile, adbQueryResult);
+      query(dbName, inFile);
 
   else if(O2_ACTION(COM_STATUS))
     if(isClient)
@@ -187,6 +103,22 @@
     error("Unrecognized command",command);
 }
 
+audioDB::audioDB(const unsigned argc, char* const argv[], adb__queryResult *adbQueryResult): O2_AUDIODB_INITIALIZERS
+{
+  processArgs(argc, argv);
+  isServer = 1; // FIXME: Hack
+  assert(O2_ACTION(COM_QUERY));
+  query(dbName, inFile, adbQueryResult);
+}
+
+audioDB::audioDB(const unsigned argc, char* const argv[], adb__statusResult *adbStatusResult): O2_AUDIODB_INITIALIZERS
+{
+  processArgs(argc, argv);
+  isServer = 1; // FIXME: Hack
+  assert(O2_ACTION(COM_STATUS));
+  status(dbName, adbStatusResult);
+}
+
 audioDB::~audioDB(){
   // Clean up
   if(indata)
@@ -233,12 +165,12 @@
   if(args_info.radius_given){
     radius=args_info.radius_arg;
     if(radius<=0 || radius>1000000000){
-      cerr << "Warning: radius out of range" << endl;
-      exit(1);
+      error("radius out of range");
     }
     else 
-      if(verbosity>3)
+      if(verbosity>3) {
 	cerr << "Setting radius to " << radius << endl;
+      }
   }
   
   if(args_info.SERVER_given){
@@ -483,8 +415,9 @@
     error("write error", "", "write");
   
   // mmap the output file
-  if(verbosity)
+  if(verbosity) {
     cerr << "header size:" << O2_HEADERSIZE << endl;
+  }
   if ((db = (char*) mmap(0, O2_DEFAULTDBSIZE, PROT_READ | PROT_WRITE,
 			 MAP_SHARED, dbfid, 0)) == (caddr_t) -1)
     error("mmap error for creating database", "", "mmap");
@@ -500,9 +433,9 @@
   dbH->flags=0; //O2_FLAG_L2NORM;
 
   memcpy (db, dbH, O2_HEADERSIZE);
-  if(verbosity)
+  if(verbosity) {
     cerr << COM_CREATE << " " << dbName << endl;
-
+  }
 }
 
 
@@ -597,16 +530,18 @@
     }
 
   if(alreadyInserted){
-    if(verbosity)
+    if(verbosity) {
       cerr << "Warning: key already exists in database, ignoring: " <<inFile << endl;
+    }
     return;
   }
   
   // Make a track index table of features to file indexes
   unsigned numVectors = (statbuf.st_size-sizeof(int))/(sizeof(double)*dbH->dim);
   if(!numVectors){
-    if(verbosity)
+    if(verbosity) {
       cerr << "Warning: ignoring zero-length feature vector file:" << key << endl;
+    }
     // CLEAN UP
     munmap(indata,statbuf.st_size);
     munmap(db,O2_DEFAULTDBSIZE);
@@ -646,9 +581,10 @@
 
   // Report status
   status(dbName);
-  if(verbosity)
+  if(verbosity) {
     cerr << COM_INSERT << " " << dbName << " " << numVectors << " vectors " 
 	 << (statbuf.st_size-sizeof(int)) << " bytes." << endl;
+  }
 
   // CLEAN UP
   munmap(indata,statbuf.st_size);
@@ -701,8 +637,9 @@
        cerr << "expected " << numVectors << " found " << numtimes << endl;
        error("Times file is incorrect length for features file",inFile);
      }
-     if(verbosity>2)
+     if(verbosity>2) {
        cerr << "numtimes: " << numtimes << endl;
+     }
    }
  }
 }
@@ -816,16 +753,18 @@
       }
   
     if(alreadyInserted){
-      if(verbosity)
+      if(verbosity) {
 	cerr << "Warning: key already exists in database:" << thisKey << endl;
+      }
     }
     else{
   
       // Make a track index table of features to file indexes
       unsigned numVectors = (statbuf.st_size-sizeof(int))/(sizeof(double)*dbH->dim);
       if(!numVectors){
-	if(verbosity)
+	if(verbosity) {
 	  cerr << "Warning: ignoring zero-length feature vector file:" << thisKey << endl;
+        }
       }
       else{	
 	if(usingTimes){
@@ -880,9 +819,10 @@
 			 MAP_SHARED, dbfid, 0)) == (caddr_t) -1)
     error("mmap error for creating database", "", "mmap");
   
-  if(verbosity)
+  if(verbosity) {
     cerr << COM_BATCHINSERT << " " << dbName << " " << totalVectors << " vectors " 
 	 << totalVectors*dbH->dim*sizeof(double) << " bytes." << endl;
+  }
   
   // Report status
   status(dbName);
@@ -890,16 +830,25 @@
   munmap(db,O2_DEFAULTDBSIZE);
 }
 
+// FIXME: this can't propagate the sequence length argument (used for
+// dudCount).  See adb__status() definition for the other half of
+// this.  -- CSR, 2007-10-01
 void audioDB::ws_status(const char*dbName, char* hostport){
   struct soap soap;
-  int adbStatusResult;  
+  adb__statusResult adbStatusResult;  
   
   // Query an existing adb database
   soap_init(&soap);
-  if(soap_call_adb__status(&soap,hostport,NULL,(char*)dbName,adbStatusResult)==SOAP_OK)
-    std::cout << "result = " << adbStatusResult << std::endl;
-  else
+  if(soap_call_adb__status(&soap,hostport,NULL,(char*)dbName,adbStatusResult)==SOAP_OK) {
+    cout << "numFiles = " << adbStatusResult.numFiles << endl;
+    cout << "dim = " << adbStatusResult.dim << endl;
+    cout << "length = " << adbStatusResult.length << endl;
+    cout << "dudCount = " << adbStatusResult.dudCount << endl;
+    cout << "nullCount = " << adbStatusResult.nullCount << endl;
+    cout << "flags = " << adbStatusResult.flags << endl;
+  } else {
     soap_print_fault(&soap,stderr);
+  }
   
   soap_destroy(&soap);
   soap_end(&soap);
@@ -929,21 +878,9 @@
 }
 
 
-void audioDB::status(const char* dbName){
+void audioDB::status(const char* dbName, adb__statusResult *adbStatusResult){
   if(!dbH)
     initTables(dbName, 0, 0);
-  
-  // Update Header information
-  cout << "num files:" << dbH->numFiles << endl;
-  cout << "data dim:" << dbH->dim <<endl;
-  if(dbH->dim>0){
-    cout << "total vectors:" << dbH->length/(sizeof(double)*dbH->dim)<<endl;
-    cout << "vectors available:" << (timesTableOffset-(dataoffset+dbH->length))/(sizeof(double)*dbH->dim) << endl;
-  }
-  cout << "total bytes:" << dbH->length << " (" << (100.0*dbH->length)/(timesTableOffset-dataoffset) << "%)" << endl;
-  cout << "bytes available:" << timesTableOffset-(dataoffset+dbH->length) << " (" <<
-    (100.0*(timesTableOffset-(dataoffset+dbH->length)))/(timesTableOffset-dataoffset) << "%)" << endl;
-  cout << "flags:" << dbH->flags << endl;
 
   unsigned dudCount=0;
   unsigned nullCount=0;
@@ -951,13 +888,35 @@
     if(trackTable[k]<sequenceLength){
       dudCount++;
       if(!trackTable[k])
-	nullCount++;
+        nullCount++;
     }
   }
-  cout << "null count: " << nullCount << " small sequence count " << dudCount-nullCount << endl;    
+  
+  if(adbStatusResult == 0) {
+
+    // Update Header information
+    cout << "num files:" << dbH->numFiles << endl;
+    cout << "data dim:" << dbH->dim <<endl;
+    if(dbH->dim>0){
+      cout << "total vectors:" << dbH->length/(sizeof(double)*dbH->dim)<<endl;
+      cout << "vectors available:" << (timesTableOffset-(dataoffset+dbH->length))/(sizeof(double)*dbH->dim) << endl;
+    }
+    cout << "total bytes:" << dbH->length << " (" << (100.0*dbH->length)/(timesTableOffset-dataoffset) << "%)" << endl;
+    cout << "bytes available:" << timesTableOffset-(dataoffset+dbH->length) << " (" <<
+      (100.0*(timesTableOffset-(dataoffset+dbH->length)))/(timesTableOffset-dataoffset) << "%)" << endl;
+    cout << "flags:" << dbH->flags << endl;
+    
+    cout << "null count: " << nullCount << " small sequence count " << dudCount-nullCount << endl;    
+  } else {
+    adbStatusResult->numFiles = dbH->numFiles;
+    adbStatusResult->dim = dbH->dim;
+    adbStatusResult->length = dbH->length;
+    adbStatusResult->dudCount = dudCount;
+    adbStatusResult->nullCount = nullCount;
+    adbStatusResult->flags = dbH->flags;
+  }
 }
 
-
 void audioDB::dump(const char* dbName){
   if(!dbH)
     initTables(dbName, 0, 0);
@@ -971,7 +930,7 @@
 }
 
 void audioDB::l2norm(const char* dbName){
-  initTables(dbName, 0, 0);
+  initTables(dbName, true, 0);
   if(dbH->length>0){
     unsigned numVectors = dbH->length/(sizeof(double)*dbH->dim);
     unitNormAndInsertL2(dataBuf, dbH->dim, numVectors, 0); // No append
@@ -1041,7 +1000,7 @@
   unsigned qIndexes[pointNN];
   unsigned sIndexes[pointNN];
   for(unsigned k=0; k<pointNN; k++){
-    distances[k]=0.0;
+    distances[k]=-DBL_MAX;
     qIndexes[k]=~0;
     sIndexes[k]=~0;    
   }
@@ -1083,8 +1042,9 @@
     if(queryPoint>numVectors-1)
       error("queryPoint > numVectors in query");
     else{
-      if(verbosity>1)
+      if(verbosity>1) {
 	cerr << "query point: " << queryPoint << endl; cerr.flush();
+      }
       query=query+queryPoint*dbH->dim;
       numVectors=queryPoint+1;
       j=1;
@@ -1129,8 +1089,9 @@
   }
 
   gettimeofday(&tv2, NULL); 
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << endl << " elapsed time:" << ( tv2.tv_sec*1000 + tv2.tv_usec/1000 ) - ( tv1.tv_sec*1000+tv1.tv_usec/1000 ) << " msec" << endl;
+  }
 
   if(adbQueryResult==0){
     // Output answer
@@ -1158,7 +1119,7 @@
     adbQueryResult->Dist = new double[listLen];
     adbQueryResult->Qpos = new int[listLen];
     adbQueryResult->Spos = new int[listLen];
-    for(k=0; k<adbQueryResult->__sizeRlist; k++){
+    for(k=0; k<(unsigned)adbQueryResult->__sizeRlist; k++){
       adbQueryResult->Rlist[k]=new char[O2_MAXFILESTR];
       adbQueryResult->Dist[k]=distances[k];
       adbQueryResult->Qpos[k]=qIndexes[k];
@@ -1193,8 +1154,6 @@
   
   // For each input vector, find the closest pointNN matching output vectors and report
   unsigned numVectors = (statbuf.st_size-sizeof(int))/(sizeof(double)*dbH->dim);
-  unsigned numTracks = dbH->numFiles;
-
   double* query = (double*)(indata+sizeof(int));
   double* data = dataBuf;
   double* queryCopy = 0;
@@ -1227,13 +1186,13 @@
   double thisDist;
 
   for(k=0; k<pointNN; k++){
-    distances[k]=0.0;
+    distances[k]=-DBL_MAX;
     qIndexes[k]=~0;
     sIndexes[k]=~0;    
   }
 
   for(k=0; k<trackNN; k++){
-    trackDistances[k]=0.0;
+    trackDistances[k]=-DBL_MAX;
     trackQIndexes[k]=~0;
     trackSIndexes[k]=~0;
     trackIDs[k]=~0;
@@ -1273,8 +1232,9 @@
     if(queryPoint>numVectors-1)
       error("queryPoint > numVectors in query");
     else{
-      if(verbosity>1)
+      if(verbosity>1) {
 	cerr << "query point: " << queryPoint << endl; cerr.flush();
+      }
       query=query+queryPoint*dbH->dim;
       numVectors=queryPoint+1;
     }
@@ -1303,8 +1263,9 @@
     }
     trackOffset=trackOffsetTable[track];     // numDoubles offset
     trackIndexOffset=trackOffset/dbH->dim; // numVectors offset
-    if(verbosity>7)
+    if(verbosity>7) {
       cerr << track << "." << trackOffset/(dbH->dim) << "." << trackTable[track] << " | ";cerr.flush();
+    }
 
     if(dbH->flags & O2_FLAG_L2NORM)
       usingQueryPoint?query=queryCopy+queryPoint*dbH->dim:query=queryCopy;
@@ -1353,16 +1314,18 @@
     // Take the average of this track's distance
     // Test the track distances
     thisDist=0;
-    n=pointNN;
-    while(n--)
-      thisDist+=distances[pointNN-n-1];
-    thisDist/=pointNN;
+    for (n = 0; n < pointNN; n++) {
+      if (distances[n] == -DBL_MAX) break;
+      thisDist += distances[n];
+    }
+    thisDist /= n;
+
     n=trackNN;
     while(n--){
       if(thisDist>=trackDistances[n]){
 	if((n==0 || thisDist<=trackDistances[n-1])){
 	  // Copy all values above up the queue
-	  for( l=pointNN-1 ; l > n ; l--){
+	  for( l=trackNN-1 ; l > n ; l--){
 	    trackDistances[l]=trackDistances[l-1];
 	    trackQIndexes[l]=trackQIndexes[l-1];
 	    trackSIndexes[l]=trackSIndexes[l-1];
@@ -1379,20 +1342,22 @@
 	break;
     }
     for(unsigned k=0; k<pointNN; k++){
-      distances[k]=0.0;
+      distances[k]=-DBL_MAX;
       qIndexes[k]=~0;
       sIndexes[k]=~0;    
     }
   } // tracks
   gettimeofday(&tv2, NULL); 
 
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << endl << "processed tracks :" << processedTracks 
 	 << " elapsed time:" << ( tv2.tv_sec*1000 + tv2.tv_usec/1000 ) - ( tv1.tv_sec*1000+tv1.tv_usec/1000 ) << " msec" << endl;
+  }
 
   if(adbQueryResult==0){
-    if(verbosity>1)
+    if(verbosity>1) {
       cerr<<endl;
+    }
     // Output answer
     // Loop over nearest neighbours
     for(k=0; k < min(trackNN,processedTracks); k++)
@@ -1409,7 +1374,7 @@
     adbQueryResult->Dist = new double[listLen];
     adbQueryResult->Qpos = new int[listLen];
     adbQueryResult->Spos = new int[listLen];
-    for(k=0; k<adbQueryResult->__sizeRlist; k++){
+    for(k=0; k<(unsigned)adbQueryResult->__sizeRlist; k++){
       adbQueryResult->Rlist[k]=new char[O2_MAXFILESTR];
       adbQueryResult->Dist[k]=trackDistances[k];
       adbQueryResult->Qpos[k]=trackQIndexes[k];
@@ -1445,10 +1410,7 @@
   // For each input vector, find the closest pointNN matching output vectors and report
   // we use stdout in this stub version
   unsigned numVectors = (statbuf.st_size-sizeof(int))/(sizeof(double)*dbH->dim);
-  unsigned numTracks = dbH->numFiles;
-  
   double* query = (double*)(indata+sizeof(int));
-  double* data = dataBuf;
   double* queryCopy = 0;
 
   double qMeanL2;
@@ -1459,10 +1421,14 @@
   double DIFF_THRESH=0;
 
   if(!(dbH->flags & O2_FLAG_L2NORM) )
-    error("Database must be L2 normed for sequence query","use -l2norm");
+    error("Database must be L2 normed for sequence query","use -L2NORM");
+
+  if(numVectors<sequenceLength)
+    error("Query shorter than requested sequence length", "maybe use -l");
   
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "performing norms ... "; cerr.flush();
+  }
   unsigned dbVectors = dbH->length/(sizeof(double)*dbH->dim);
 
   // Make a copy of the query
@@ -1517,28 +1483,29 @@
   unsigned processedTracks=0;
   for(i=0; i<dbH->numFiles; i++){
     if(trackTable[i]>sequenceLength-1){
-      w = trackTable[i]-sequenceLength;
+      w = trackTable[i]-sequenceLength+1;
       pn = sMeanL2+i;
       *pn=0;
       while(w--)
 	if(*ps>0)
 	  *pn+=*ps++;
-      *pn/=trackTable[i]-sequenceLength;
+      *pn/=trackTable[i]-sequenceLength+1;
       SILENCE_THRESH+=*pn;
       processedTracks++;
     }
     ps = sNorm + trackTable[i];
   }
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "processedTracks: " << processedTracks << endl;
-
+  }
     
   SILENCE_THRESH/=processedTracks;
   USE_THRESH=1; // Turn thresholding on
   DIFF_THRESH=SILENCE_THRESH; //  mean shingle power
   SILENCE_THRESH/=5; // 20% of the mean shingle power is SILENCE
-  if(verbosity>4)
+  if(verbosity>4) {
     cerr << "silence thresh: " << SILENCE_THRESH;
+  }
   w=sequenceLength-1;
   i=1;
   tmp1=*qNorm;
@@ -1561,12 +1528,13 @@
   }
   qMeanL2 /= numVectors-sequenceLength+1;
 
-  if(verbosity>1)
-    cerr << "done." << endl;    
+  if(verbosity>1) {
+    cerr << "done." << endl;
+  }
   
-  
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "matching tracks..." << endl;
+  }
   
   assert(pointNN>0 && pointNN<=O2_MAXNN);
   assert(trackNN>0 && trackNN<=O2_MAXNN);
@@ -1584,7 +1552,6 @@
 
   unsigned k,l,m,n,track,trackOffset=0, HOP_SIZE=sequenceHop, wL=sequenceLength;
   double thisDist;
-  double oneOverWL=1.0/wL;
   
   for(k=0; k<pointNN; k++){
     distances[k]=1.0e6;
@@ -1622,8 +1589,9 @@
       meanQdur+=timesdata[k];
     }
     meanQdur/=k;
-    if(verbosity>1)
+    if(verbosity>1) {
       cerr << "mean query file duration: " << meanQdur << endl;
+    }
     meanDBdur = new double[dbH->numFiles];
     assert(meanDBdur);
     for(k=0; k<dbH->numFiles; k++){
@@ -1638,8 +1606,9 @@
     if(queryPoint>numVectors || queryPoint>numVectors-wL+1)
       error("queryPoint > numVectors-wL+1 in query");
     else{
-      if(verbosity>1)
+      if(verbosity>1) {
 	cerr << "query point: " << queryPoint << endl; cerr.flush();
+      }
       query=query+queryPoint*dbH->dim;
       qNorm=qNorm+queryPoint;
       numVectors=wL;
@@ -1660,7 +1629,6 @@
   double* qp;
   double* sp;
   double* dp;
-  double diffL2;
 
   // build track offset table
   unsigned *trackOffsetTable = new unsigned[dbH->numFiles];
@@ -1696,10 +1664,11 @@
     trackOffset=trackOffsetTable[track];     // numDoubles offset
     trackIndexOffset=trackOffset/dbH->dim; // numVectors offset
 
-    if(sequenceLength<trackTable[track]){  // test for short sequences
+    if(sequenceLength<=trackTable[track]){  // test for short sequences
       
-      if(verbosity>7)
+      if(verbosity>7) {
 	cerr << track << "." << trackIndexOffset << "." << trackTable[track] << " | ";cerr.flush();
+      }
 		
       // Sum products matrix
       for(j=0; j<numVectors;j++){
@@ -1714,7 +1683,6 @@
 	assert(DD[j]);
       }
 
-      double tmp;
       // Dot product
       for(j=0; j<numVectors; j++)
 	for(k=0; k<trackTable[track]; k++){
@@ -1755,7 +1723,7 @@
 	  }
       }
       
-      if(verbosity>3 && usingTimes){
+      if(verbosity>3 && usingTimes) {
 	cerr << "meanQdur=" << meanQdur << " meanDBdur=" << meanDBdur[track] << endl;
 	cerr.flush();
       }
@@ -1764,17 +1732,18 @@
 	 (usingTimes 
 	  && fabs(meanDBdur[track]-meanQdur)<meanQdur*timesTol)){
 
-	if(verbosity>3 && usingTimes){
+	if(verbosity>3 && usingTimes) {
 	  cerr << "within duration tolerance." << endl;
 	  cerr.flush();
 	}
 
 	// Search for minimum distance by shingles (concatenated vectors)
-	for(j=0;j<numVectors-wL;j+=HOP_SIZE)
-	  for(k=0;k<trackTable[track]-wL;k+=HOP_SIZE){
+	for(j=0;j<=numVectors-wL;j+=HOP_SIZE)
+	  for(k=0;k<=trackTable[track]-wL;k+=HOP_SIZE){
 	    thisDist=2-(2/(qNorm[j]*sNorm[trackIndexOffset+k]))*DD[j][k];
-	    if(verbosity>10)
+	    if(verbosity>10) {
 	      cerr << thisDist << " " << qNorm[j] << " " << sNorm[trackIndexOffset+k] << endl;
+            }
 	    // Gather chi^2 statistics
 	    if(thisDist<minSample)
 	      minSample=thisDist;
@@ -1820,13 +1789,16 @@
 	  }
 	// Calculate the mean of the N-Best matches
 	thisDist=0.0;
-	for(m=0; m<pointNN; m++)
+	for(m=0; m<pointNN; m++) {
+          if (distances[m] == 1000000.0) break;
 	  thisDist+=distances[m];
-	thisDist/=pointNN;
+        }
+	thisDist/=m;
 	
 	// Let's see the distances then...
-	if(verbosity>3)
+	if(verbosity>3) {
 	  cerr << fileTable+track*O2_FILETABLESIZE << " " << thisDist << endl;
+        }
 
 
 	// All the track stuff goes here
@@ -1874,15 +1846,16 @@
   }
 
   gettimeofday(&tv2,NULL);
-  if(verbosity>1){
+  if(verbosity>1) {
     cerr << endl << "processed tracks :" << processedTracks << " matched tracks: " << successfulTracks << " elapsed time:" 
 	 << ( tv2.tv_sec*1000 + tv2.tv_usec/1000 ) - ( tv1.tv_sec*1000+tv1.tv_usec/1000 ) << " msec" << endl;
     cerr << "sampleCount: " << sampleCount << " sampleSum: " << sampleSum << " logSampleSum: " << logSampleSum 
 	 << " minSample: " << minSample << " maxSample: " << maxSample << endl;
   }  
   if(adbQueryResult==0){
-    if(verbosity>1)
+    if(verbosity>1) {
       cerr<<endl;
+    }
     // Output answer
     // Loop over nearest neighbours
     for(k=0; k < min(trackNN,successfulTracks); k++)
@@ -1899,7 +1872,7 @@
     adbQueryResult->Dist = new double[listLen];
     adbQueryResult->Qpos = new int[listLen];
     adbQueryResult->Spos = new int[listLen];
-    for(k=0; k<adbQueryResult->__sizeRlist; k++){
+    for(k=0; k<(unsigned)adbQueryResult->__sizeRlist; k++){
       adbQueryResult->Rlist[k]=new char[O2_MAXFILESTR];
       adbQueryResult->Dist[k]=trackDistances[k];
       adbQueryResult->Qpos[k]=trackQIndexes[k];
@@ -1939,10 +1912,7 @@
   // For each input vector, find the closest pointNN matching output vectors and report
   // we use stdout in this stub version
   unsigned numVectors = (statbuf.st_size-sizeof(int))/(sizeof(double)*dbH->dim);
-  unsigned numTracks = dbH->numFiles;
-  
   double* query = (double*)(indata+sizeof(int));
-  double* data = dataBuf;
   double* queryCopy = 0;
 
   double qMeanL2;
@@ -1955,8 +1925,9 @@
   if(!(dbH->flags & O2_FLAG_L2NORM) )
     error("Database must be L2 normed for sequence query","use -l2norm");
   
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "performing norms ... "; cerr.flush();
+  }
   unsigned dbVectors = dbH->length/(sizeof(double)*dbH->dim);
 
   // Make a copy of the query
@@ -2011,28 +1982,29 @@
   unsigned processedTracks=0;
   for(i=0; i<dbH->numFiles; i++){
     if(trackTable[i]>sequenceLength-1){
-      w = trackTable[i]-sequenceLength;
+      w = trackTable[i]-sequenceLength+1;
       pn = sMeanL2+i;
       *pn=0;
       while(w--)
 	if(*ps>0)
 	  *pn+=*ps++;
-      *pn/=trackTable[i]-sequenceLength;
+      *pn/=trackTable[i]-sequenceLength+1;
       SILENCE_THRESH+=*pn;
       processedTracks++;
     }
     ps = sNorm + trackTable[i];
   }
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "processedTracks: " << processedTracks << endl;
-
+  }
     
   SILENCE_THRESH/=processedTracks;
   USE_THRESH=1; // Turn thresholding on
   DIFF_THRESH=SILENCE_THRESH; //  mean shingle power
   SILENCE_THRESH/=5; // 20% of the mean shingle power is SILENCE
-  if(verbosity>4)
+  if(verbosity>4) {
     cerr << "silence thresh: " << SILENCE_THRESH;
+  }
   w=sequenceLength-1;
   i=1;
   tmp1=*qNorm;
@@ -2055,12 +2027,13 @@
   }
   qMeanL2 /= numVectors-sequenceLength+1;
 
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "done." << endl;    
+  }
   
-  
-  if(verbosity>1)
+  if(verbosity>1) {
     cerr << "matching tracks..." << endl;
+  }
   
   assert(pointNN>0 && pointNN<=O2_MAXNN);
   assert(trackNN>0 && trackNN<=O2_MAXNN);
@@ -2076,9 +2049,8 @@
   unsigned sIndexes[pointNN];
   
 
-  unsigned k,l,m,n,track,trackOffset=0, HOP_SIZE=sequenceHop, wL=sequenceLength;
+  unsigned k,l,n,track,trackOffset=0, HOP_SIZE=sequenceHop, wL=sequenceLength;
   double thisDist;
-  double oneOverWL=1.0/wL;
   
   for(k=0; k<pointNN; k++){
     distances[k]=0.0;
@@ -2116,8 +2088,9 @@
       meanQdur+=timesdata[k];
     }
     meanQdur/=k;
-    if(verbosity>1)
+    if(verbosity>1) {
       cerr << "mean query file duration: " << meanQdur << endl;
+    }
     meanDBdur = new double[dbH->numFiles];
     assert(meanDBdur);
     for(k=0; k<dbH->numFiles; k++){
@@ -2132,8 +2105,9 @@
     if(queryPoint>numVectors || queryPoint>numVectors-wL+1)
       error("queryPoint > numVectors-wL+1 in query");
     else{
-      if(verbosity>1)
+      if(verbosity>1) {
 	cerr << "query point: " << queryPoint << endl; cerr.flush();
+      }
       query=query+queryPoint*dbH->dim;
       qNorm=qNorm+queryPoint;
       numVectors=wL;
@@ -2154,7 +2128,6 @@
   double* qp;
   double* sp;
   double* dp;
-  double diffL2;
 
   // build track offset table
   unsigned *trackOffsetTable = new unsigned[dbH->numFiles];
@@ -2190,11 +2163,12 @@
     trackOffset=trackOffsetTable[track];     // numDoubles offset
     trackIndexOffset=trackOffset/dbH->dim; // numVectors offset
 
-    if(sequenceLength<trackTable[track]){  // test for short sequences
+    if(sequenceLength<=trackTable[track]){  // test for short sequences
       
-      if(verbosity>7)
+      if(verbosity>7) {
 	cerr << track << "." << trackIndexOffset << "." << trackTable[track] << " | ";cerr.flush();
-		
+      }
+
       // Sum products matrix
       for(j=0; j<numVectors;j++){
 	D[j]=new double[trackTable[track]]; 
@@ -2208,7 +2182,6 @@
 	assert(DD[j]);
       }
 
-      double tmp;
       // Dot product
       for(j=0; j<numVectors; j++)
 	for(k=0; k<trackTable[track]; k++){
@@ -2249,7 +2222,7 @@
 	  }
       }
       
-      if(verbosity>3 && usingTimes){
+      if(verbosity>3 && usingTimes) {
 	cerr << "meanQdur=" << meanQdur << " meanDBdur=" << meanDBdur[track] << endl;
 	cerr.flush();
       }
@@ -2258,17 +2231,18 @@
 	 (usingTimes 
 	  && fabs(meanDBdur[track]-meanQdur)<meanQdur*timesTol)){
 
-	if(verbosity>3 && usingTimes){
+	if(verbosity>3 && usingTimes) {
 	  cerr << "within duration tolerance." << endl;
 	  cerr.flush();
 	}
 
 	// Search for minimum distance by shingles (concatenated vectors)
-	for(j=0;j<numVectors-wL;j+=HOP_SIZE)
-	  for(k=0;k<trackTable[track]-wL;k+=HOP_SIZE){
+	for(j=0;j<=numVectors-wL;j+=HOP_SIZE)
+	  for(k=0;k<=trackTable[track]-wL;k+=HOP_SIZE){
 	    thisDist=2-(2/(qNorm[j]*sNorm[trackIndexOffset+k]))*DD[j][k];
-	    if(verbosity>10)
+	    if(verbosity>10) {
 	      cerr << thisDist << " " << qNorm[j] << " " << sNorm[trackIndexOffset+k] << endl;
+            }
 	    // Gather chi^2 statistics
 	    if(thisDist<minSample)
 	      minSample=thisDist;
@@ -2299,8 +2273,9 @@
 	thisDist=distances[0];
 	
 	// Let's see the distances then...
-	if(verbosity>3)
+	if(verbosity>3) {
 	  cerr << fileTable+track*O2_FILETABLESIZE << " " << thisDist << endl;
+        }
 
 	// All the track stuff goes here
 	n=trackNN;
@@ -2347,7 +2322,7 @@
   }
 
   gettimeofday(&tv2,NULL);
-  if(verbosity>1){
+  if(verbosity>1) {
     cerr << endl << "processed tracks :" << processedTracks << " matched tracks: " << successfulTracks << " elapsed time:" 
 	 << ( tv2.tv_sec*1000 + tv2.tv_usec/1000 ) - ( tv1.tv_sec*1000+tv1.tv_usec/1000 ) << " msec" << endl;
     cerr << "sampleCount: " << sampleCount << " sampleSum: " << sampleSum << " logSampleSum: " << logSampleSum 
@@ -2355,8 +2330,9 @@
   }
   
   if(adbQueryResult==0){
-    if(verbosity>1)
+    if(verbosity>1) {
       cerr<<endl;
+    }
     // Output answer
     // Loop over nearest neighbours
     for(k=0; k < min(trackNN,successfulTracks); k++)
@@ -2372,7 +2348,7 @@
     adbQueryResult->Dist = new double[listLen];
     adbQueryResult->Qpos = new int[listLen];
     adbQueryResult->Spos = new int[listLen];
-    for(k=0; k<adbQueryResult->__sizeRlist; k++){
+    for(k=0; k<(unsigned)adbQueryResult->__sizeRlist; k++){
       adbQueryResult->Rlist[k]=new char[O2_MAXFILESTR];
       adbQueryResult->Dist[k]=trackDistances[k];
       adbQueryResult->Qpos[k]=trackQIndexes[k];
@@ -2401,47 +2377,13 @@
 
 }
 
-void audioDB::normalize(double* X, int dim, int n){
-  unsigned c = n*dim;
-  double minval,maxval,v,*p;
-
-  p=X;  
-  while(c--){
-    v=*p++;
-    if(v<minval)
-      minval=v;
-    else if(v>maxval)
-      maxval=v;
-  }
-
-  normalize(X, dim, n, minval, maxval);
-
-}
-
-void audioDB::normalize(double* X, int dim, int n, double minval, double maxval){
-  unsigned c = n*dim;
-  double *p;
-
-
-  if(maxval==minval)
-    return;
-
-  maxval=1.0/(maxval-minval);
-  c=n*dim;
-  p=X;
-
-  while(c--){
-    *p=(*p-minval)*maxval;
-    p++;
-  }
-}
-
 // Unit norm block of features
 void audioDB::unitNorm(double* X, unsigned dim, unsigned n, double* qNorm){
   unsigned d;
-  double L2, oneOverL2, *p;
-  if(verbosity>2)
+  double L2, *p;
+  if(verbosity>2) {
     cerr << "norming " << n << " vectors...";cerr.flush();
+  }
   while(n--){
     p=X;
     L2=0.0;
@@ -2462,14 +2404,15 @@
     */
     X+=dim;
   }
-  if(verbosity>2)
+  if(verbosity>2) {
     cerr << "done..." << endl;
+  }
 }
 
 // Unit norm block of features
 void audioDB::unitNormAndInsertL2(double* X, unsigned dim, unsigned n, unsigned append=0){
   unsigned d;
-  double L2, oneOverL2, *p;
+  double *p;
   unsigned nn = n;
 
   assert(l2normTable);
@@ -2477,8 +2420,9 @@
   if( !append && (dbH->flags & O2_FLAG_L2NORM) )
     error("Database is already L2 normed", "automatic norm on insert is enabled");
 
-  if(verbosity>2)
+  if(verbosity>2) {
     cerr << "norming " << n << " vectors...";cerr.flush();
+  }
 
   double* l2buf = new double[n];
   double* l2ptr = l2buf;
@@ -2512,8 +2456,9 @@
   memcpy(l2normTable+offset, l2buf, n*sizeof(double));
   if(l2buf)
     delete[] l2buf;
-  if(verbosity>2)
+  if(verbosity>2) {
     cerr << "done..." << endl;
+  }
 }
 
 
@@ -2536,7 +2481,7 @@
 	      soap_print_fault(&soap, stderr);
 	      break;
 	    }
-	  fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d\n", i,
+	  fprintf(stderr, "%d: accepted connection from IP=%lu.%lu.%lu.%lu socket=%d\n", i,
 		  (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
 	  if (soap_serve(&soap) != SOAP_OK) // process RPC request
 	    soap_print_fault(&soap, stderr); // print error
@@ -2552,12 +2497,16 @@
 // web services
 
 // SERVER SIDE
-int adb__status(struct soap* soap, xsd__string dbName, xsd__int &adbCreateResult){
+int adb__status(struct soap* soap, xsd__string dbName, adb__statusResult &adbStatusResult){
   char* const argv[]={"audioDB",COM_STATUS,"-d",dbName};
   const unsigned argc = 4;
-  audioDB(argc,argv);
-  adbCreateResult=100;
-  return SOAP_OK;
+  try {
+    audioDB(argc, argv, &adbStatusResult);
+    return SOAP_OK;
+  } catch(char *err) {
+    soap_receiver_fault(soap, err, "");
+    return SOAP_FAULT;
+  }
 }
 
 // Literal translation of command line to web service
--- a/audioDB.h	Wed Aug 29 16:24:29 2007 +0000
+++ b/audioDB.h	Mon Oct 01 14:40:08 2007 +0000
@@ -1,75 +1,3 @@
-/* audioDB.h 
-
-audioDB version 1.0
-
-An efficient feature-vector database management system (FVDBMS) for 
-content-based multimedia search and retrieval.
-
-Usage: audioDB [OPTIONS]...
-
-      --full-help              Print help, including hidden options, and exit
-  -V, --version                Print version and exit
-  -H, --help                   print help on audioDB usage and exit.
-
-Database Setup:
-  These commands require a database argument.
-  -d, --database=filename      database name to be used with database commands
-  -N, --new                    make a new database
-  -S, --status                 database information
-  -D, --dump                   list all tracks: index key size
-
-Database Insertion:
-  The following commands process a binary input feature file and optional 
-  associated key.
-  -I, --insert                 add feature vectors to an existing database
-  -f, --features=filename      binary series of vectors file
-  -t, --times=filename         list of time points (ascii) for feature vectors
-  -k, --key=identifier         unique identifier associated with features
-
-Batch Commands:
-  These batch commands require a list of feature vector filenames in a text 
-  file and optional list of keys in a text file.
-  -B, --batchinsert            add feature vectors named in a featureList file 
-                                 (with optional keys in a keyList file) to the 
-                                 named database
-  -F, --featureList=filename   text file containing list of binary feature 
-                                 vector files to process
-  -T, --timesList=filename     text file containing list of ascii time-point 
-                                 files for each feature vector file named in 
-                                 featureList
-  -K, --keyList=filename       text file containing list of unique identifiers 
-                                 to associate with list of feature files
-
-Database Search:
-  Thse commands control the behaviour of retrieval from a named database.
-  -Q, --query                  perform a content-based search on the named 
-                                 database using the named feature vector file 
-                                 as a query
-  -q, --qtype=type             the type of search  (possible values="point", 
-                                 "track", "sequence" default=`sequence')
-  -p, --qpoint=position        ordinal position of query vector (or start of 
-                                 sequence) in feature vector input file  
-                                 (default=`0')
-  -n, --pointnn=numpoints      number of point nearest neighbours to use [per 
-                                 track in track and sequence mode]  
-                                 (default=`10')
-  -r, --resultlength=length    maximum length of the result list  
-                                 (default=`10')
-  -l, --sequencelength=length  length of sequences for sequence search  
-                                 (default=`16')
-  -h, --sequencehop=hop        hop size of sequence window for sequence search  
-                                 (default=`1')
-
-Web Services:
-  These commands enable the database process to establish a connection via the 
-  internet and operate as separate client and server processes.
-  -s, --server=port            run as standalone web service on named port  
-                                 (default=`80011')
-  -c, --client=hostname:port   run as a client using named host service
-
-*/
-
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -82,6 +10,7 @@
 #include <math.h>
 #include <sys/time.h>
 #include <assert.h>
+#include <float.h>
 
 // includes for web services
 #include "soapH.h"
@@ -224,13 +153,13 @@
   void initTables(const char* dbName, bool forWrite, const char* inFile);
   void unitNorm(double* X, unsigned d, unsigned n, double* qNorm);
   void unitNormAndInsertL2(double* X, unsigned dim, unsigned n, unsigned append);
-  void normalize(double* X, int dim, int n);
-  void normalize(double* X, int dim, int n, double minval, double maxval);
   void insertTimeStamps(unsigned n, ifstream* timesFile, double* timesdata);
   unsigned getKeyPos(char* key);
  public:
 
-  audioDB(const unsigned argc, char* const argv[], adb__queryResult *adbQueryResult=0);
+  audioDB(const unsigned argc, char* const argv[]);
+  audioDB(const unsigned argc, char* const argv[], adb__queryResult *adbQueryResult);
+  audioDB(const unsigned argc, char* const argv[], adb__statusResult *adbStatusResult);
   ~audioDB();
   int processArgs(const unsigned argc, char* const argv[]);
   void get_lock(int fd, bool exclusive);
@@ -240,7 +169,7 @@
   void insert(const char* dbName, const char* inFile);
   void batchinsert(const char* dbName, const char* inFile);
   void query(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0);
-  void status(const char* dbName);
+  void status(const char* dbName, adb__statusResult *adbStatusResult=0);
   void ws_status(const char*dbName, char* hostport);
   void ws_query(const char*dbName, const char *trackKey, const char* hostport);
   void l2norm(const char* dbName);
--- a/audioDBws.h	Wed Aug 29 16:24:29 2007 +0000
+++ b/audioDBws.h	Mon Oct 01 14:40:08 2007 +0000
@@ -23,8 +23,17 @@
   int *Spos;
 };
 
+class adb__statusResult {
+  unsigned numFiles;
+  unsigned dim;
+  unsigned length;
+  unsigned dudCount;
+  unsigned nullCount;
+  unsigned flags;
+};
+    
 // Print the status of an existing adb database
-int adb__status(xsd__string dbName, xsd__int &adbCreateResult);
+int adb__status(xsd__string dbName, adb__statusResult &adbStatusResult);
 
 // Query an existing adb database
 int adb__query(xsd__string dbName, xsd__string qKey, xsd__string keyList, xsd__string timesFileName, xsd__int qType, xsd__int qPos, xsd__int pointNN, xsd__int segNN, xsd__int segLen, adb__queryResult &adbQueryResult);
--- a/debian/changelog	Wed Aug 29 16:24:29 2007 +0000
+++ b/debian/changelog	Mon Oct 01 14:40:08 2007 +0000
@@ -1,3 +1,9 @@
+audiodb (1.0-7) unstable; urgency=low
+
+  * updated to svn version #93
+
+ -- Christophe Rhodes <c.rhodes@gold.ac.uk>  Mon,  1 Oct 2007 16:35:53 +0100
+
 audiodb (1.0-6) unstable; urgency=low
 
   * updated to svn version #51
--- a/gengetopt.in	Wed Aug 29 16:24:29 2007 +0000
+++ b/gengetopt.in	Mon Oct 01 14:40:08 2007 +0000
@@ -73,7 +73,7 @@
 option "sequencehop"  h "hop size of sequence window for sequence search." int typestr="hop" default="1" dependon="QUERY" optional
 
 section "Web Services" sectiondesc="These commands enable the database process to establish a connection via the internet and operate as separate client and server processes.\n"
-option "SERVER" s "run as standalone web service on named port." int typestr="port" default="80011" optional
+option "SERVER" s "run as standalone web service on named port." int typestr="port" default="14475" optional
 option "client" c "run as a client using named host service." string typestr="hostname:port" optional
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0001/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+# creation
+${AUDIODB} -N -d testdb
+
+stat testdb
+
+# should fail (testdb exists)
+expect_clean_error_exit ${AUDIODB} -N -d testdb
+
+# should fail (no db given)
+expect_clean_error_exit ${AUDIODB} -N
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0001/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+DB creation
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0002/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -N -d testdb
+
+# FIXME: at some point we will want to test that some relevant
+# information is being printed
+${AUDIODB} -S -d testdb
+${AUDIODB} -d testdb -S
+
+# should fail (no db given)
+expect_clean_error_exit ${AUDIODB} -S
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0002/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+DB status
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0003/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,32 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+# We could contemplate putting the test feature (and the expected
+# query output) under svn control if we trust its binary file
+# handling.
+
+# FIXME: endianness!
+intstring 1 > testfeature
+floatstring 1 >> testfeature
+
+${AUDIODB} -d testdb -I -f testfeature
+
+${AUDIODB} -d testdb -Q point -f testfeature > test-query-output
+
+echo testfeature 1 0 0 > test-expected-query-output
+
+cmp test-query-output test-expected-query-output
+
+# failure cases
+expect_clean_error_exit ${AUDIODB} -d testdb -I
+expect_clean_error_exit ${AUDIODB} -d testdb -f testfeature
+expect_clean_error_exit ${AUDIODB} -I -f testfeature
+expect_clean_error_exit ${AUDIODB} -d testdb -Q notpoint -f testfeature
+expect_clean_error_exit ${AUDIODB} -Q point -f testfeature
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0003/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+1D insertion / point query
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0004/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,39 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+intstring 2 > testfeature
+floatstring 0 1 >> testfeature
+floatstring 1 0 >> testfeature
+
+${AUDIODB} -d testdb -I -f testfeature
+
+echo "query point (0.0,0.5)"
+intstring 2 > testquery
+floatstring 0 0.5 >> testquery
+
+${AUDIODB} -d testdb -Q point -f testquery > testoutput
+echo testfeature 0.5 0 0 > test-expected-output
+echo testfeature 0 0 1 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q point -f testquery -n 1 > testoutput
+echo testfeature 0.5 0 0 > test-expected-output
+cmp testoutput test-expected-output
+
+echo "query point (0.5,0.0)"
+intstring 2 > testquery
+floatstring 0.5 0 >> testquery
+
+${AUDIODB} -d testdb -Q point -f testquery > testoutput
+echo testfeature 0.5 0 1 > test-expected-output
+echo testfeature 0 0 0 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q point -f testquery -n 1 > testoutput
+echo testfeature 0.5 0 1 > test-expected-output
+cmp testoutput test-expected-output
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0004/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+point query
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0005/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,19 @@
+#! /bin/bash
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+intstring 2 > testfeature
+floatstring 0 1 >> testfeature
+floatstring 1 0 >> testfeature
+
+${AUDIODB} -d testdb -I -f testfeature
+
+echo running L2Norm
+
+${AUDIODB} -d testdb -L
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0005/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+L2 Norm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0006/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+intstring 2 > testfeature
+floatstring 0 1 >> testfeature
+floatstring 1 0 >> testfeature
+
+${AUDIODB} -d testdb -I -f testfeature
+
+# sequence queries require L2NORM
+${AUDIODB} -d testdb -L
+
+echo "query point (0.0,0.5)"
+intstring 2 > testquery
+floatstring 0 0.5 >> testquery
+
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery > testoutput
+echo testfeature 1 0 0 > test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -n 1 > testoutput
+echo testfeature 0 0 0 > test-expected-output
+cmp testoutput test-expected-output
+
+echo "query point (0.5,0.0)"
+intstring 2 > testquery
+floatstring 0.5 0 >> testquery
+
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery > testoutput
+echo testfeature 1 0 1 > test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -n 1 > testoutput
+echo testfeature 0 0 1 > test-expected-output
+cmp testoutput test-expected-output
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0006/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+sequence search / 1 track
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0007/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,35 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+# tests that the lack of -l when the query sequence is shorter doesn't
+# segfault.
+
+intstring 2 > testfeature
+floatstring 0 1 >> testfeature
+floatstring 1 0 >> testfeature
+
+${AUDIODB} -d testdb -I -f testfeature
+
+# sequence queries require L2NORM
+${AUDIODB} -d testdb -L
+
+echo "query point (0.0,0.5)"
+intstring 2 > testquery
+floatstring 0 0.5 >> testquery
+
+expect_clean_error_exit ${AUDIODB} -d testdb -Q sequence -f testquery
+expect_clean_error_exit ${AUDIODB} -d testdb -Q sequence -f testquery -n 1
+
+echo "query point (0.5,0.0)"
+intstring 2 > testquery
+floatstring 0.5 0 >> testquery
+
+expect_clean_error_exit ${AUDIODB} -d testdb -Q sequence -f testquery
+expect_clean_error_exit ${AUDIODB} -d testdb -Q sequence -f testquery -n 1
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0007/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+short query [no -l] error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0008/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,44 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+intstring 2 > testfeature01
+floatstring 0 1 >> testfeature01
+intstring 2 > testfeature10
+floatstring 1 0 >> testfeature10
+
+${AUDIODB} -d testdb -I -f testfeature01
+${AUDIODB} -d testdb -I -f testfeature10
+
+# sequence queries require L2NORM
+${AUDIODB} -d testdb -L
+
+echo "query point (0.0,0.5)"
+intstring 2 > testquery
+floatstring 0 0.5 >> testquery
+
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery > testoutput
+echo testfeature01 0 0 0 > test-expected-output
+echo testfeature10 2 0 0 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -r 1 > testoutput
+echo testfeature01 0 0 0 > test-expected-output
+cmp testoutput test-expected-output
+
+echo "query point (0.5,0.0)"
+intstring 2 > testquery
+floatstring 0.5 0 >> testquery
+
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery > testoutput
+echo testfeature10 0 0 0 > test-expected-output
+echo testfeature01 2 0 0 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -r 1 > testoutput
+echo testfeature10 0 0 0 > test-expected-output
+cmp testoutput test-expected-output
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0008/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+sequence search / 2 tracks
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0009/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,41 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+intstring 2 > testfeature01
+floatstring 0 1 >> testfeature01
+intstring 2 > testfeature10
+floatstring 1 0 >> testfeature10
+
+${AUDIODB} -d testdb -I -f testfeature01
+${AUDIODB} -d testdb -I -f testfeature10
+
+echo "query point (0.0,0.5)"
+intstring 2 > testquery
+floatstring 0 0.5 >> testquery
+
+${AUDIODB} -d testdb -Q track -l 1 -f testquery > testoutput
+echo testfeature01 0.5 0 0 > test-expected-output
+echo testfeature10 0 0 0 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q track -l 1 -f testquery -r 1 > testoutput
+echo testfeature01 0.5 0 0 > test-expected-output
+cmp testoutput test-expected-output
+
+echo "query point (0.5,0.0)"
+intstring 2 > testquery
+floatstring 0.5 0 >> testquery
+
+${AUDIODB} -d testdb -Q track -l 1 -f testquery > testoutput
+echo testfeature10 0.5 0 0 > test-expected-output
+echo testfeature01 0 0 0 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q track -l 1 -f testquery -r 1 > testoutput
+echo testfeature10 0.5 0 0 > test-expected-output
+cmp testoutput test-expected-output
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0009/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+track search
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0010/run-test.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,49 @@
+#! /bin/sh
+
+. ../test-utils.sh
+
+if [ -f testdb ]; then rm -f testdb; fi
+
+${AUDIODB} -d testdb -N
+
+intstring 2 > testfeature01
+floatstring 0 1 >> testfeature01
+intstring 2 > testfeature10
+floatstring 1 0 >> testfeature10
+
+${AUDIODB} -d testdb -I -f testfeature01
+${AUDIODB} -d testdb -I -f testfeature10
+
+# sequence queries require L2NORM
+${AUDIODB} -d testdb -L
+
+echo "query point (0.0,0.5)"
+intstring 2 > testquery
+floatstring 0 0.5 >> testquery
+
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -R 5 > testoutput
+echo testfeature01 1 > test-expected-output
+echo testfeature10 1 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -r 1 -R 5 > testoutput
+echo testfeature01 1 > test-expected-output
+cmp testoutput test-expected-output
+
+echo "query point (0.5,0.0)"
+intstring 2 > testquery
+floatstring 0.5 0 >> testquery
+
+# FIXME: because there's only one point in each track (and the query),
+# the ordering is essentially database order.  We need these test
+# cases anyway because we need to test non-segfaulting, non-empty
+# results...
+
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -R 5 > testoutput
+echo testfeature01 1 > test-expected-output
+echo testfeature10 1 >> test-expected-output
+cmp testoutput test-expected-output
+${AUDIODB} -d testdb -Q sequence -l 1 -f testquery -r 1 -R 5 > testoutput
+echo testfeature01 1 > test-expected-output
+cmp testoutput test-expected-output
+
+exit 104
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/0010/short-description	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,1 @@
+sequence radius search / 1 point
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/clean.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+for file in [0-9][0-9][0-9][0-9]*; do
+  if [ -d ${file} ]; then
+    echo Cleaning ${file}
+    rm -f ${file}/test*
+    if [ -f ${file}/clean.sh ]; then
+      (cd ${file} && sh ./clean.sh)
+    fi
+  fi
+done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/run-tests.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,39 @@
+#! /bin/sh
+
+AUDIODB=../../${EXECUTABLE:-audioDB}
+export AUDIODB
+
+if [ -x ${AUDIODB:3} ]; then 
+  :
+else 
+  echo Cannot execute audioDB: ${AUDIODB:3}
+  exit 1
+fi
+
+for file in [0-9][0-9][0-9][0-9]*; do
+  if [ -d ${file} ]; then
+    if [ -f ${file}/run-test.sh ]; then
+      echo -n Running test ${file}
+      if [ -f ${file}/short-description ]; then
+        awk '{ printf(" (%s)",$0) }' < ${file}/short-description
+      fi
+      echo -n :
+      (cd ${file} && sh ./run-test.sh > test.out 2> test.err)
+      EXIT_STATUS=$?
+      if [ ${EXIT_STATUS} -ne 104 ]; then
+        echo " failed (exit status ${EXIT_STATUS})".
+        FAILED=true
+      else
+        echo " success."
+      fi
+    else
+      echo Skipping test ${file}
+    fi
+  fi
+done
+
+if [ -z "${FAILED}" ]; then
+  exit 0
+else
+  exit 1
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-utils.sh	Mon Oct 01 14:40:08 2007 +0000
@@ -0,0 +1,49 @@
+# no shebang line: this file should be sourced by run-test.sh files
+
+trap "exit 1" ERR
+
+if [ -z ${AUDIODB} ]; then
+  AUDIODB=../../audioDB
+fi
+
+# FIXME: maybe generalize to multiple arguments?  Also, implement it
+# properly, rather than just for a few floats that we know how to
+# encode.  This might involve writing some C code, as Bash doesn't do
+# Floating Point.  (scanf() is probably enough).
+
+expect_clean_error_exit() {
+  trap - ERR
+  "$@"
+  exit_code=$?
+  trap "exit 1" ERR
+  if [ $exit_code -eq 0 ]; then
+    exit 1
+  elif [ $exit_code -ge 126 ]; then
+    exit 1
+  fi
+}
+
+floatstring() {
+  for arg in "$@"; do
+    case ${arg} in
+      0)
+        printf "\x00\x00\x00\x00\x00\x00\x00\x00";;
+      0.5)
+        printf "\x00\x00\x00\x00\x00\x00\xe0\x3f";;
+      1)
+        printf "\x00\x00\x00\x00\x00\x00\xf0\x3f";;
+      *)
+        echo "bad arg to floatstring(): ${arg}"
+        exit 1;;
+    esac
+  done
+}
+
+# FIXME: likewise.  And endianness issues (which are a reflection of
+# the endianness of audioDB as of 2007-09-18, unfortunately).
+
+intstring() {
+  # works up to 9 for now
+  if [ $1 -ge 10 ]; then echo "intstring() arg too large: ${1}"; exit 1; fi
+  printf "%b\x00\x00\x00" "\\x${1}"
+}