diff examples/iAudioDB/AppController.m @ 699:9a7d829bc492

* Pulled through query start changes * Disables fields when no query selected / new db opened * Added icon files
author mas01mj
date Wed, 28 Apr 2010 15:48:59 +0000
parents 02756c5ca15a
children 54974e8a6b87
line wrap: on
line diff
--- a/examples/iAudioDB/AppController.m	Mon Apr 26 17:17:07 2010 +0000
+++ b/examples/iAudioDB/AppController.m	Wed Apr 28 15:48:59 2010 +0000
@@ -5,8 +5,8 @@
 //  Created by Mike Jewell on 27/01/2010.
 //  Copyright 2010 __MyCompanyName__. All rights reserved.
 //
+#import "AppController.h"
 
-#import "AppController.h"
 
 
 @implementation AppController
@@ -18,57 +18,158 @@
 	// A max of 100 results.
 	results = [[NSMutableArray alloc] initWithCapacity: 100];
 	
-	
 	return self;
 }
 
+- (void)awakeFromNib {
+	[tracksView setTarget:self];
+	[tracksView setDoubleAction:@selector(tableDoubleClick:)];
+	[self updateStatus];
+}
+
+
+- (IBAction)tableDoubleClick:(id)sender
+{
+	[self playResult:Nil];
+//	NSLog(@"Table double clicked");
+}
+	
 
 /**
  * Create a new database, given the selected filename.
  */
 -(IBAction)newDatabase:(id)sender
 {
+	
+	[NSApp beginSheet:createSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
+	session = [NSApp beginModalSessionForWindow:createSheet];
+	[NSApp runModalSession:session];	
+}
+
+/**
+ * Cancel the db creation (at configuration time).
+ */
+-(IBAction)cancelCreate:(id)sender
+{
+	[NSApp endModalSession:session];
+	[createSheet orderOut:nil];
+	[NSApp endSheet:createSheet];
+}
+
+-(IBAction)createDatabase:(id)sender
+{
+	[self cancelCreate:self];
+	
 	NSSavePanel* panel = [NSSavePanel savePanel];
 	NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@""];
-	
+	 
 	[results removeAllObjects];
 	[tracksView reloadData];
-	
+	 
 	if(response == NSFileHandlingPanelOKButton)
 	{
-		// TODO: Refactor this into a 'tidy' method.
-		// Tidy any existing references up.
-		if(db)
+		// Work out which extractor to use
+		NSString* extractor = @"adb_chroma";
+		// TODO: This should be stored with the n3.
+		int dim;
+		switch([extractorOptions selectedTag])
 		{
-			audiodb_close(db);
+			case 0:
+				extractor = @"adb_chroma";
+				dim = 12;
+				break;
+			case 1:
+				extractor = @"adb_cq";
+				dim = 48;
+				break;
+			case 2:
+				extractor = @"qm_chroma";
+				dim = 12;
+				break;
+			case 3:
+				extractor = @"qm_mfcc";
+				dim = 12;
+				break;
 		}
 		
-		if(dbFilename)
-		{
-			[dbFilename release];
-			[dbName release];
-			[plistFilename release];
-		}
+		// Calculate the max DB size
+		int vectors = ceil(([maxLengthField doubleValue] * 60.0f) / ([hopSizeField doubleValue] / 44100.0f));
+		int numtracks = [maxTracksField intValue];
+		int datasize = ceil((numtracks * vectors * dim * 8.0f) / 1024.0f / 1024.0f); // In MB
 		
+		[self reset];
+		 
 		// Create new db, and set flags.
-		db = audiodb_create([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], 0, 0, 0);
+		db = audiodb_create([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], datasize, numtracks, dim);
 		audiodb_l2norm(db);
-		audiodb_power(db);
-		
+			 
 		// Store useful paths.
 		dbName = [[[panel URL] relativePath] retain];
 		dbFilename = [[panel filename] retain];
 		plistFilename = [[NSString stringWithFormat:@"%@.plist", [dbFilename stringByDeletingPathExtension]] retain];
-		
+			
 		// Create the plist file (contains mapping from filename to key).
+		dbState = [[NSMutableDictionary alloc] init];
 		trackMap = [[NSMutableDictionary alloc] init];
-		[trackMap writeToFile:plistFilename atomically:YES];
-		
+		[dbState setValue:trackMap forKey:@"tracks"];
+		[dbState setValue:extractor forKey:@"extractor"];
+		[dbState setValue:[hopSizeField stringValue] forKey:@"hopsize"];
+		[dbState setValue:[windowSizeField stringValue] forKey:@"windowsize"];
+		[dbState writeToFile:plistFilename atomically:YES];
+			 
 		[queryKey setStringValue:@"None Selected"];
 		[self updateStatus];
 	}
 }
 
