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