annotate examples/iAudioDB/AppController.m @ 702:6d8539709d9c

Switching over to use sample rate where possible - poss bug in query start specification.
author mas01mj
date Fri, 30 Apr 2010 14:57:30 +0000
parents e21abbac820e
children 14b48e1b9ca4
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@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@702 96 int vectors = ceil(([maxLengthField doubleValue] * 60.0f) / (([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@700 228 [statusField setStringValue: [NSString stringWithFormat:@"%@ Dim: %d Files: %d Hop: %@ 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@700 310 [task setLaunchPath:@"/usr/local/bin/sonic-annotator"];
mas01mj@700 311 NSArray* args;
mas01mj@700 312 args = [NSArray arrayWithObjects:@"-t", n3FileName, @"-w", @"rdf", @"-r", @"--rdf-network", @"--rdf-one-file", featuresFileName, @"--rdf-force", filename, nil];
mas01mj@700 313 [task setArguments:args];
mas01mj@700 314 [task launch];
mas01mj@700 315 [task waitUntilExit];
mas01mj@700 316 [task release];
mas01mj@700 317
mas01mj@700 318 // Populate the audioDB instance
mas01mj@700 319 NSTask* importTask = [[NSTask alloc] init];
mas01mj@700 320 [importTask setLaunchPath:@"/usr/local/bin/populate"];
mas01mj@700 321 args = [NSArray arrayWithObjects:featuresFileName, dbFilename, nil];
mas01mj@700 322 [importTask setArguments:args];
mas01mj@700 323 [importTask launch];
mas01mj@700 324 [importTask waitUntilExit];
mas01mj@700 325 [importTask release];
mas01mj@700 326
mas01mj@700 327 NSString* val = [filename retain];
mas01mj@700 328 NSString* key = [[filename lastPathComponent] retain];
mas01mj@700 329
mas01mj@700 330 // Update the plist store.
mas01mj@700 331 [trackMap setValue:val forKey:key];
mas01mj@700 332 [dbState writeToFile:plistFilename atomically: YES];
mas01mj@700 333
mas01mj@700 334 }
mas01mj@700 335
mas01mj@669 336 /**
mas01mj@669 337 * Choose the file(s) to be imported.
mas01mj@669 338 */
mas01mj@699 339 -(IBAction)importAudio:(id)sender
mas01mj@669 340 {
mas01mj@669 341 [tracksView reloadData];
mas01mj@669 342
mas01mj@669 343 NSArray *fileTypes = [NSArray arrayWithObject:@"wav"];
mas01mj@669 344 NSOpenPanel* panel = [NSOpenPanel openPanel];
mas01mj@669 345 [panel setAllowsMultipleSelection:TRUE];
mas01mj@669 346 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
mas01mj@669 347 if(response == NSFileHandlingPanelOKButton)
mas01mj@669 348 {
mas01mj@699 349 [indicator startAnimation:self];
mas01mj@692 350
mas01mj@699 351 [NSApp beginSheet:importSheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
mas01mj@699 352 session = [NSApp beginModalSessionForWindow: importSheet];
mas01mj@699 353 [NSApp runModalSession:session];
mas01mj@669 354
mas01mj@669 355 NSArray *filesToOpen = [panel filenames];
mas01mj@669 356
mas01mj@699 357 NSString* extractor = [dbState objectForKey:@"extractor"];
mas01mj@699 358 NSString* extractorPath = [NSString stringWithFormat:@"/Applications/iAudioDB.app/rdf/%@.n3", extractor];
mas01mj@669 359
mas01mj@669 360 for(int i=0; i<[filesToOpen count]; i++)
mas01mj@699 361 {
mas01mj@699 362 audiodb_close(db);
mas01mj@700 363
mas01mj@700 364 // Get the sample rate for the audio file
mas01mj@700 365
mas01mj@700 366 [self importFile:[filesToOpen objectAtIndex:i] withExtractorConfig:extractorPath];
mas01mj@699 367 db = audiodb_open([dbFilename cStringUsingEncoding:NSUTF8StringEncoding], O_RDONLY);
mas01mj@669 368 [self updateStatus];
mas01mj@669 369 }
mas01mj@669 370
mas01mj@669 371 [NSApp endModalSession:session];
mas01mj@669 372 [importSheet orderOut:nil];
mas01mj@669 373 [NSApp endSheet:importSheet];
mas01mj@669 374 [indicator stopAnimation:self];
mas01mj@669 375 }
mas01mj@669 376 }
mas01mj@669 377
mas01mj@669 378 /**
mas01mj@669 379 * Required table methods begin here.
mas01mj@669 380 */
mas01mj@669 381 -(int)numberOfRowsInTableView:(NSTableView *)v
mas01mj@669 382 {
mas01mj@669 383 return [results count];
mas01mj@669 384 }
mas01mj@669 385
mas01mj@669 386 /**
mas01mj@669 387 * Return appropriate values - or the distance indicator if it's the meter column.
mas01mj@669 388 */
mas01mj@669 389 -(id)tableView:(NSTableView *)v objectValueForTableColumn:(NSTableColumn *)tc row:(NSInteger)row
mas01mj@669 390 {
mas01mj@669 391 id result = [results objectAtIndex:row];
mas01mj@669 392 id value = [result objectForKey:[tc identifier]];
mas01mj@669 393
mas01mj@669 394 if([[tc identifier] isEqualToString:@"meter"])
mas01mj@669 395 {
mas01mj@669 396 NSLevelIndicatorCell *distance = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSRelevancyLevelIndicatorStyle];
mas01mj@699 397 [distance setFloatValue:10.0f-[(NSNumber*)value floatValue]*100.0f];
mas01mj@669 398 return distance;
mas01mj@669 399 }
mas01mj@669 400 else
mas01mj@669 401 {
mas01mj@669 402 return value;
mas01mj@669 403 }
mas01mj@669 404 }
mas01mj@669 405
mas01mj@669 406 /**
mas01mj@669 407 * Handle column sorting.
mas01mj@669 408 */
mas01mj@669 409 - (void)tableView:(NSTableView *)v sortDescriptorsDidChange:(NSArray *)oldDescriptors
mas01mj@669 410 {
mas01mj@669 411 [results sortUsingDescriptors:[v sortDescriptors]];
mas01mj@669 412 [v reloadData];
mas01mj@669 413 }
mas01mj@669 414
mas01mj@669 415 /**
mas01mj@669 416 * Only enable the import menu option if a database is loaded.
mas01mj@669 417 */
mas01mj@669 418 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
mas01mj@669 419 {
mas01mj@669 420 SEL theAction = [anItem action];
mas01mj@669 421 if (theAction == @selector(importAudio:))
mas01mj@669 422 {
mas01mj@669 423 if(!db)
mas01mj@669 424 {
mas01mj@669 425 return NO;
mas01mj@669 426 }
mas01mj@669 427 }
mas01mj@669 428 return YES;
mas01mj@669 429 }
mas01mj@669 430
mas01mj@669 431 /**
mas01mj@669 432 * Ensure play buttons are only enabled if a track is selected.
mas01mj@669 433 */
mas01mj@669 434 -(IBAction)selectedChanged:(id)sender
mas01mj@669 435 {
mas01mj@669 436 if([tracksView numberOfSelectedRows] == 0)
mas01mj@669 437 {
mas01mj@699 438 [playBothButton setEnabled:NO];
mas01mj@699 439 [playResultButton setEnabled:NO];
mas01mj@669 440 }
mas01mj@669 441 else
mas01mj@669 442 {
mas01mj@699 443 [playBothButton setEnabled:YES];
mas01mj@699 444 [playResultButton setEnabled:YES];
mas01mj@669 445 }
mas01mj@669 446 }
mas01mj@669 447
mas01mj@669 448 /**
mas01mj@669 449 * Play just the result track.
mas01mj@669 450 */
mas01mj@669 451 -(IBAction)playResult:(id)sender
mas01mj@669 452 {
mas01mj@669 453
mas01mj@699 454 if([tracksView selectedRow] == -1)
mas01mj@699 455 {
mas01mj@699 456 return;
mas01mj@699 457 }
mas01mj@699 458
mas01mj@669 459 NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]];
mas01mj@669 460 NSString* value = [selectedRow objectForKey:@"key"];
mas01mj@669 461 float ipos = [[selectedRow objectForKey:@"ipos"] floatValue];
mas01mj@669 462 NSString* filename = [trackMap objectForKey:value];
mas01mj@669 463 NSLog(@"Key: %@ Value: %@", value, filename);
mas01mj@669 464
mas01mj@669 465 if(queryTrack)
mas01mj@669 466 {
mas01mj@669 467 if([queryTrack isPlaying])
mas01mj@669 468 {
mas01mj@669 469 [queryTrack setDelegate:Nil];
mas01mj@669 470 [queryTrack stop];
mas01mj@669 471 }
mas01mj@669 472 [queryTrack release];
mas01mj@699 473 queryTrack = Nil;
mas01mj@669 474 }
mas01mj@669 475
mas01mj@669 476 if(resultTrack)
mas01mj@669 477 {
mas01mj@669 478 if([resultTrack isPlaying])
mas01mj@669 479 {
mas01mj@669 480 [resultTrack setDelegate:Nil];
mas01mj@669 481 [resultTrack stop];
mas01mj@669 482 }
mas01mj@669 483 [resultTrack release];
mas01mj@699 484 resultTrack = Nil;
mas01mj@669 485 }
mas01mj@669 486
mas01mj@669 487 resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain];
mas01mj@669 488 [resultTrack setCurrentTime:ipos];
mas01mj@669 489 [resultTrack setDelegate:self];
mas01mj@669 490 [resultTrack play];
mas01mj@669 491
mas01mj@669 492 [stopButton setEnabled:YES];
mas01mj@669 493 }
mas01mj@669 494
mas01mj@669 495 /**
mas01mj@669 496 * Play the result and query simultaneously.
mas01mj@669 497 */
mas01mj@669 498 -(IBAction)playBoth:(id)sender
mas01mj@669 499 {
mas01mj@669 500
mas01mj@669 501 NSDictionary* selectedRow = [results objectAtIndex:[tracksView selectedRow]];
mas01mj@669 502 NSString* value = [selectedRow objectForKey:@"key"];
mas01mj@669 503 float ipos = [[selectedRow objectForKey:@"ipos"] floatValue];
mas01mj@669 504 NSString* filename = [trackMap objectForKey:value];
mas01mj@669 505 NSLog(@"Key: %@ Value: %@", value, filename);
mas01mj@669 506
mas01mj@669 507 if(queryTrack)
mas01mj@669 508 {
mas01mj@669 509
mas01mj@669 510 if([queryTrack isPlaying])
mas01mj@669 511 {
mas01mj@669 512 [queryTrack setDelegate:Nil];
mas01mj@669 513 [queryTrack stop];
mas01mj@669 514 }
mas01mj@669 515 [queryTrack release];
mas01mj@699 516 queryTrack = Nil;
mas01mj@669 517 }
mas01mj@669 518 if(resultTrack)
mas01mj@669 519 {
mas01mj@669 520 if([resultTrack isPlaying])
mas01mj@669 521 {
mas01mj@669 522 [resultTrack setDelegate:Nil];
mas01mj@669 523 [resultTrack stop];
mas01mj@669 524 }
mas01mj@669 525 [resultTrack release];
mas01mj@699 526 resultTrack = Nil;
mas01mj@669 527 }
mas01mj@669 528
mas01mj@669 529 // Get query track and shift to start point
mas01mj@669 530 queryTrack = [[[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES] retain];
mas01mj@669 531 [queryTrack setDelegate:self];
mas01mj@669 532
mas01mj@669 533 [queryTrack play];
mas01mj@669 534
mas01mj@669 535 resultTrack = [[[NSSound alloc] initWithContentsOfFile:filename byReference:YES] retain];
mas01mj@669 536 [resultTrack setCurrentTime:ipos];
mas01mj@669 537 [resultTrack setDelegate:self];
mas01mj@669 538 [resultTrack play];
mas01mj@669 539
mas01mj@669 540 [stopButton setEnabled:YES];
mas01mj@669 541 }
mas01mj@669 542
mas01mj@669 543 /**
mas01mj@669 544 * Disable the stop button after playback of both tracks.
mas01mj@669 545 */
mas01mj@669 546 - (void)sound:(NSSound *)sound didFinishPlaying:(BOOL)playbackSuccessful
mas01mj@669 547 {
mas01mj@669 548
mas01mj@669 549 if((queryTrack && [queryTrack isPlaying]) || (resultTrack && [resultTrack isPlaying]))
mas01mj@669 550 {
mas01mj@669 551 return;
mas01mj@669 552 }
mas01mj@669 553 else
mas01mj@669 554 {
mas01mj@669 555 [stopButton setEnabled:NO];
mas01mj@669 556 }
mas01mj@669 557 }
mas01mj@669 558
mas01mj@669 559 /**
mas01mj@669 560 * Stop playback.
mas01mj@669 561 */
mas01mj@669 562 -(IBAction)stopPlay:(id)sender
mas01mj@669 563 {
mas01mj@669 564 if(queryTrack)
mas01mj@669 565 {
mas01mj@669 566 [queryTrack stop];
mas01mj@669 567 }
mas01mj@669 568 if(resultTrack)
mas01mj@669 569 {
mas01mj@669 570 [resultTrack stop];
mas01mj@669 571 }
mas01mj@669 572 }
mas01mj@669 573
mas01mj@669 574 /**
mas01mj@669 575 * Select an audio file, determine the key, and fire off a query.
mas01mj@669 576 */
mas01mj@669 577 -(IBAction)chooseQuery:(id)sender
mas01mj@669 578 {
mas01mj@699 579 [queryButton setEnabled:(selectedKey ? YES : NO)];
mas01mj@699 580 [NSApp beginSheet:querySheet modalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
mas01mj@699 581 session = [NSApp beginModalSessionForWindow:querySheet];
mas01mj@699 582 [NSApp runModalSession:session];
mas01mj@699 583 }
mas01mj@699 584
mas01mj@699 585
mas01mj@699 586 -(IBAction)selectQueryFile:(id)sender
mas01mj@699 587 {
mas01mj@669 588 NSArray* fileTypes = [NSArray arrayWithObject:@"wav"];
mas01mj@669 589 NSOpenPanel* panel = [NSOpenPanel openPanel];
mas01mj@669 590 NSInteger response = [panel runModalForDirectory:NSHomeDirectory() file:@"" types:fileTypes];
mas01mj@669 591 if(response == NSFileHandlingPanelOKButton)
mas01mj@669 592 {
mas01mj@669 593 NSArray* opts = [trackMap allKeysForObject:[panel filename]];
mas01mj@669 594 if([opts count] != 1)
mas01mj@669 595 {
mas01mj@699 596 // TODO : Needs fixing!
mas01mj@699 597
mas01mj@669 598 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
mas01mj@669 599 [alert addButtonWithTitle:@"OK"];
mas01mj@669 600 [alert setMessageText:@"Track not found"];
mas01mj@669 601 [alert setInformativeText:@"Make sure you have specified a valid track identifier."];
mas01mj@669 602 [alert setAlertStyle:NSWarningAlertStyle];
mas01mj@669 603 [alert beginSheetModalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
mas01mj@669 604 }
mas01mj@669 605 else
mas01mj@669 606 {
mas01mj@669 607 selectedKey = [opts objectAtIndex:0];
mas01mj@669 608 [queryKey setStringValue:selectedKey];
mas01mj@699 609 [queryPath setStringValue:selectedKey];
mas01mj@669 610 selectedFilename = [[panel filename] retain];
mas01mj@699 611 [queryButton setEnabled:YES];
mas01mj@699 612
mas01mj@699 613 [self resetLengths:self];
mas01mj@669 614 }
mas01mj@669 615 }
mas01mj@669 616 }
mas01mj@669 617
mas01mj@699 618 -(IBAction)resetLengths:(id)sender
mas01mj@699 619 {
mas01mj@699 620 queryTrack = [[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES];
mas01mj@699 621
mas01mj@702 622 int sampleRate = [self getSampleRate:selectedFilename];
mas01mj@702 623 int hopSize = [self getHopSizeInSamples:selectedFilename];
mas01mj@702 624 int winSize = [self nearestPow2:(hopSize*8)];
mas01mj@702 625
mas01mj@702 626 double samples = ([queryTrack duration]*sampleRate);
mas01mj@699 627
mas01mj@699 628 [queryLengthSeconds setDoubleValue:[queryTrack duration]];
mas01mj@699 629 [queryLengthVectors setDoubleValue:ceil((samples-winSize)/hopSize)];
mas01mj@699 630
mas01mj@699 631 // For now, go with 0
mas01mj@699 632 [queryStartSeconds setDoubleValue:0];
mas01mj@699 633 [queryStartVectors setDoubleValue:0];
mas01mj@699 634
mas01mj@699 635 [queryLengthSeconds setEnabled:YES];
mas01mj@699 636 [queryLengthVectors setEnabled:YES];
mas01mj@699 637 [queryStartSeconds setEnabled:YES];
mas01mj@699 638 [queryStartVectors setEnabled:YES];
mas01mj@699 639 [resetButton setEnabled:YES];
mas01mj@699 640 [multipleCheckBox setEnabled:YES];
mas01mj@702 641 [queryButton setEnabled:YES];
mas01mj@699 642
mas01mj@699 643 }
mas01mj@699 644
mas01mj@699 645 - (void)controlTextDidChange:(NSNotification *)nd
mas01mj@699 646 {
mas01mj@699 647 NSTextField *ed = [nd object];
mas01mj@699 648
mas01mj@702 649 int sampleRate = [self getSampleRate:selectedFilename];
mas01mj@702 650 int hopSize = [self getHopSizeInSamples:selectedFilename];
mas01mj@702 651 int winSize = [self nearestPow2:(hopSize*8)];
mas01mj@699 652
mas01mj@699 653 if(!queryTrack)
mas01mj@699 654 {
mas01mj@699 655 queryTrack = [[NSSound alloc] initWithContentsOfFile:selectedFilename byReference:YES];
mas01mj@699 656 }
mas01mj@699 657
mas01mj@699 658 double totalDuration = [queryTrack duration];
mas01mj@702 659 double samples = totalDuration * sampleRate;
mas01mj@699 660 double totalVectors = ceil((samples-winSize)/hopSize);
mas01mj@699 661
mas01mj@702 662
mas01mj@699 663 double lengthSecs = [queryLengthSeconds doubleValue];
mas01mj@699 664 double startSecs = [queryStartSeconds doubleValue];
mas01mj@699 665 double lengthVectors = [queryLengthVectors doubleValue];
mas01mj@699 666 double startVectors = [queryStartVectors doubleValue];
mas01mj@699 667
mas01mj@699 668 // Query Length
mas01mj@699 669 if (ed == queryLengthSeconds)
mas01mj@699 670 {
mas01mj@699 671 if(lengthSecs >= 0)
mas01mj@699 672 {
mas01mj@702 673 lengthVectors = ceil(((lengthSecs*sampleRate)-winSize)/hopSize);
mas01mj@699 674 if(lengthVectors < 0) {lengthVectors = 0; }
mas01mj@699 675 [queryLengthVectors setDoubleValue:lengthVectors];
mas01mj@699 676 }
mas01mj@699 677 }
mas01mj@699 678
mas01mj@699 679 if (ed == queryLengthVectors)
mas01mj@699 680 {
mas01mj@699 681 if(lengthVectors >= 0)
mas01mj@699 682 {
mas01mj@702 683 lengthSecs = ((hopSize*lengthVectors)+winSize)/sampleRate;
mas01mj@699 684 if(lengthSecs < 0) { lengthSecs = 0; }
mas01mj@699 685 [queryLengthSeconds setDoubleValue:lengthSecs];
mas01mj@699 686 }
mas01mj@699 687 }
mas01mj@699 688
mas01mj@699 689 // Query start
mas01mj@699 690 if (ed == queryStartSeconds)
mas01mj@699 691 {
mas01mj@699 692 if(startSecs >= 0)
mas01mj@699 693 {
mas01mj@702 694 startVectors = ceil(((startSecs*sampleRate)-winSize)/hopSize);
mas01mj@699 695 if(startVectors < 0) { startVectors = 0; }
mas01mj@699 696 [queryStartVectors setDoubleValue:startVectors];
mas01mj@699 697 }
mas01mj@699 698 }
mas01mj@699 699 if (ed == queryStartVectors)
mas01mj@699 700 {
mas01mj@699 701 if(startVectors >= 0)
mas01mj@699 702 {
mas01mj@702 703 startSecs = ((hopSize*startVectors)+winSize)/sampleRate;
mas01mj@699 704 if(startSecs < 0) { startSecs = 0; }
mas01mj@699 705 [queryStartSeconds setDoubleValue:startSecs];
mas01mj@699 706 }
mas01mj@699 707 }
mas01mj@699 708
mas01mj@699 709 if((lengthSecs + startSecs) > totalDuration || (lengthVectors + startVectors) > totalVectors || lengthVectors == 0)
mas01mj@699 710 {
mas01mj@699 711 [queryButton setEnabled:NO];
mas01mj@699 712 }
mas01mj@699 713 else if(![queryButton isEnabled])
mas01mj@699 714 {
mas01mj@699 715 [queryButton setEnabled:YES];
mas01mj@699 716 }
mas01mj@699 717 }
mas01mj@699 718
mas01mj@699 719 -(IBAction)cancelQuery:(id)sender
mas01mj@699 720 {
mas01mj@699 721 [NSApp endModalSession:session];
mas01mj@699 722 [querySheet orderOut:nil];
mas01mj@699 723 [NSApp endSheet:querySheet];
mas01mj@699 724 }
mas01mj@699 725
mas01mj@669 726 /**
mas01mj@669 727 * Actually perform the query. TODO: Monolithic.
mas01mj@669 728 */
mas01mj@699 729 -(IBAction)performQuery:(id)sender
mas01mj@669 730 {
mas01mj@699 731 [NSApp endModalSession:session];
mas01mj@699 732 [querySheet orderOut:nil];
mas01mj@699 733 [NSApp endSheet:querySheet];
mas01mj@699 734
mas01mj@669 735 NSLog(@"Perform query! %@, %@", selectedKey, selectedFilename);
mas01mj@669 736
mas01mj@669 737 adb_query_spec_t *spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
mas01mj@669 738 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
mas01mj@669 739
mas01mj@699 740 spec->qid.sequence_length = [queryLengthVectors doubleValue];
mas01mj@699 741 spec->qid.sequence_start = [queryStartVectors doubleValue];
mas01mj@699 742 spec->qid.flags = 0;
mas01mj@699 743 // spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
mas01mj@692 744
mas01mj@669 745 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
mas01mj@699 746
mas01mj@699 747 if([multipleCheckBox state] == NSOnState)
mas01mj@699 748 {
mas01mj@699 749 spec->params.npoints = 100;
mas01mj@699 750 }
mas01mj@699 751 else
mas01mj@699 752 {
mas01mj@699 753 spec->params.npoints = 1;
mas01mj@699 754 }
mas01mj@699 755
mas01mj@669 756 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
mas01mj@669 757
mas01mj@669 758 spec->params.ntracks = 100;
mas01mj@699 759 //spec->refine.radius = 5.0;
mas01mj@669 760 // spec->refine.absolute_threshold = -6;
mas01mj@669 761 // spec->refine.relative_threshold = 10;
mas01mj@669 762 // spec->refine.duration_ratio = 0;
mas01mj@669 763
mas01mj@669 764 spec->refine.flags = 0;
mas01mj@669 765 // spec->refine.flags |= ADB_REFINE_ABSOLUTE_THRESHOLD;
mas01mj@669 766 // spec->refine.flags |= ADB_REFINE_RELATIVE_THRESHOLD;
mas01mj@699 767 // spec->refine.flags |= ADB_REFINE_HOP_SIZE;
mas01mj@699 768 //spec->refine.flags |= ADB_REFINE_RADIUS;
mas01mj@669 769
mas01mj@669 770 adb_query_results_t *result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
mas01mj@669 771 spec->qid.datum->data = NULL;
mas01mj@669 772 spec->qid.datum->power = NULL;
mas01mj@669 773 spec->qid.datum->times = NULL;
mas01mj@669 774
mas01mj@669 775 [results removeAllObjects];
mas01mj@669 776
mas01mj@669 777 int ok = audiodb_retrieve_datum(db, [selectedKey cStringUsingEncoding:NSUTF8StringEncoding], spec->qid.datum);
mas01mj@669 778 if(ok == 0)
mas01mj@669 779 {
mas01mj@699 780
mas01mj@699 781 float hopSize = [[dbState objectForKey:@"hopsize"] floatValue];
mas01mj@669 782 NSLog(@"Got a datum");
mas01mj@669 783 result = audiodb_query_spec(db, spec);
mas01mj@669 784 if(result == NULL)
mas01mj@669 785 {
mas01mj@669 786
mas01mj@669 787 NSLog(@"No results");
mas01mj@669 788 }
mas01mj@669 789 else
mas01mj@669 790 {
mas01mj@702 791
mas01mj@699 792 NSLog(@"Populate table: %d", result->nresults);
mas01mj@669 793 for(int i=0; i<result->nresults; i++)
mas01mj@669 794 {
mas01mj@699 795
mas01mj@702 796 NSString* filename = [trackMap objectForKey:[NSString stringWithFormat:@"%s", result->results[i].ikey]];
mas01mj@702 797 int sampleRate = [self getSampleRate:filename];
mas01mj@702 798 int hopSize = [self getHopSizeInSamples:filename];
mas01mj@702 799 float divisor = (sampleRate/hopSize);
mas01mj@702 800
mas01mj@669 801 NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:4];
mas01mj@699 802 [dict setValue:[NSString stringWithFormat:@"%s", result->results[i].ikey] forKey:@"key"];
mas01mj@669 803 [dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"distance"];
mas01mj@669 804 [dict setValue:[NSNumber numberWithFloat:result->results[i].dist] forKey:@"meter"];
mas01mj@699 805 [dict setValue:[NSNumber numberWithFloat:result->results[i].ipos/divisor] forKey:@"ipos"];
mas01mj@699 806 NSLog(@"%s ipos: %d, dist: %f", result->results[i].ikey,result->results[i].ipos, result->results[i].dist);
mas01mj@669 807 [results addObject: dict];
mas01mj@669 808 }
mas01mj@669 809 }
mas01mj@669 810
mas01mj@669 811 NSSortDescriptor *distSort = [[NSSortDescriptor alloc]initWithKey:@"meter" ascending:YES];
mas01mj@669 812 NSArray *distDescs = [NSArray arrayWithObject:distSort];
mas01mj@669 813
mas01mj@669 814 [results sortUsingDescriptors:distDescs];
mas01mj@669 815 [tracksView setSortDescriptors:distDescs];
mas01mj@669 816 [tracksView reloadData];
mas01mj@669 817
mas01mj@669 818 }
mas01mj@669 819 else
mas01mj@669 820 {
mas01mj@669 821 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
mas01mj@669 822 [alert addButtonWithTitle:@"OK"];
mas01mj@669 823 [alert setMessageText:@"Track not found"];
mas01mj@669 824 [alert setInformativeText:@"Make sure you have specified a valid track identifier."];
mas01mj@669 825 [alert setAlertStyle:NSWarningAlertStyle];
mas01mj@669 826 [alert beginSheetModalForWindow:mainWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
mas01mj@669 827 }
mas01mj@669 828 // audiodb_query_free_results(db, spec, result);
mas01mj@669 829 }
mas01mj@669 830
mas01mj@669 831 @end