+-(void)reset
+{
+	// Tidy any existing references up.
+	if(db)
+	{
+		NSLog(@"Close db");
+		audiodb_close(db);
+	}
+	
+	if(dbFilename)
+	{
+		NSLog(@"Tidy up filenames");
+		[dbFilename release];
+		[dbName release];
+		[plistFilename release];
+		[trackMap release];
+		[dbState release];
+	}
+	
+	if(selectedKey)
+	{
+		NSLog(@"Released selected key: %@", selectedKey);
+		[selectedKey release];
+		selectedKey = Nil;
+		NSLog(@"Is now %@", selectedKey);
+	}
+	
+	if(selectedKey)
+	{
+		NSLog(@"Still evals");
+	}
+	
+	// Reset query flags
+	[queryPath setStringValue: @"No file selected"];
+	[queryLengthSeconds setDoubleValue:0];
+	[queryLengthVectors setDoubleValue:0];
+	[multipleCheckBox setState:NSOnState];
+	[queryStartSeconds setDoubleValue:0];
+	[queryStartVectors setDoubleValue:0];
+	
+	[queryLengthSeconds setEnabled:NO];
+	[queryLengthVectors setEnabled:NO];
+	[queryStartSeconds setEnabled:NO];
+	[queryStartVectors setEnabled:NO];
+	[resetButton setEnabled:NO];
+	[multipleCheckBox setEnabled:NO];
+}
+
 /**
  * Open an existing adb (which must have a plist)
  */
@@ -79,21 +180,11 @@
 	NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
 	if(response == NSFileHandlingPanelOKButton)
 	{
-		// Tidy any existing references up.
-		if(db)
-		{
-			audiodb_close(db);
-		}
-		
-		if(dbFilename)
-		{
-			[dbFilename release];
-			[dbName release];
-			[plistFilename release];
-		}
+		[self reset];
 		
 		// Store useful paths.
-		db = audiodb_open([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], O_RDWR);
+		NSLog(@"Open");
+		db = audiodb_open([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
 		dbName = [[[panel URL] relativePath] retain];
 		dbFilename = [[panel filename] retain];
 		
@@ -105,7 +196,6 @@
 		[tracksView reloadData];
 		
 		[queryKey setStringValue:@"None Selected"];
-		[self updateStatus];
 		
 		adb_liszt_results_t* liszt_results = audiodb_liszt(db);
 		
@@ -116,57 +206,58 @@
 		}
 		
 		audiodb_liszt_free_results(db, liszt_results);
-		trackMap = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistFilename] retain];
+		dbState = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistFilename] retain];
+		trackMap = [[dbState objectForKey:@"tracks"] retain];
+		
+		[self updateStatus];
+		
 		NSLog(@"Size: %d", [trackMap count]);
 	}
 }
 
