Index: /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/AbstractAudioSearch.java
===================================================================
--- /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/AbstractAudioSearch.java	(revision 24398)
+++ /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/AbstractAudioSearch.java	(revision 24398)
@@ -0,0 +1,57 @@
+/*
+ *    AbstractAudioSearch.java
+ *    Copyright (C) 2011 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.greenstone.gsdl3.service;
+
+// Greenstone classes
+import org.greenstone.gsdl3.util.GSXML;
+import org.greenstone.gsdl3.util.GSPath;
+
+// XML classes
+import org.w3c.dom.Document;
+import org.w3c.dom.Element; 
+import org.w3c.dom.NodeList;
+
+// java classes
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.*;
+
+/** Partially implements a generic audio search service
+ */
+
+public abstract class AbstractAudioSearch
+    extends AbstractSearch 
+{
+
+   static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.AbstractAudioSearch.class.getName());
+
+   
+    public AbstractAudioSearch()
+    {
+	// the search service
+	QUERY_SERVICE = "AudioQuery";
+    }
+
+    /** do the actual query 
+     * must be implemented by subclass */
+    abstract protected Element processAudioQuery(Element request); 
+	
+}
+
Index: /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/AbstractGS2AudioSearch.java
===================================================================
--- /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/AbstractGS2AudioSearch.java	(revision 24398)
+++ /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/AbstractGS2AudioSearch.java	(revision 24398)
@@ -0,0 +1,193 @@
+/*
+ *    AbstractGS2AudioSearch.java
+ *    Copyright (C) 2011 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *   the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.greenstone.gsdl3.service;
+
+
+// Greenstone classes
+import org.greenstone.gsdl3.util.OID;
+import org.greenstone.gsdl3.util.DBInfo;
+import org.greenstone.gsdl3.util.GSXML;
+import org.greenstone.gsdl3.util.SimpleDocumentDatabase;
+import org.greenstone.gsdl3.util.GSFile;
+
+// XML classes
+import org.w3c.dom.Document;
+import org.w3c.dom.Element; 
+import org.w3c.dom.NodeList;
+
+// java
+import java.util.Vector;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.io.File;
+
+import org.apache.log4j.*;
+
+public abstract class AbstractGS2AudioSearch 
+    extends AbstractAudioSearch 
+{
+
+    protected static final String EQUIV_TERM_ELEM = "equivTerm";
+
+    protected static final String STEM_ATT = "stem";
+    protected static final String NUM_DOCS_MATCH_ATT = "numDocsMatch";
+    protected static final String FREQ_ATT = "freq";
+
+    // Elements used in the config file that are specific to this class
+    protected static final String DEFAULT_INDEX_ELEM = "defaultIndex";
+    protected static final String INDEX_STEM_ELEM = "indexStem";
+    protected static final String INDEX_ELEM = "index";
+    protected static final String DEFAULT_INDEX_SUBCOLLECTION_ELEM = "defaultIndexSubcollection";
+    protected static final String DEFAULT_INDEX_LANGUAGE_ELEM = "defaultIndexLanguage";
+    protected static final String INDEX_SUBCOLLECTION_ELEM = "indexSubcollection";
+    protected static final String INDEX_LANGUAGE_ELEM = "indexLanguage";
+   
+
+    // Some indexing options
+    protected static final String STEMINDEX_OPTION = "stemIndexes";
+    protected static final String MAXNUMERIC_OPTION = "maxnumeric";
+
+    /** the stem used for the index files */
+    protected String index_stem = null;
+
+    // stem indexes available
+    protected boolean does_case=true;
+    protected boolean does_stem=true;
+    protected boolean does_accent=false;
+
+    // maxnumeric - 
+    protected int maxnumeric = 4;
+
+    SimpleDocumentDatabase gs_doc_db = null;
+
+    static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.AbstractGS2AudioSearch.class.getName());
+
+
+    /** constructor */
+    public AbstractGS2AudioSearch()
+    {
+	
+    }
+    public void cleanUp() {
+	super.cleanUp();
+	this.gs_doc_db.cleanUp();
+    }
+
+    /** configure this service */
+    public boolean configure(Element info, Element extra_info)
+    {
+	if (!super.configure(info,extra_info)) {
+	    return false;
+	}
+
+	// find out what kind of database we have
+	Element database_type_elem = (Element) GSXML.getChildByTagName(info, GSXML.DATABASE_TYPE_ELEM);
+	String database_type = null;
+	if (database_type_elem != null) {
+	  database_type = database_type_elem.getAttribute(GSXML.NAME_ATT);
+	}
+	if (database_type == null || database_type.equals("")) {
+	  database_type = "gdbm"; // the default
+	}
+	
+	// the index stem is either the collection name or is specified in the config file
+	Element index_stem_elem = (Element) GSXML.getChildByTagName(info, INDEX_STEM_ELEM);
+	if (index_stem_elem != null) {
+	    this.index_stem = index_stem_elem.getAttribute(GSXML.NAME_ATT);
+	}
+	if (this.index_stem == null || this.index_stem.equals("")) {
+	    logger.warn("indexStem element not found, stem will default to collection name");
+	    this.index_stem = this.cluster_name;
+	}
+
+	// replaces default AbstractSearch version with one tied to database
+	gs_doc_db = new SimpleDocumentDatabase(this.doc,
+					       database_type,this.site_home,
+					       this.cluster_name,
+					       this.index_stem);
+	if (!gs_doc_db.isValid()) {
+	    logger.error("Failed to open Document Database.");
+	    return false;
+	}
+	this.gs_doc = gs_doc_db; 
+
+	// do we support any of the extended features?
+	does_chunking = true;
+
+	// Get the default index out of <defaultIndex> (buildConfig.xml)
+	Element def = (Element) GSXML.getChildByTagName(info, DEFAULT_INDEX_ELEM);
+	if (def != null) {
+	    this.default_index = def.getAttribute(GSXML.SHORTNAME_ATT);
+	} // otherwise will be "", and the first one will be the default
+        
+
+	// Get index options ... 
+	// ... but there are currently no index options supported for audio
+	// so nothing to do
+
+	// Similarly the following will not currently do anything, but leave
+	// in for the time we do start supporting different indexes
+	// 
+	// *** Also, it is identical to that in AbstractGS2TextSearch, so
+	// consider putting into supporting routine and sharing 
+	// (realted in 'info' object
+
+	// get display info from extra info
+	if (extra_info !=null) {
+	    Document owner = info.getOwnerDocument();
+	    // so far we have index specific display elements, and global format elements 
+	    NodeList indexes = info.getElementsByTagName(GSXML.INDEX_ELEM);
+	    Element config_search = (Element)GSXML.getChildByTagName(extra_info, GSXML.SEARCH_ELEM);
+	    
+	    for (int i=0; i<indexes.getLength();i++) {
+		Element ind = (Element)indexes.item(i);
+		String name = ind.getAttribute(GSXML.NAME_ATT);
+		Element node_extra = GSXML.getNamedElement(config_search,
+							   GSXML.INDEX_ELEM,
+							   GSXML.NAME_ATT,
+							   name);
+		if (node_extra == null) {
+		    logger.error("haven't found extra info for index named "+name);
+		    continue;
+		}
+		
+		// get the display elements if any - displayName
+		NodeList display_names = node_extra.getElementsByTagName(GSXML.DISPLAY_TEXT_ELEM);
+		if (display_names !=null) {
+		    for (int j=0; j<display_names.getLength(); j++) {
+			Element e = (Element)display_names.item(j);
+			ind.appendChild(owner.importNode(e, true));
+		    }
+		}
+	    } // for each index
+	}
+
+	return true;
+    }
+
+    protected void getIndexData(ArrayList index_ids, ArrayList index_names, String lang) 
+    {
+	logger.info("Trivial index support for audio for now.  Adding hardwired 'audioDB' to index");
+	index_names.add("audioDB");
+    }
+}
+
+
Index: /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/GS2AudioDBSearch.java
===================================================================
--- /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/GS2AudioDBSearch.java	(revision 24397)
+++ /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/service/GS2AudioDBSearch.java	(revision 24398)
@@ -38,5 +38,5 @@
 import org.apache.log4j.*;
 
