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