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