annotate examples/iAudioDB/AppController.m @ 700:54974e8a6b87

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