-public class GS2AudioDBSearch extends AbstractGS2Search {
+public class GS2AudioDBSearch extends AbstractGS2AudioSearch {
 
     static Logger logger = Logger.getLogger (org.greenstone.gsdl3.service.GS2AudioDBSearch.class.getName ());
@@ -48,5 +48,5 @@
 
     protected static final String AUDIODB_DEFAULT_DIRECTORY = "audioDB";
-    protected static final String ADB_FILENAME = "lhs-features.adb";
+    protected static final String ADB_FILENAME = "lsh-features.adb";
     
     protected AudioDBWrapper audiodb_src = null;
@@ -60,5 +60,5 @@
     
      /** do the actual query */
-    protected Element processTextQuery (Element request) {
+    protected Element processAudioQuery (Element request) {
 	// MG needs to be synchronized (this inspiration for this class)
 	// Since it is not known how concurrent audioDB can be, play it safe for
@@ -68,5 +68,8 @@
 	    // Create a new (empty) result message ('doc' is in ServiceRack.java)
 	    Element result = this.doc.createElement (GSXML.RESPONSE_ELEM);
-	    result.setAttribute (GSXML.FROM_ATT, TEXT_QUERY_SERVICE); // Result looks the same as a text query
+
+	    // Rather than QUERY_SERVICE use "TextQuery" 
+	    // => makes the result looks the same as a text query
+	    result.setAttribute (GSXML.FROM_ATT, "TextQuery"); 
 	    result.setAttribute (GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
 	    
@@ -74,5 +77,5 @@
 	    Element param_list = (Element) GSXML.getChildByTagName (request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
 	    if (param_list == null) {
-		logger.error ("AudioDB TextQuery request had no paramList.");
+		logger.error ("AudioDB AudioQuery request had no paramList.");
 		return result;  // Return the empty result
 	    }
@@ -86,5 +89,5 @@
 		return result;  // Return the empty result
 	    }
-	    
+
 	    // If an index hasn't been specified, use the default
 	    String index = (String) params.get (INDEX_PARAM);
@@ -94,15 +97,17 @@
 	    
 	    // The location of the audioDB index
-	    String index_dir = GSFile.collectionIndexDir (this.site_home, this.cluster_name) +  File.separatorChar + index;
-	    
+	    String toplevel_index_dir = GSFile.collectionIndexDir (this.site_home, this.cluster_name);
+	    String audioDB_index_dir = toplevel_index_dir + File.separatorChar + index;
+	    String assoc_index_dir = toplevel_index_dir + File.separatorChar + "assoc";
+
 	    // set the audioDB query parameters to the values the user has specified
 	    setStandardQueryParams (params);
 	    
-	    this.audiodb_src.runQuery (index_dir, ADB_FILENAME, query);
+	    this.audiodb_src.runQuery (audioDB_index_dir, ADB_FILENAME, assoc_index_dir, query);
 	    Vector docs = this.audiodb_src.getQueryResult ();
 
 	    if (docs.isEmpty()) {
 		// something has gone wrong
-		GSXML.addError (this.doc, result, "Couldn't query the mg database", GSXML.ERROR_TYPE_SYSTEM);
+		GSXML.addError (this.doc, result, "Couldn't query the audioDB database", GSXML.ERROR_TYPE_SYSTEM);
 		return result;
 	    }
@@ -129,7 +134,13 @@
 		result.appendChild (document_list);
 		for (int d = 0; d < docs.size (); d++) {
-		    String doc_id = ((AudioDBDocInfo) docs.elementAt (d)).oid_;
-		    float rank = ((AudioDBDocInfo) docs.elementAt (d)).rank_;
-		    Element doc_node = createDocNode (doc_id, Float.toString (rank));
+		    AudioDBDocInfo adb_doc = (AudioDBDocInfo) docs.elementAt(d);
+
+		    String doc_id  = adb_doc.getDocID();
+		    double rank    = adb_doc.getTopRank();
+		    String offsets = adb_doc.getOffsetList();
+
+		    Element doc_node = createDocNode (doc_id, Double.toString (rank));
+		    doc_node.setAttribute("frameOffset", offsets);
+
 		    document_list.appendChild (doc_node);
 		}
Index: /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/util/AudioDBDocInfo.java
===================================================================
--- /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/util/AudioDBDocInfo.java	(revision 24397)
+++ /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/util/AudioDBDocInfo.java	(revision 24398)
@@ -19,19 +19,82 @@
 package org.greenstone.gsdl3.util;
 
+import java.util.Vector;
+import java.lang.Comparable;
 
-public class AudioDBDocInfo
+public class AudioDBDocInfo implements Comparable<AudioDBDocInfo>
 {
-    public String oid_ = null;
-    public float rank_ = 0;
+    public String oid_;
+    public Vector<Double> rankVector_;
+    public Vector<Integer> offsetVector_;
 
-    public AudioDBDocInfo(String doc_oid, float rank)
+    public AudioDBDocInfo(String doc_oid, double rank, int offset)
     {
 	oid_ = doc_oid;
-	rank_ = rank;
+	
+	rankVector_ = new Vector<Double>();
+	rankVector_.add (rank);
+
+	offsetVector_ = new Vector<Integer>();
+	offsetVector_.add(offset);
     }
 
-    public String toString()
+
+    public AudioDBDocInfo(String doc_oid, Vector<Double> rankVector, Vector<Integer> offsetVector)
     {
-	return "" + oid_ + " (" + rank_ + ")";
+	oid_ = doc_oid;
+	rankVector_ = rankVector;
+	offsetVector_ = offsetVector;
+    }
+
+    public String getDocID()
+    {
+	return oid_;
+    }
+
+    public double getTopRank()
+    {
+	return rankVector_.get(0);
+    }
+
+
+    public String getOffsetList() 
+    {
+	StringBuffer all_offsets = new StringBuffer();
+	boolean first = true;
+
+	for (int i=0; i<offsetVector_.size(); i++) {
+	    int offset = offsetVector_.get(i);
+	    String offsetStr = Integer.toString(offset);
+
+	    if (first) {
+		first = false;
+	    }
+	    else {
+		all_offsets.append(",");
+	    }
+
+	    all_offsets.append(offsetStr);
+	}
+
+	return all_offsets.toString();
+    }
+
+    public int compareTo(AudioDBDocInfo di)
+    {
+	// based on first entry in rank Vector
+
+	// embodies a descending sort order
+	double lrank = rankVector_.get(0);
+	double rrank = di.rankVector_.get(0);
+
+	if (lrank<rrank) {
+	    return 1;
+	}
+	else if (lrank>rrank) {
+	    return -1;
+	}
+	else {
+	    return 0;
+	}
     }
 }
Index: /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/util/AudioDBWrapper.java
===================================================================
--- /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/util/AudioDBWrapper.java	(revision 24397)
+++ /gs3-extensions/audioDB/trunk/src/src/java/org/greenstone/gsdl3/util/AudioDBWrapper.java	(revision 24398)
@@ -19,6 +19,9 @@
 package org.greenstone.gsdl3.util;
 
-import java.io.File;
+import java.io.*;
 import java.util.Vector;
+import java.util.Collections;
+
+import org.apache.log4j.*;
 
 /** java wrapper class for access to the audioDB executable
@@ -30,5 +33,5 @@
 {
     /** the query result, filled in by runQuery */
-    protected Vector query_result_ = null;
+    protected Vector query_result_;
 
     protected int offset_ = 100;
@@ -40,6 +43,8 @@
     protected int max_docs_;
 
+    static Logger logger = Logger.getLogger (org.greenstone.gsdl3.util.AudioDBWrapper.class.getName ());
+
     public AudioDBWrapper() {  
-	query_result_ = new Vector();
+	query_result_ = null;
     }
 
@@ -70,4 +75,26 @@
     // the following was in MG version, do we need this in audioDB version?
     //public String getQueryParams() {}
+
+
+    protected boolean addQueryResult(boolean first_entry, String doc_id, 
+				  Vector<Double> rankVector, Vector<Integer> offsetVector)
+    {
+
+	if (first_entry) {
+	    AudioDBDocInfo audioDB_doc_info = new AudioDBDocInfo(doc_id,rankVector,offsetVector);
+	    query_result_.add(audioDB_doc_info);
+	    first_entry = false;
+	}
+	else {
+	    double rank = rankVector.get(0);
+	    int offset = offsetVector.get(0);
+	    AudioDBDocInfo audioDB_doc_info = new AudioDBDocInfo(doc_id,rank,offset);
+	    
+	    query_result_.add(audioDB_doc_info);
+	}
+
+	return first_entry;
+    }
+
 
     /** actually carry out the query.
@@ -79,19 +106,111 @@
      * base_dir must end with a file separator (OS dependant)
      */
-    public void runQuery(String index_dir, String adb_file, String query_string) {
+    public void runQuery(String audioDB_index_dir, String adb_file, 
+			 String assoc_index_dir, String query_string) {
 
 	// combine index_dir with audiodb fileanem
 
-	String full_adb_filename  = index_dir + File.separatorChar + adb_file;
+	String full_adb_filename  = audioDB_index_dir + File.separatorChar + adb_file;
+	String full_chr12_filename = assoc_index_dir + File.separatorChar 
+	    + query_string + File.separatorChar + "doc.chr12";
+
+	int num_matches_within_track = 6;
 
 	String cmd = "audioDB"
 	    + " -d " + full_adb_filename
-	    + " -Q sequence"
+	    + " -Q nsequence"
 	    + " -p " + offset_ 
-	    + " -n 1"
+	    + " -n " + num_matches_within_track
 	    + " -l " + length_
-	    + " -r " + "numMatchingDocs"
-	    + " -f " + "track.chr12";
-
+	    + " -r " + max_docs_
+	    + " -f " + full_chr12_filename;
+
+	System.err.println("**** cmd = " + cmd);
+
+	Runtime runtime = Runtime.getRuntime();
+	try {
+	    Process audioDB_proc = runtime.exec(cmd);
+	    InputStream ais = audioDB_proc.getInputStream();
+	    InputStreamReader aisr = new InputStreamReader(ais);
+	    BufferedReader abr = new BufferedReader(aisr);
+
+	    query_result_ = new Vector();
+
+	    boolean first_entry = true;
+	    int line_count = 0;
+
+	    String root_doc_id = null;
+	    Vector<Double> rankVector = new Vector<Double>();
+	    Vector<Integer> offsetVector = new Vector<Integer>();
+
+	    // Example output
+	    //   D8 0.00105175
+	    //   1.69786e-16 392 392
+	    //   0.00113568 392 673
+	    //   0.00127239 392 910
+	    //   0.00139736 392 481
+	    //   0.00145331 392 303
+	    //   D2 0.00429758
+	    //   0.00403335 392 865
+	    //   0.00411288 392 458
+	    //   0.00442461 392 866
+	    //   0.00444272 392 864
+	    //   0.00447434 392 424
+	    // ...
+
+	    String line;
+	    while ((line = abr.readLine()) != null) {
+		String[] tokens = line.split("\\s+");
+		line_count++;
+
+		if (tokens.length==2) {
+		    // processing a top-level doc line
+
+		    if (line_count>1) {
+			// struck new top-level entry => store vector vals for previous block
+			first_entry = addQueryResult(first_entry,root_doc_id,rankVector,offsetVector);
+			// and now reset vectors to empty to be ready for next chain of values
+			rankVector = new Vector<Double>();
+			offsetVector = new Vector<Integer>();
+		    }
+
+		    root_doc_id = tokens[0];
+		}
+		else {
+		    // should be 3 items 
+		    double euclidean_dist = Double.parseDouble(tokens[0]);
+		    int src_frame = Integer.parseInt(tokens[1]);
+		    int target_frame = Integer.parseInt(tokens[2]);
+
+		    // enforce 1.0 as upper limit due to rounding errors 
+		    // in audioDB distance calculations
+		    double rank = Math.min(1.0 - euclidean_dist,1.0); 
+
+		    if ((line_count==2) && (src_frame==target_frame)) {
+			// Found match with self
+			continue;
+		    }
+
+		    rankVector.add(rank);
+		    offsetVector.add(target_frame);
+		}
+
+	    }
+
+	    addQueryResult(first_entry,root_doc_id,rankVector,offsetVector);
+
+	    abr.close();
+
+	    // sort query_result_ on 'rank' field 
+	    // note: compareTo() method impelemented to sort into descending order
+
+	    Collections.sort(query_result_);
+
+
+	}
+	catch (Exception e) {
+	    logger.error("Failed to execute the following command: " + cmd);
+	    e.printStackTrace();
+	}
 
     }
