comparison 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
comparison
equal deleted inserted replaced
698:10d3692e0b06 699:9a7d829bc492
3 // iAudioDB 3 // iAudioDB
4 // 4 //
5 // Created by Mike Jewell on 27/01/2010. 5 // Created by Mike Jewell on 27/01/2010.
6 // Copyright 2010 __MyCompanyName__. All rights reserved. 6 // Copyright 2010 __MyCompanyName__. All rights reserved.
7 // 7 //
8
9 #import "AppController.h" 8 #import "AppController.h"
9
10 10
11 11
12 @implementation AppController 12 @implementation AppController
13 13
14 -(id)init 14 -(id)init
16 [super init]; 16 [super init];
17 17
18 // A max of 100 results. 18 // A max of 100 results.
19 results = [[NSMutableArray alloc] initWithCapacity: 100]; 19 results = [[NSMutableArray alloc] initWithCapacity: 100];
20 20
21
22 return self; 21 return self;
23 } 22 }
24 23
24 - (void)awakeFromNib {
25 [tracksView setTarget:self];
26 [tracksView setDoubleAction:@selector(tableDoubleClick:)];
27 [self updateStatus];
28 }
29
30
31 - (IBAction)tableDoubleClick:(id)sender
32 {
33 [self playResult:Nil];
34 // NSLog(@"Table double clicked");
35 }
36
25 37
26 /** 38 /**
27 * Create a new database, given the selected filename. 39 * Create a new database, given the selected filename.
28 */ 40 */
29 -(IBAction)newDatabase:(id)sender 41 -(IBAction)newDatabase:(id)sender
30 { 42 {
43
44 [NSApp beginSheet:createSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
45 session = [NSApp beginModalSessionForWindow:createSheet];
46 [NSApp runModalSession:session];
47 }
48
49 /**
50 * Cancel the db creation (at configuration time).
51 */
52 -(IBAction)cancelCreate:(id)sender
53 {
54 [NSApp endModalSession:session];
55 [createSheet orderOut:nil];
56 [NSApp endSheet:createSheet];
57 }
58
59 -(IBAction)createDatabase:(id)sender
60 {
61 [self cancelCreate:self];
62
31 NSSavePanel* panel = [NSSavePanel savePanel]; 63 NSSavePanel* panel = [NSSavePanel savePanel];
32 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@""]; 64 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@""];
33 65
34 [results removeAllObjects]; 66 [results removeAllObjects];
35 [tracksView reloadData]; 67 [tracksView reloadData];
36 68
37 if(response == NSFileHandlingPanelOKButton) 69 if(response == NSFileHandlingPanelOKButton)
38 { 70 {
39 // TODO: Refactor this into a 'tidy' method. 71 // Work out which extractor to use
40 // Tidy any existing references up. 72 NSString* extractor = @"adb_chroma";
41 if(db) 73 // TODO: This should be stored with the n3.
42 { 74 int dim;
43 audiodb_close(db); 75 switch([extractorOptions selectedTag])
44 } 76 {
45 77 case 0:
46 if(dbFilename) 78 extractor = @"adb_chroma";
47 { 79 dim = 12;
48 [dbFilename release]; 80 break;
49 [dbName release]; 81 case 1:
50 [plistFilename release]; 82 extractor = @"adb_cq";
51 } 83 dim = 48;
52 84 break;
85 case 2:
86 extractor = @"qm_chroma";
87 dim = 12;
88 break;
89 case 3:
90 extractor = @"qm_mfcc";
91 dim = 12;
92 break;
93 }
94
95 // Calculate the max DB size
96 int vectors = ceil(([maxLengthField doubleValue] * 60.0f) / ([hopSizeField doubleValue] / 44100.0f));
97 int numtracks = [maxTracksField intValue];
98 int datasize = ceil((numtracks * vectors * dim * 8.0f) / 1024.0f / 1024.0f); // In MB
99
100 [self reset];
101
53 // Create new db, and set flags. 102 // Create new db, and set flags.
54 db = audiodb_create([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], 0, 0, 0); 103 db = audiodb_create([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], datasize, numtracks, dim);
55 audiodb_l2norm(db); 104 audiodb_l2norm(db);
56 audiodb_power(db); 105
57
58 // Store useful paths. 106 // Store useful paths.
59 dbName = [[[panel URL] relativePath] retain]; 107 dbName = [[[panel URL] relativePath] retain];
60 dbFilename = [[panel filename] retain]; 108 dbFilename = [[panel filename] retain];
61 plistFilename = [[NSString stringWithFormat:@"%@.plist", [dbFilename stringByDeletingPathExtension]] retain]; 109 plistFilename = [[NSString stringWithFormat:@"%@.plist", [dbFilename stringByDeletingPathExtension]] retain];
62 110
63 // Create the plist file (contains mapping from filename to key). 111 // Create the plist file (contains mapping from filename to key).
112 dbState = [[NSMutableDictionary alloc] init];
64 trackMap = [[NSMutableDictionary alloc] init]; 113 trackMap = [[NSMutableDictionary alloc] init];
65 [trackMap writeToFile:plistFilename atomically:YES]; 114 [dbState setValue:trackMap forKey:@"tracks"];
66 115 [dbState setValue:extractor forKey:@"extractor"];
116 [dbState setValue:[hopSizeField stringValue] forKey:@"hopsize"];
117 [dbState setValue:[windowSizeField stringValue] forKey:@"windowsize"];
118 [dbState writeToFile:plistFilename atomically:YES];
119
67 [queryKey setStringValue:@"None Selected"]; 120 [queryKey setStringValue:@"None Selected"];
68 [self updateStatus]; 121 [self updateStatus];
69 } 122 }
123 }
124
125 -(void)reset
126 {
127 // Tidy any existing references up.
128 if(db)
129 {
130 NSLog(@"Close db");
131 audiodb_close(db);
132 }
133
134 if(dbFilename)
135 {
136 NSLog(@"Tidy up filenames");
137 [dbFilename release];
138 [dbName release];
139 [plistFilename release];
140 [trackMap release];
141 [dbState release];
142 }
143
144 if(selectedKey)
145 {
146 NSLog(@"Released selected key: %@", selectedKey);
147 [selectedKey release];
148 selectedKey = Nil;
149 NSLog(@"Is now %@", selectedKey);
150 }
151
152 if(selectedKey)
153 {
154 NSLog(@"Still evals");
155 }
156
157 // Reset query flags
158 [queryPath setStringValue: @"No file selected"];
159 [queryLengthSeconds setDoubleValue:0];
160 [queryLengthVectors setDoubleValue:0];
161 [multipleCheckBox setState:NSOnState];
162 [queryStartSeconds setDoubleValue:0];
163 [queryStartVectors setDoubleValue:0];
164
165 [queryLengthSeconds setEnabled:NO];
166 [queryLengthVectors setEnabled:NO];
167 [queryStartSeconds setEnabled:NO];
168 [queryStartVectors setEnabled:NO];
169 [resetButton setEnabled:NO];
170 [multipleCheckBox setEnabled:NO];
70 } 171 }
71 172
72 /** 173 /**
73 * Open an existing adb (which must have a plist) 174 * Open an existing adb (which must have a plist)
74 */ 175 */
77 NSArray *fileTypes = [NSArray arrayWithObject:@"adb"]; 178 NSArray *fileTypes = [NSArray arrayWithObject:@"adb"];
78 NSOpenPanel* panel = [NSOpenPanel openPanel]; 179 NSOpenPanel* panel = [NSOpenPanel openPanel];
79 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes]; 180 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
80 if(response == NSFileHandlingPanelOKButton) 181 if(response == NSFileHandlingPanelOKButton)
81 { 182 {
82 // Tidy any existing references up. 183 [self reset];
83 if(db)
84 {
85 audiodb_close(db);
86 }
87
88 if(dbFilename)
89 {
90 [dbFilename release];
91 [dbName release];
92 [plistFilename release];
93 }
94 184
95 // Store useful paths. 185 // Store useful paths.
96 db = audiodb_open([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], O_RDWR); 186 NSLog(@"Open");
187 db = audiodb_open([[panel filename] cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
97 dbName = [[[panel URL] relativePath] retain]; 188 dbName = [[[panel URL] relativePath] retain];
98 dbFilename = [[panel filename] retain]; 189 dbFilename = [[panel filename] retain];
99 190
100 // TODO: Verify this exists! 191 // TODO: Verify this exists!
101 plistFilename = [[NSString stringWithFormat:@"%@.plist", [dbFilename stringByDeletingPathExtension]] retain]; 192 plistFilename = [[NSString stringWithFormat:@"%@.plist", [dbFilename stringByDeletingPathExtension]] retain];
103 // Clear out any old results. 194 // Clear out any old results.
104 [results removeAllObjects]; 195 [results removeAllObjects];
105 [tracksView reloadData]; 196 [tracksView reloadData];
106 197
107 [queryKey setStringValue:@"None Selected"]; 198 [queryKey setStringValue:@"None Selected"];
108 [self updateStatus];
109 199
110 adb_liszt_results_t* liszt_results = audiodb_liszt(db); 200 adb_liszt_results_t* liszt_results = audiodb_liszt(db);
111 201
112 for(int k=0; k<liszt_results->nresults; k++) 202 for(int k=0; k<liszt_results->nresults; k++)
113 { 203 {
114 NSMutableString *trackVal = [[NSMutableString alloc] init]; 204 NSMutableString *trackVal = [[NSMutableString alloc] init];
115 [trackVal appendFormat:@"%s", liszt_results->entries[k].key]; 205 [trackVal appendFormat:@"%s", liszt_results->entries[k].key];
116 } 206 }
117 207
118 audiodb_liszt_free_results(db, liszt_results); 208 audiodb_liszt_free_results(db, liszt_results);
119 trackMap = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistFilename] retain]; 209 dbState = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistFilename] retain];
210 trackMap = [[dbState objectForKey:@"tracks"] retain];
211
212 [self updateStatus];
213
120 NSLog(@"Size: %d", [trackMap count]); 214 NSLog(@"Size: %d", [trackMap count]);
121 } 215 }
122 } 216 }
123 217
218 -(IBAction)pathAction:(id)sender
219 {
220 NSLog(@"Path action");
221 }
222
124 /** 223 /**
125 * Update button states and status field based on current state. 224 * Update button states and status field based on current state.
126 */ 225 */
127 -(void)updateStatus 226 -(void)updateStatus
128 { 227 {
228 NSLog(@"Update status");
129 if(db) 229 if(db)
130 { 230 {
131 adb_status_ptr status = (adb_status_ptr)malloc(sizeof(struct adbstatus)); 231 NSLog(@"Got a db");
232 adb_status_t *status = (adb_status_t *)malloc(sizeof(adb_status_t));
132 int flags; 233 int flags;
133 flags = audiodb_status(db, status); 234 flags = audiodb_status(db, status);
134 [statusField setStringValue: [NSString stringWithFormat:@"Database: %@ Dimensions: %d Files: %d", dbName, status->dim, status->numFiles]]; 235 [statusField setStringValue: [NSString stringWithFormat:@"%@ Dim: %d Files: %d Hop: %@ Win: %@ Ext: %@",
135 [chooseButton setEnabled:YES]; 236 dbName,
237 status->dim,
238 status->numFiles,
239 [dbState objectForKey:@"hopsize"],
240 [dbState objectForKey:@"windowsize"],
241 [dbState objectForKey:@"extractor"]]];
242 [performQueryButton setEnabled:YES];
243 [importAudioButton setEnabled:YES];
136 } 244 }
137 else 245 else
138 { 246 {
139 [chooseButton setEnabled:NO]; 247 NSLog(@"No db");
140 [playBothButton setEnabled:FALSE]; 248 [performQueryButton setEnabled:NO];
141 [playResultButton setEnabled:FALSE]; 249 [importAudioButton setEnabled:NO];
142 } 250 [playBothButton setEnabled:NO];
143 } 251 [playResultButton setEnabled:NO];
144 252 [stopButton setEnabled:NO];
145 /** 253 }
146 * Get user's import choices.
147 */
148 -(IBAction)importAudio:(id)sender
149 {
150 [NSApp beginSheet:importSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
151 session = [NSApp beginModalSessionForWindow: importSheet];
152 [NSApp runModalSession:session];
153 }
154
155 /**
156 * Cancel the import (at configuration time).
157 */
158 -(IBAction)cancelImport:(id)sender;
159 {
160 [NSApp endModalSession:session];
161 [importSheet orderOut:nil];
162 [NSApp endSheet:importSheet];
163 } 254 }
164 255
165 /** 256 /**
166 * Choose the file(s) to be imported. 257 * Choose the file(s) to be imported.
167 * TODO: Currently handles the import process too - split this off. 258 * TODO: Currently handles the import process too - split this off.
168 */ 259 */
169 -(IBAction)selectFiles:(id)sender 260 -(IBAction)importAudio:(id)sender
170 { 261 {
171 [tracksView reloadData]; 262 [tracksView reloadData];
172 263
173 NSArray *fileTypes = [NSArray arrayWithObject:@"wav"]; 264 NSArray *fileTypes = [NSArray arrayWithObject:@"wav"];
174 NSOpenPanel* panel = [NSOpenPanel openPanel]; 265 NSOpenPanel* panel = [NSOpenPanel openPanel];
175 [panel setAllowsMultipleSelection:TRUE]; 266 [panel setAllowsMultipleSelection:TRUE];
176 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes]; 267 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
177 if(response == NSFileHandlingPanelOKButton) 268 if(response == NSFileHandlingPanelOKButton)
178 { 269 {
179 NSRect newFrame;
180
181 [extractingBox setHidden:FALSE];
182 newFrame.origin.x = [importSheet frame].origin.x;
183 newFrame.origin.y = [importSheet frame].origin.y - [extractingBox frame].size.height;
184 newFrame.size.width = [importSheet frame].size.width;
185 newFrame.size.height = [importSheet frame].size.height + [extractingBox frame].size.height;
186
187 [indicator startAnimation:self]; 270 [indicator startAnimation:self];
188 [importSheet setFrame:newFrame display:YES animate:YES]; 271
272 [NSApp beginSheet:importSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
273 session = [NSApp beginModalSessionForWindow: importSheet];
274 [NSApp runModalSession:session];
189 275
190 NSArray *filesToOpen = [panel filenames]; 276 NSArray *filesToOpen = [panel filenames];
191 277
192 NSLog(@"Begin import"); 278 NSString* extractor = [dbState objectForKey:@"extractor"];
193 279 NSString* extractorPath = [NSString stringWithFormat:@"/Applications/iAudioDB.app/rdf/%@.n3", extractor];
194 // Work out which extractor to use 280
195 NSString* extractor = @"chromagram"; 281 // TODO Shift this process into a separate function.
196 switch([extractorOptions selectedTag]) 282 // Create the customized extractor config
197 { 283 NSString* extractorContent = [NSString stringWithContentsOfFile:extractorPath];
198 case 0: 284 NSString* hopStr = [dbState objectForKey:@"hopsize"];
199 extractor = @"mfcc"; 285 NSString* winStr = [dbState objectForKey:@"windowsize"];
200 break; 286 NSString* newContent = [[extractorContent stringByReplacingOccurrencesOfString:@"HOP_SIZE" withString:hopStr]
201 case 1: 287 stringByReplacingOccurrencesOfString:@"WINDOW_SIZE" withString:winStr];
202 extractor = @"chromagram"; 288 NSString* n3FileName = [NSTemporaryDirectory() stringByAppendingPathComponent:@"extractor_config.n3"];
203 break; 289
204 } 290 NSError* error;
291 [newContent writeToFile:n3FileName atomically:YES encoding:NSASCIIStringEncoding error:&error];
205 292
206 for(int i=0; i<[filesToOpen count]; i++) 293 for(int i=0; i<[filesToOpen count]; i++)
207 { 294 {
208 // First extract powers 295 audiodb_close(db);
209 296 NSString* tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"features.XXXXXX"];
210 NSString *tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"powers.XXXXXX"]; 297 const char* tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
211 const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation]; 298 char* tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
212 char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
213 strcpy(tempFileNameCString, tempFileTemplateCString); 299 strcpy(tempFileNameCString, tempFileTemplateCString);
214 mktemp(tempFileNameCString); 300 mktemp(tempFileNameCString);
215 301
216 NSString* powersFileName = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)]; 302 NSString* featuresFileName = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
217 free(tempFileNameCString); 303 free(tempFileNameCString);
218 304
219 NSTask *task = [[NSTask alloc] init]; 305 NSTask* task = [[NSTask alloc] init];
220 [task setLaunchPath:@"/usr/local/bin/fftExtract"]; 306
221 NSArray *args = [NSArray arrayWithObjects:@"-P", @"-s", @"250", [filesToOpen objectAtIndex:i], powersFileName, nil]; 307 [task setLaunchPath:@"/usr/local/bin/sonic-annotator"];
308 NSArray* args;
309 args = [NSArray arrayWithObjects:@"-t", n3FileName, @"-w", @"rdf", @"-r", @"--rdf-network", @"--rdf-one-file", featuresFileName, @"--rdf-force", [filesToOpen objectAtIndex:i], nil];
222 [task setArguments:args]; 310 [task setArguments:args];
223 [task launch]; 311 [task launch];
224 [task waitUntilExit]; 312 [task waitUntilExit];
225 [task release]; 313 [task release];
226 314
227 // Then features 315 NSTask* importTask = [[NSTask alloc] init];
228 316 [importTask setLaunchPath:@"/usr/local/bin/populate"];
229 tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"features.XXXXXX"]; 317 args = [NSArray arrayWithObjects:featuresFileName, dbFilename, nil];
230 tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation]; 318 [importTask setArguments:args];
231 tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1); 319 [importTask launch];
232 strcpy(tempFileNameCString, tempFileTemplateCString); 320 [importTask waitUntilExit];
233 mktemp(tempFileNameCString); 321 [importTask release];
234
235 NSString* featuresFileName = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
236 free(tempFileNameCString);
237
238 task = [[NSTask alloc] init];
239
240 [task setLaunchPath:@"/usr/local/bin/fftExtract"];
241
242 NSArray *args2;
243
244 // Choose the args (TODO: This should use sonic annotator eventually)
245 if([extractor isEqualToString:@"chromagram"])
246 {
247 args2 = [NSArray arrayWithObjects:@"-p",@"/Users/mikej/planfile",@"-c", @"12", @"-s", @"250", [filesToOpen objectAtIndex:i], featuresFileName, nil];
248 }
249 else
250 {
251 args2 = [NSArray arrayWithObjects:@"-p",@"/Users/mikej/planfile",@"-m", @"13", @"-s", @"250", [filesToOpen objectAtIndex:i], featuresFileName, nil];
252 }
253 [task setArguments:args2];
254 [task launch];
255 [task waitUntilExit];
256 [task release];
257 322
258 NSString* val = [[filesToOpen objectAtIndex:i] retain]; 323 NSString* val = [[filesToOpen objectAtIndex:i] retain];
259 NSString* key = [[[filesToOpen objectAtIndex:i] lastPathComponent] retain]; 324 NSString* key = [[[filesToOpen objectAtIndex:i] lastPathComponent] retain];
260 325
261 adb_insert_t insert;
262 insert.features = [featuresFileName cStringUsingEncoding:NSUTF8StringEncoding];
263 insert.power = [powersFileName cStringUsingEncoding:NSUTF8StringEncoding];
264 insert.times = NULL;
265 insert.key = [key cStringUsingEncoding:NSUTF8StringEncoding];
266
267 // Insert into db.
268 if(audiodb_insert(db, &insert))
269 {
270 // TODO: Show an error message.
271 NSLog(@"Weep: %@ %@ %@", featuresFileName, powersFileName, key);
272 continue;
273 }
274
275 // Update the plist store. 326 // Update the plist store.
276 [trackMap setValue:val forKey:key]; 327 [trackMap setValue:val forKey:key];
277 [trackMap writeToFile:plistFilename atomically: YES]; 328 [dbState writeToFile:plistFilename atomically: YES];
278 329
330
331 db = audiodb_open([dbFilename cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
279 [self updateStatus]; 332 [self updateStatus];
280 } 333 }
281
282 newFrame.origin.x = [importSheet frame].origin.x;
283 newFrame.origin.y = [importSheet frame].origin.y + [extractingBox frame].size.height;
284 newFrame.size.width = [importSheet frame].size.width;
285 newFrame.size.height = [importSheet frame].size.height - [extractingBox frame].size.height;
286
287 [importSheet setFrame:newFrame display:YES animate:YES];
288 334
289 [NSApp endModalSession:session]; 335 [NSApp endModalSession:session];
290 [importSheet orderOut:nil]; 336 [importSheet orderOut:nil];
291 [NSApp endSheet:importSheet]; 337 [NSApp endSheet:importSheet];
292 [indicator stopAnimation:self]; 338 [indicator stopAnimation:self];
293 [extractingBox setHidden:TRUE];
294 } 339 }
295 } 340 }
296 341
297 /** 342 /**
298 * Required table methods begin here. 343 * Required table methods begin here.
311 id value = [result objectForKey:[tc identifier]]; 356 id value = [result objectForKey:[tc identifier]];
312 357
313 if([[tc identifier] isEqualToString:@"meter"]) 358 if([[tc identifier] isEqualToString:@"meter"])
314 { 359 {
315 NSLevelIndicatorCell *distance = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSRelevancyLevelIndicatorStyle]; 360 NSLevelIndicatorCell *distance = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSRelevancyLevelIndicatorStyle];
316 [distance setFloatValue:10-[(NSNumber*)value floatValue]*100]; 361 [distance setFloatValue:10.0f-[(NSNumber*)value floatValue]*100.0f];
317 return distance; 362 return distance;
318 } 363 }
319 else 364 else
320 { 365 {
321 return value; 366 return value;
352 */ 397 */
353 -(IBAction)selectedChanged:(id)sender 398 -(IBAction)selectedChanged:(id)sender
354 { 399 {
355 if([tracksView numberOfSelectedRows] == 0) 400 if([tracksView numberOfSelectedRows] == 0)
356 { 401 {
357 [playBothButton setEnabled:FALSE]; 402 [playBothButton setEnabled:NO];
358 [playResultButton setEnabled:FALSE]; 403 [playResultButton setEnabled:NO];
359 } 404 }
360 else 405 else
361 { 406 {
362 [playBothButton setEnabled:TRUE]; 407 [playBothButton setEnabled:YES];
363 [playResultButton setEnabled:TRUE]; 408 [playResultButton setEnabled:YES];
364 } 409 }
365 } 410 }
366 411
367 /** 412 /**
368 * Play just the result track. 413 * Play just the result track.
369 */ 414 */
370 -(IBAction)playResult:(id)sender 415 -(IBAction)playResult:(id)sender
371 { 416 {
372 417
418 if([tracksView selectedRow] == -1)
419 {
420 return;
421 }
422
373 NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]]; 423 NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]];
374 NSString* value = [selectedRow objectForKey:@"key"]; 424 NSString* value = [selectedRow objectForKey:@"key"];
375 float ipos = [[selectedRow objectForKey:@"ipos"] floatValue]; 425 float ipos = [[selectedRow objectForKey:@"ipos"] floatValue];
376 NSString* filename = [trackMap objectForKey:value]; 426 NSString* filename = [trackMap objectForKey:value];
377 NSLog(@"Key: %@ Value: %@", value, filename); 427 NSLog(@"Key: %@ Value: %@", value, filename);
382 { 432 {
383 [queryTrack setDelegate:Nil]; 433 [queryTrack setDelegate:Nil];
384 [queryTrack stop]; 434 [queryTrack stop];
385 } 435 }
386 [queryTrack release]; 436 [queryTrack release];
437 queryTrack = Nil;
387 } 438 }
388 439
389 if(resultTrack) 440 if(resultTrack)
390 { 441 {
391 if([resultTrack isPlaying]) 442 if([resultTrack isPlaying])
392 { 443 {
393 [resultTrack setDelegate:Nil]; 444 [resultTrack setDelegate:Nil];
394 [resultTrack stop]; 445 [resultTrack stop];
395 } 446 }
396 [resultTrack release]; 447 [resultTrack release];
448 resultTrack = Nil;
397 } 449 }
398 450
399 resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain]; 451 resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain];
400 [resultTrack setCurrentTime:ipos]; 452 [resultTrack setCurrentTime:ipos];
401 [resultTrack setDelegate:self]; 453 [resultTrack setDelegate:self];
411 { 463 {
412 464
413 NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]]; 465 NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]];
414 NSString* value = [selectedRow objectForKey:@"key"]; 466 NSString* value = [selectedRow objectForKey:@"key"];
415 float ipos = [[selectedRow objectForKey:@"ipos"] floatValue]; 467 float ipos = [[selectedRow objectForKey:@"ipos"] floatValue];
416 float qpos = [[selectedRow objectForKey:@"qpos"] floatValue];
417 NSString* filename = [trackMap objectForKey:value]; 468 NSString* filename = [trackMap objectForKey:value];
418 NSLog(@"Key: %@ Value: %@", value, filename); 469 NSLog(@"Key: %@ Value: %@", value, filename);
419 470
420 if(queryTrack) 471 if(queryTrack)
421 { 472 {
424 { 475 {
425 [queryTrack setDelegate:Nil]; 476 [queryTrack setDelegate:Nil];
426 [queryTrack stop]; 477 [queryTrack stop];
427 } 478 }
428 [queryTrack release]; 479 [queryTrack release];
480 queryTrack = Nil;
429 } 481 }
430 if(resultTrack) 482 if(resultTrack)
431 { 483 {
432 if([resultTrack isPlaying]) 484 if([resultTrack isPlaying])
433 { 485 {
434 [resultTrack setDelegate:Nil]; 486 [resultTrack setDelegate:Nil];
435 [resultTrack stop]; 487 [resultTrack stop];
436 } 488 }
437 [resultTrack release]; 489 [resultTrack release];
490 resultTrack = Nil;
438 } 491 }
439 492
440 // Get query track and shift to start point 493 // Get query track and shift to start point
441 queryTrack = [[[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES] retain]; 494 queryTrack = [[[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES] retain];
442 [queryTrack setCurrentTime:qpos];
443 [queryTrack setDelegate:self]; 495 [queryTrack setDelegate:self];
444 496
445 [queryTrack play]; 497 [queryTrack play];
446 498
447 resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain]; 499 resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain];
486 /** 538 /**
487 * Select an audio file, determine the key, and fire off a query. 539 * Select an audio file, determine the key, and fire off a query.
488 */ 540 */
489 -(IBAction)chooseQuery:(id)sender 541 -(IBAction)chooseQuery:(id)sender
490 { 542 {
543 [queryButton setEnabled:(selectedKey ? YES : NO)];
544 [NSApp beginSheet:querySheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
545 session = [NSApp beginModalSessionForWindow:querySheet];
546 [NSApp runModalSession:session];
547 }
548
549
550 -(IBAction)selectQueryFile:(id)sender
551 {
491 NSArray* fileTypes = [NSArray arrayWithObject:@"wav"]; 552 NSArray* fileTypes = [NSArray arrayWithObject:@"wav"];
492 NSOpenPanel* panel = [NSOpenPanel openPanel]; 553 NSOpenPanel* panel = [NSOpenPanel openPanel];
493 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes]; 554 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
494 if(response == NSFileHandlingPanelOKButton) 555 if(response == NSFileHandlingPanelOKButton)
495 { 556 {
496 NSLog(@"%@", [panel filename]);
497 // Grab key
498 NSArray* opts = [trackMap allKeysForObject:[panel filename]]; 557 NSArray* opts = [trackMap allKeysForObject:[panel filename]];
499 if([opts count] != 1) 558 if([opts count] != 1)
500 { 559 {
560 // TODO : Needs fixing!
561
501 NSAlert *alert = [[[NSAlert alloc] init] autorelease]; 562 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
502 [alert addButtonWithTitle:@"OK"]; 563 [alert addButtonWithTitle:@"OK"];
503 [alert setMessageText:@"Track not found"]; 564 [alert setMessageText:@"Track not found"];
504 [alert setInformativeText:@"Make sure you have specified a valid track identifier."]; 565 [alert setInformativeText:@"Make sure you have specified a valid track identifier."];
505 [alert setAlertStyle:NSWarningAlertStyle]; 566 [alert setAlertStyle:NSWarningAlertStyle];
507 } 568 }
508 else 569 else
509 { 570 {
510 selectedKey = [opts objectAtIndex:0]; 571 selectedKey = [opts objectAtIndex:0];
511 [queryKey setStringValue:selectedKey]; 572 [queryKey setStringValue:selectedKey];
573 [queryPath setStringValue:selectedKey];
512 selectedFilename = [[panel filename] retain]; 574 selectedFilename = [[panel filename] retain];
513 [self performQuery]; 575 [queryButton setEnabled:YES];
514 } 576
515 } 577 [self resetLengths:self];
578 }
579 }
580 }
581
582 -(IBAction)resetLengths:(id)sender
583 {
584 queryTrack = [[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES];
585
586 double samples = ([queryTrack duration]*44100.0f);
587 double hopSize = [[dbState objectForKey:@"hopsize"] doubleValue];
588 double winSize = [[dbState objectForKey:@"windowsize"] doubleValue];
589
590 [queryLengthSeconds setDoubleValue:[queryTrack duration]];
591 [queryLengthVectors setDoubleValue:ceil((samples-winSize)/hopSize)];
592
593 // For now, go with 0
594 [queryStartSeconds setDoubleValue:0];
595 [queryStartVectors setDoubleValue:0];
596
597 [queryLengthSeconds setEnabled:YES];
598 [queryLengthVectors setEnabled:YES];
599 [queryStartSeconds setEnabled:YES];
600 [queryStartVectors setEnabled:YES];
601 [resetButton setEnabled:YES];
602 [multipleCheckBox setEnabled:YES];
603
604 }
605
606 - (void)controlTextDidChange:(NSNotification *)nd
607 {
608 NSTextField *ed = [nd object];
609
610 double hopSize = [[dbState objectForKey:@"hopsize"] doubleValue];
611 double winSize = [[dbState objectForKey:@"windowsize"] doubleValue];
612
613 if(!queryTrack)
614 {
615 queryTrack = [[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES];
616 }
617
618 double totalDuration = [queryTrack duration];
619 double samples = totalDuration * 44100.0f;
620 double totalVectors = ceil((samples-winSize)/hopSize);
621
622 double lengthSecs = [queryLengthSeconds doubleValue];
623 double startSecs = [queryStartSeconds doubleValue];
624 double lengthVectors = [queryLengthVectors doubleValue];
625 double startVectors = [queryStartVectors doubleValue];
626
627 // Query Length
628 if (ed == queryLengthSeconds)
629 {
630 if(lengthSecs >= 0)
631 {
632 lengthVectors = ceil(((lengthSecs*44100.0f)-winSize)/hopSize);
633 if(lengthVectors < 0) {lengthVectors = 0; }
634 [queryLengthVectors setDoubleValue:lengthVectors];
635
636 }
637 }
638
639 if (ed == queryLengthVectors)
640 {
641 if(lengthVectors >= 0)
642 {
643 lengthSecs = ((hopSize*lengthVectors)+winSize)/44100.0f;
644 if(lengthSecs < 0) { lengthSecs = 0; }
645 [queryLengthSeconds setDoubleValue:lengthSecs];
646 }
647 }
648
649 // Query start
650 if (ed == queryStartSeconds)
651 {
652 if(startSecs >= 0)
653 {
654 startVectors = ceil(((startSecs*44100.0f)-winSize)/hopSize);
655 if(startVectors < 0) { startVectors = 0; }
656 [queryStartVectors setDoubleValue:startVectors];
657 }
658 }
659 if (ed == queryStartVectors)
660 {
661 if(startVectors >= 0)
662 {
663 startSecs = ((hopSize*startVectors)+winSize)/44100.0f;
664 if(startSecs < 0) { startSecs = 0; }
665 [queryStartSeconds setDoubleValue:startSecs];
666 }
667 }
668
669 if((lengthSecs + startSecs) > totalDuration || (lengthVectors + startVectors) > totalVectors || lengthVectors == 0)
670 {
671 [queryButton setEnabled:NO];
672 }
673 else if(![queryButton isEnabled])
674 {
675 [queryButton setEnabled:YES];
676 }
677 }
678
679 -(IBAction)cancelQuery:(id)sender
680 {
681 [NSApp endModalSession:session];
682 [querySheet orderOut:nil];
683 [NSApp endSheet:querySheet];
516 } 684 }
517 685
518 /** 686 /**
519 * Actually perform the query. TODO: Monolithic. 687 * Actually perform the query. TODO: Monolithic.
520 */ 688 */
521 -(void)performQuery 689 -(IBAction)performQuery:(id)sender
522 { 690 {
691 [NSApp endModalSession:session];
692 [querySheet orderOut:nil];
693 [NSApp endSheet:querySheet];
694
523 NSLog(@"Perform query! %@, %@", selectedKey, selectedFilename); 695 NSLog(@"Perform query! %@, %@", selectedKey, selectedFilename);
524 696
525 adb_query_spec_t *spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t)); 697 adb_query_spec_t *spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
526 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t)); 698 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
527 699
528 spec->qid.sequence_length = 20; 700 spec->qid.sequence_length = [queryLengthVectors doubleValue];
529 spec->qid.sequence_start = 0; 701 spec->qid.sequence_start = [queryStartVectors doubleValue];
530 spec->qid.flags = 0; 702 spec->qid.flags = 0;
531
532 // spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE; 703 // spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
704
533 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK; 705 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
706
707 if([multipleCheckBox state] == NSOnState)
708 {
709 spec->params.npoints = 100;
710 }
711 else
712 {
713 spec->params.npoints = 1;
714 }
715
534 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED; 716 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
535 717
536 spec->params.npoints = 1;
537 spec->params.ntracks = 100; 718 spec->params.ntracks = 100;
538 spec->refine.radius = 5.0; 719 //spec->refine.radius = 5.0;
539 spec->refine.hopsize = 1;
540 // spec->refine.absolute_threshold = -6; 720 // spec->refine.absolute_threshold = -6;
541 // spec->refine.relative_threshold = 10; 721 // spec->refine.relative_threshold = 10;
542 // spec->refine.duration_ratio = 0; 722 // spec->refine.duration_ratio = 0;
543 723
544 spec->refine.flags = 0; 724 spec->refine.flags = 0;
545 // spec->refine.flags |= ADB_REFINE_ABSOLUTE_THRESHOLD; 725 // spec->refine.flags |= ADB_REFINE_ABSOLUTE_THRESHOLD;
546 // spec->refine.flags |= ADB_REFINE_RELATIVE_THRESHOLD; 726 // spec->refine.flags |= ADB_REFINE_RELATIVE_THRESHOLD;
547 spec->refine.flags |= ADB_REFINE_HOP_SIZE; 727 // spec->refine.flags |= ADB_REFINE_HOP_SIZE;
548 spec->refine.flags |= ADB_REFINE_RADIUS; 728 //spec->refine.flags |= ADB_REFINE_RADIUS;
549 729
550 adb_query_results_t *result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t)); 730 adb_query_results_t *result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
551 spec->qid.datum->data = NULL; 731 spec->qid.datum->data = NULL;
552 spec->qid.datum->power = NULL; 732 spec->qid.datum->power = NULL;
553 spec->qid.datum->times = NULL; 733 spec->qid.datum->times = NULL;
555 [results removeAllObjects]; 735 [results removeAllObjects];
556 736
557 int ok = audiodb_retrieve_datum(db, [selectedKey cStringUsingEncoding:NSUTF8StringEncoding], spec->qid.datum); 737 int ok = audiodb_retrieve_datum(db, [selectedKey cStringUsingEncoding:NSUTF8StringEncoding], spec->qid.datum);
558 if(ok == 0) 738 if(ok == 0)
559 { 739 {
740
741 float hopSize = [[dbState objectForKey:@"hopsize"] floatValue];
560 NSLog(@"Got a datum"); 742 NSLog(@"Got a datum");
561 result = audiodb_query_spec(db, spec); 743 result = audiodb_query_spec(db, spec);
562 if(result == NULL) 744 if(result == NULL)
563 { 745 {
564 746
565 NSLog(@"No results"); 747 NSLog(@"No results");
566 } 748 }
567 else 749 else
568 { 750 {
751 NSLog(@"Populate table: %d", result->nresults);
752 float divisor = (44100.0f/hopSize);
569 for(int i=0; i<result->nresults; i++) 753 for(int i=0; i<result->nresults; i++)
570 { 754 {
755
571 NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:4]; 756 NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:4];
572 [dict setValue:[NSString stringWithFormat:@"%s", result->results[i].key] forKey:@"key"]; 757 [dict setValue:[NSString stringWithFormat:@"%s", result->results[i].ikey] forKey:@"key"];
573 [dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"distance"]; 758 [dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"distance"];
574 [dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"meter"]; 759 [dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"meter"];
575 [dict setValue:[NSNumber numberWithFloat:result->results[i].qpos/4] forKey:@"qpos"]; 760 [dict setValue:[NSNumber numberWithFloat:result->results[i].ipos/divisor] forKey:@"ipos"];
576 [dict setValue:[NSNumber numberWithFloat:result->results[i].ipos/4] forKey:@"ipos"]; 761 NSLog(@"%s ipos: %d, dist: %f", result->results[i].ikey,result->results[i].ipos, result->results[i].dist);
577 NSLog(@"%s qpos %d ipos %d", result->results[i].key, result->results[i].qpos/4, result->results[i].ipos/4);
578 [results addObject: dict]; 762 [results addObject: dict];
579 } 763 }
580 } 764 }
581 765
582 NSSortDescriptor *distSort = [[NSSortDescriptor alloc]initWithKey:@"meter" ascending:YES]; 766 NSSortDescriptor *distSort = [[NSSortDescriptor alloc]initWithKey:@"meter" ascending:YES];