annotate examples/iAudioDB/AppController.m @ 705:1214e1da5b8d

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