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