+-(IBAction)pathAction:(id)sender
+{
+	NSLog(@"Path action");
+}
+
 /**
  * Update button states and status field based on current state.
  */
 -(void)updateStatus
 {
+	NSLog(@"Update status");
 	if(db)
 	{
-		adb_status_ptr status = (adb_status_ptr)malloc(sizeof(struct adbstatus));
+		NSLog(@"Got a db");
+		adb_status_t *status = (adb_status_t *)malloc(sizeof(adb_status_t));
 		int flags;
 		flags = audiodb_status(db, status);
-		[statusField setStringValue: [NSString stringWithFormat:@"Database: %@ Dimensions: %d Files: %d", dbName, status->dim, status->numFiles]];
-		[chooseButton setEnabled:YES];
+		[statusField setStringValue: [NSString stringWithFormat:@"%@ Dim: %d Files: %d Hop: %@ Win: %@ Ext: %@", 
+									  dbName, 
+									  status->dim, 
+									  status->numFiles, 
+									  [dbState objectForKey:@"hopsize"],
+									  [dbState objectForKey:@"windowsize"],
+									  [dbState objectForKey:@"extractor"]]];
+		[performQueryButton setEnabled:YES];
+		[importAudioButton setEnabled:YES];
 	}
 	else
 	{
-		[chooseButton setEnabled:NO];
-		[playBothButton setEnabled:FALSE];
-		[playResultButton setEnabled:FALSE];
+		NSLog(@"No db");
+		[performQueryButton setEnabled:NO];
+		[importAudioButton setEnabled:NO];
+		[playBothButton setEnabled:NO];
+		[playResultButton setEnabled:NO];
+		[stopButton setEnabled:NO];
 	}
 }
 
 /**
- * Get user's import choices.
- */
--(IBAction)importAudio:(id)sender
-{
-	[NSApp beginSheet:importSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
-	session = [NSApp beginModalSessionForWindow: importSheet];
-	[NSApp runModalSession:session];
-}
-
-/**
- * Cancel the import (at configuration time).
- */
--(IBAction)cancelImport:(id)sender;
-{
-	[NSApp endModalSession:session];
-	[importSheet orderOut:nil];
-	[NSApp endSheet:importSheet];
-}
-
-/**
  * Choose the file(s) to be imported.
  * TODO: Currently handles the import process too - split this off.
  */
--(IBAction)selectFiles:(id)sender
+-(IBAction)importAudio:(id)sender
 {
 	[tracksView reloadData];
 	
@@ -176,121 +267,75 @@
 	NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
 	if(response == NSFileHandlingPanelOKButton)
 	{
-		NSRect newFrame;
+		[indicator startAnimation:self];
 		
-		[extractingBox setHidden:FALSE];
-		newFrame.origin.x = [importSheet frame].origin.x;
-		newFrame.origin.y = [importSheet frame].origin.y - [extractingBox frame].size.height;
-		newFrame.size.width = [importSheet frame].size.width;
-		newFrame.size.height = [importSheet frame].size.height + [extractingBox frame].size.height;
-		
-		[indicator startAnimation:self];
-		[importSheet setFrame:newFrame display:YES animate:YES];
+		[NSApp beginSheet:importSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
+		session = [NSApp beginModalSessionForWindow: importSheet];
+		[NSApp runModalSession:session];
 		
 		NSArray *filesToOpen = [panel filenames];
 		
-		NSLog(@"Begin import");
+		NSString* extractor = [dbState objectForKey:@"extractor"];
+		NSString* extractorPath = [NSString stringWithFormat:@"/Applications/iAudioDB.app/rdf/%@.n3", extractor];
 		
-		// Work out which extractor to use
-		NSString* extractor = @"chromagram";
-		switch([extractorOptions selectedTag])
-		{
-			case 0:
-				extractor = @"mfcc";
-				break;
-			case 1:
-				extractor = @"chromagram";
-				break;
-		}
+		// TODO Shift this process into a separate function.
+		// Create the customized extractor config
+		NSString* extractorContent = [NSString stringWithContentsOfFile:extractorPath];
+		NSString* hopStr = [dbState objectForKey:@"hopsize"];
+		NSString* winStr = [dbState objectForKey:@"windowsize"];
+		NSString* newContent = [[extractorContent stringByReplacingOccurrencesOfString:@"HOP_SIZE" withString:hopStr] 
+								stringByReplacingOccurrencesOfString:@"WINDOW_SIZE" withString:winStr];
+		NSString* n3FileName = [NSTemporaryDirectory() stringByAppendingPathComponent:@"extractor_config.n3"];
+		
+		NSError* error;
+		[newContent writeToFile:n3FileName atomically:YES encoding:NSASCIIStringEncoding error:&error];
 		
 		for(int i=0; i<[filesToOpen count]; i++)
-		{
-			// First extract powers
-			
-			NSString *tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"powers.XXXXXX"];
-			const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
-			char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
-			strcpy(tempFileNameCString, tempFileTemplateCString);
-			mktemp(tempFileNameCString);
-			
-			NSString* powersFileName = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
-			free(tempFileNameCString);
-			
-			NSTask *task = [[NSTask alloc] init];
-			[task setLaunchPath:@"/usr/local/bin/fftExtract"];
-			NSArray *args = [NSArray arrayWithObjects:@"-P", @"-s", @"250", [filesToOpen objectAtIndex:i], powersFileName, nil];
-			[task setArguments:args];
-			[task launch];
-			[task waitUntilExit];
-			[task release];
-			
-			// Then features
-			
-			tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"features.XXXXXX"];
-			tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
-			tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
+		{		
+			audiodb_close(db);
+			NSString* tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"features.XXXXXX"];
+			const char* tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
+			char* tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
 			strcpy(tempFileNameCString, tempFileTemplateCString);
 			mktemp(tempFileNameCString);
 
 			NSString* featuresFileName = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
 			free(tempFileNameCString);
 			
-			task = [[NSTask alloc] init];
+			NSTask* task = [[NSTask alloc] init];
 			
-			[task setLaunchPath:@"/usr/local/bin/fftExtract"];
-			
-			NSArray *args2;
-			
-			// Choose the args (TODO: This should use sonic annotator eventually)
-			if([extractor isEqualToString:@"chromagram"])
-			{
-				args2 = [NSArray arrayWithObjects:@"-p",@"/Users/mikej/planfile",@"-c", @"12", @"-s", @"250", [filesToOpen objectAtIndex:i], featuresFileName, nil];
-			}
-			else
-			{
-				args2 = [NSArray arrayWithObjects:@"-p",@"/Users/mikej/planfile",@"-m", @"13", @"-s", @"250", [filesToOpen objectAtIndex:i], featuresFileName, nil];
-			}
-			[task setArguments:args2];
+			[task setLaunchPath:@"/usr/local/bin/sonic-annotator"];
+			NSArray* args;
+			args = [NSArray arrayWithObjects:@"-t", n3FileName, @"-w", @"rdf", @"-r", @"--rdf-network", @"--rdf-one-file", featuresFileName, @"--rdf-force", [filesToOpen objectAtIndex:i], nil];
+			[task setArguments:args];
 			[task launch];
 			[task waitUntilExit];
 			[task release];
 			
+			NSTask* importTask = [[NSTask alloc] init];
+			[importTask setLaunchPath:@"/usr/local/bin/populate"];
+			args = [NSArray arrayWithObjects:featuresFileName, dbFilename, nil];
+			[importTask setArguments:args];
+			[importTask launch];
+			[importTask waitUntilExit];
+			[importTask release];
+			
 			NSString* val = [[filesToOpen objectAtIndex:i] retain];
 			NSString* key = [[[filesToOpen objectAtIndex:i] lastPathComponent] retain]; 
-			
-			adb_insert_t insert;
-			insert.features = [featuresFileName cStringUsingEncoding:NSUTF8StringEncoding];
-			insert.power = [powersFileName cStringUsingEncoding:NSUTF8StringEncoding];
-			insert.times = NULL;
-			insert.key = [key cStringUsingEncoding:NSUTF8StringEncoding];
-			
-			// Insert into db.
-			if(audiodb_insert(db, &insert))
-			{
-				// TODO: Show an error message.
-				NSLog(@"Weep: %@ %@ %@", featuresFileName, powersFileName, key);
-				continue;
-			}
-			
+		
 			// Update the plist store.
 			[trackMap setValue:val forKey:key];
-			[trackMap writeToFile:plistFilename atomically: YES];
+			[dbState writeToFile:plistFilename atomically: YES];
 			
+			
+			db = audiodb_open([dbFilename cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
 			[self updateStatus];
 		}
 		
-		newFrame.origin.x = [importSheet frame].origin.x;
-		newFrame.origin.y = [importSheet frame].origin.y + [extractingBox frame].size.height;
-		newFrame.size.width = [importSheet frame].size.width;
-		newFrame.size.height = [importSheet frame].size.height - [extractingBox frame].size.height;
-		
-		[importSheet setFrame:newFrame display:YES animate:YES];
-		
 		[NSApp endModalSession:session];
 		[importSheet orderOut:nil];
 		[NSApp endSheet:importSheet];
 		[indicator stopAnimation:self];
-		[extractingBox setHidden:TRUE];
 	}
 }
 
@@ -313,7 +358,7 @@
 	if([[tc identifier] isEqualToString:@"meter"])
 	{
 		NSLevelIndicatorCell *distance = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSRelevancyLevelIndicatorStyle];
-		[distance setFloatValue:10-[(NSNumber*)value floatValue]*100];
+		[distance setFloatValue:10.0f-[(NSNumber*)value floatValue]*100.0f];
 		return distance;
 	}
 	else
@@ -354,13 +399,13 @@
 {
 	if([tracksView numberOfSelectedRows] == 0)
 	{
-		[playBothButton setEnabled:FALSE];
-		[playResultButton setEnabled:FALSE];
+		[playBothButton setEnabled:NO];
+		[playResultButton setEnabled:NO];
 	}
 	else
 	{
-		[playBothButton setEnabled:TRUE];
-		[playResultButton setEnabled:TRUE];
+		[playBothButton setEnabled:YES];
+		[playResultButton setEnabled:YES];
 	}
 }
 
@@ -370,6 +415,11 @@
 -(IBAction)playResult:(id)sender
 {
 
+	if([tracksView selectedRow] == -1)
+	{
+		return;
+	}
+	
 	NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]];
 	NSString* value = [selectedRow objectForKey:@"key"];
 	float ipos = [[selectedRow objectForKey:@"ipos"] floatValue];
@@ -384,6 +434,7 @@
 			[queryTrack stop];
 		}
 		[queryTrack release];
+		queryTrack = Nil;
 	}
 	
 	if(resultTrack)
@@ -394,6 +445,7 @@
 			[resultTrack stop];
 		}
 		[resultTrack release];
+		resultTrack = Nil;
 	}
 	
 	resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain];
@@ -413,7 +465,6 @@
 	NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]];
 	NSString* value = [selectedRow objectForKey:@"key"];
 	float ipos = [[selectedRow objectForKey:@"ipos"] floatValue];
-	float qpos = [[selectedRow objectForKey:@"qpos"] floatValue];
 	NSString* filename = [trackMap objectForKey:value];
 	NSLog(@"Key: %@ Value: %@", value, filename);
 		
@@ -426,6 +477,7 @@
 			[queryTrack stop];
 		}
 		[queryTrack release];
+		queryTrack = Nil;
 	}
 	if(resultTrack)
 	{
@@ -435,11 +487,11 @@
 			[resultTrack stop];
 		}
 		[resultTrack release];
+		resultTrack = Nil;
 	}
 	
 	// Get query track and shift to start point
 	queryTrack = [[[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES] retain];
-	[queryTrack setCurrentTime:qpos];
 	[queryTrack setDelegate:self];
 	
 	[queryTrack play];
@@ -488,16 +540,25 @@
  */
 -(IBAction)chooseQuery:(id)sender
 {
+	[queryButton setEnabled:(selectedKey ? YES : NO)];
+	[NSApp beginSheet:querySheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
+	session = [NSApp beginModalSessionForWindow:querySheet];
+	[NSApp runModalSession:session];	
+}
+
+
+-(IBAction)selectQueryFile:(id)sender
+{
 	NSArray* fileTypes = [NSArray arrayWithObject:@"wav"];
 	NSOpenPanel* panel = [NSOpenPanel openPanel];
 	NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
 	if(response == NSFileHandlingPanelOKButton)
 	{
-		NSLog(@"%@", [panel filename]);
-		// Grab key
 		NSArray* opts = [trackMap allKeysForObject:[panel filename]];
 		if([opts count] != 1)
 		{
+			// TODO : Needs fixing!
+			
 			NSAlert *alert = [[[NSAlert alloc] init] autorelease];
 			[alert addButtonWithTitle:@"OK"];
 			[alert setMessageText:@"Track not found"];
@@ -509,34 +570,153 @@
 		{
 			selectedKey = [opts objectAtIndex:0];
 			[queryKey setStringValue:selectedKey];
+			[queryPath setStringValue:selectedKey];
 			selectedFilename = [[panel filename] retain];
-			[self performQuery];
+			[queryButton setEnabled:YES];
+			
+			[self resetLengths:self];
 		}
 	}
 }
 
+-(IBAction)resetLengths:(id)sender
+{
+	queryTrack = [[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES];
+
+	double samples = ([queryTrack duration]*44100.0f);
+	double hopSize = [[dbState objectForKey:@"hopsize"] doubleValue];
+	double winSize = [[dbState objectForKey:@"windowsize"] doubleValue];
+	
+	[queryLengthSeconds setDoubleValue:[queryTrack duration]];
+	[queryLengthVectors setDoubleValue:ceil((samples-winSize)/hopSize)];
+	
+	// For now, go with 0
+	[queryStartSeconds setDoubleValue:0];
+	[queryStartVectors setDoubleValue:0];
+
+	[queryLengthSeconds setEnabled:YES];
+	[queryLengthVectors setEnabled:YES];
+	[queryStartSeconds setEnabled:YES];
+	[queryStartVectors setEnabled:YES];
+	[resetButton setEnabled:YES];
+	[multipleCheckBox setEnabled:YES];
+	
+}
+
+- (void)controlTextDidChange:(NSNotification *)nd
+{
+	NSTextField *ed = [nd object];
+	
+	double hopSize = [[dbState objectForKey:@"hopsize"] doubleValue];
+	double winSize = [[dbState objectForKey:@"windowsize"] doubleValue];
+	
+	if(!queryTrack)
+	{
+		queryTrack = [[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES];
+	}
+	
+	double totalDuration = [queryTrack duration];
+	double samples = totalDuration * 44100.0f;
+	double totalVectors = ceil((samples-winSize)/hopSize);
+
+	double lengthSecs = [queryLengthSeconds doubleValue];
+	double startSecs = [queryStartSeconds doubleValue];
+	double lengthVectors = [queryLengthVectors doubleValue];
+	double startVectors = [queryStartVectors doubleValue];
+	
+	// Query Length
+	if (ed == queryLengthSeconds)
+	{
+		if(lengthSecs >= 0)
+		{
+			lengthVectors = ceil(((lengthSecs*44100.0f)-winSize)/hopSize);
+			if(lengthVectors < 0) {lengthVectors = 0; }
+			[queryLengthVectors setDoubleValue:lengthVectors];
+			
+		}
+	}
+	
+	if (ed == queryLengthVectors)
+	{
+		if(lengthVectors >= 0)
+		{
+			lengthSecs = ((hopSize*lengthVectors)+winSize)/44100.0f;
+			if(lengthSecs < 0) { lengthSecs = 0; }
+			[queryLengthSeconds setDoubleValue:lengthSecs];
+		}
+	}
+	
+	// Query start
+	if (ed == queryStartSeconds)
+	{
+		if(startSecs >= 0)
+		{
+			startVectors = ceil(((startSecs*44100.0f)-winSize)/hopSize);
+			if(startVectors < 0) { startVectors = 0; }
+			[queryStartVectors setDoubleValue:startVectors];
+		}
+	}
+	if (ed == queryStartVectors)
+	{
+		if(startVectors >= 0)
+		{
+			startSecs = ((hopSize*startVectors)+winSize)/44100.0f;
+			if(startSecs < 0) { startSecs = 0; }
+			[queryStartSeconds setDoubleValue:startSecs];
+		}
+	}
+	
+	if((lengthSecs + startSecs) > totalDuration || (lengthVectors + startVectors) > totalVectors || lengthVectors == 0)
+	{
+		[queryButton setEnabled:NO];
+	}
+	else if(![queryButton isEnabled])
+	{
+		[queryButton setEnabled:YES];
+	}
+}
+
+-(IBAction)cancelQuery:(id)sender
+{
+	[NSApp endModalSession:session];
+	[querySheet orderOut:nil];
+	[NSApp endSheet:querySheet];
+}
+
 /**
  * Actually perform the query. TODO: Monolithic.
  */
--(void)performQuery
+-(IBAction)performQuery:(id)sender
 {
+	[NSApp endModalSession:session];
+	[querySheet orderOut:nil];
+	[NSApp endSheet:querySheet];
+	
 	NSLog(@"Perform query! %@, %@", selectedKey, selectedFilename);
 	
 	adb_query_spec_t *spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
 	spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
 	
-	spec->qid.sequence_length = 20;
-	spec->qid.sequence_start = 0;
-	spec->qid.flags = 0;
+	spec->qid.sequence_length = [queryLengthVectors doubleValue];
+	spec->qid.sequence_start = [queryStartVectors doubleValue];
+	spec->qid.flags = 0;	
+//	spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
 	
-//	spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
 	spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
+	
+	if([multipleCheckBox state] == NSOnState)
+	{
+		spec->params.npoints = 100;
+	}
+	else
+	{
+		spec->params.npoints = 1;
+	}
+		
 	spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
 	
-	spec->params.npoints = 1;
 	spec->params.ntracks = 100;
-	spec->refine.radius = 5.0;
-	spec->refine.hopsize = 1;
+	//spec->refine.radius = 5.0;
 //	spec->refine.absolute_threshold = -6;
 //	spec->refine.relative_threshold = 10;
 //	spec->refine.duration_ratio = 0;
@@ -544,8 +724,8 @@
 	spec->refine.flags = 0;
 //	spec->refine.flags |= ADB_REFINE_ABSOLUTE_THRESHOLD;
 //	spec->refine.flags |= ADB_REFINE_RELATIVE_THRESHOLD;
-	spec->refine.flags |= ADB_REFINE_HOP_SIZE;
-	spec->refine.flags |= ADB_REFINE_RADIUS;
+//	spec->refine.flags |= ADB_REFINE_HOP_SIZE;
+	//spec->refine.flags |= ADB_REFINE_RADIUS;
 
 	adb_query_results_t *result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
 	spec->qid.datum->data = NULL;
@@ -557,6 +737,8 @@
 	int ok = audiodb_retrieve_datum(db, [selectedKey cStringUsingEncoding:NSUTF8StringEncoding], spec->qid.datum);
 	if(ok == 0)
 	{
+		
+		float hopSize = [[dbState objectForKey:@"hopsize"] floatValue];
 		NSLog(@"Got a datum");
 		result = audiodb_query_spec(db, spec);
 		if(result == NULL)
@@ -566,15 +748,17 @@
 		}
 		else
 		{
+			NSLog(@"Populate table: %d", result->nresults);
+			float divisor = (44100.0f/hopSize);
 			for(int i=0; i<result->nresults; i++)
 			{
+				
 				NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:4];
-				[dict setValue:[NSString stringWithFormat:@"%s", result->results[i].key] forKey:@"key"];
+				[dict setValue:[NSString stringWithFormat:@"%s", result->results[i].ikey] forKey:@"key"];
 				[dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"distance"];
 				[dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"meter"];
-				[dict setValue:[NSNumber numberWithFloat:result->results[i].qpos/4] forKey:@"qpos"];
-				[dict setValue:[NSNumber numberWithFloat:result->results[i].ipos/4] forKey:@"ipos"];
-				NSLog(@"%s qpos %d ipos %d", result->results[i].key, result->results[i].qpos/4, result->results[i].ipos/4);
+				[dict setValue:[NSNumber numberWithFloat:result->results[i].ipos/divisor] forKey:@"ipos"];
+				NSLog(@"%s ipos: %d, dist: %f", result->results[i].ikey,result->results[i].ipos, result->results[i].dist);
 				[results addObject: dict];
 			}
 		}