fix compose mess, and a number of 64 bit printf specs
[ardour.git] / libs / ardour / audio_library.cc
1 /*
2     Copyright (C) 2003 Paul Davis 
3     Author: Taybin Rutkin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19     $Id$
20 */
21
22 #include <cstdio> // Needed so that libraptor (included in lrdf) won't complain
23 #include <iostream>
24 #include <sstream>
25 #include <cctype>
26
27 #include <lrdf.h>
28
29 #include <pbd/compose.h>
30
31 #include <ardour/ardour.h>
32 #include <ardour/configuration.h>
33 #include <ardour/audio_library.h>
34 #include <ardour/utils.h>
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40
41 namespace std {
42 struct UriSorter {
43         bool operator() (string a, string b) const { 
44                 return cmp_nocase(Library->get_label(a), Library->get_label(b)) == -1; 
45         }
46 }; 
47 };
48
49 static char* GROUP = "http://ardour.org/ontology/Group";
50 static char* SOUNDFILE = "http://ardour.org/ontology/Soundfile";
51 static char* hasFile = "http://ardour.org/ontology/hasFile";
52 static char* memberOf = "http://ardour.org/ontology/memberOf";
53 static char* subGroupOf = "http://ardour.org/ontology/subGroupOf";
54
55 AudioLibrary::AudioLibrary ()
56 {
57         src = "file:" + Config->get_user_ardour_path() + "sfdb";
58
59         // workaround for possible bug in raptor that crashes when saving to a
60         // non-existant file.
61         touch_file(Config->get_user_ardour_path() + "sfdb");
62
63         lrdf_read_file(src.c_str());
64
65         lrdf_statement pattern;
66
67         pattern.subject = GROUP;
68         pattern.predicate = RDF_TYPE;
69         pattern.object = RDFS_CLASS;
70         pattern.object_type = lrdf_uri;
71
72         lrdf_statement* matches = lrdf_matches(&pattern);
73
74         // if empty DB, create basic schema
75         if (matches == 0) {
76                 initialize_db ();
77                 save_changes();
78         } 
79
80         lrdf_free_statements(matches);
81 }
82
83 AudioLibrary::~AudioLibrary ()
84 {
85 }
86
87 void
88 AudioLibrary::initialize_db ()
89 {
90         // define ardour:Group
91         lrdf_add_triple(src.c_str(), GROUP, RDF_TYPE, RDFS_CLASS, lrdf_uri);
92         // define ardour:Soundfile
93         lrdf_add_triple(src.c_str(), SOUNDFILE, RDF_TYPE, RDFS_CLASS, lrdf_uri);
94
95         // add intergral fields
96         add_field("channels");
97         add_field("samplerate");
98         add_field("resolution");
99         add_field("format");
100 }
101
102 void
103 AudioLibrary::save_changes ()
104 {
105         if (lrdf_export_by_source(src.c_str(), src.substr(5).c_str())) {
106                 warning << string_compose(_("Could not open %1.  Audio Library not saved"), src) << endmsg;
107         }
108 }
109
110 string
111 AudioLibrary::add_group (string group, string parent_uri)
112 {
113         string local_group(string_compose("file:sfbd/group/%1", get_uid()));
114
115         lrdf_add_triple(src.c_str(), local_group.c_str(), 
116                                         RDFS_BASE "label", group.c_str(), lrdf_literal);
117
118         if (parent_uri == ""){
119                 lrdf_add_triple(src.c_str(), local_group.c_str(), 
120                                                 subGroupOf, GROUP, lrdf_uri);
121         } else {
122                 lrdf_add_triple(src.c_str(), local_group.c_str(), 
123                                                 subGroupOf, parent_uri.c_str(), lrdf_uri);
124         }
125
126          added_group(local_group, parent_uri); /* EMIT SIGNAL */
127
128         return local_group;
129 }
130
131 void
132 AudioLibrary::remove_group (string uri)
133 {
134         list<string> items;
135         list<string>::iterator i;
136
137         get_members(items, uri);
138         for (i = items.begin(); i != items.end(); ++i) {
139                 remove_member(*i);
140         }
141         
142         items.clear();
143         
144         get_groups(items, uri);
145         for (i = items.begin(); i != items.end(); ++i) {
146                 remove_group(*i);
147         }
148
149         lrdf_remove_uri_matches(uri.c_str());
150         save_changes ();
151
152          removed_group(uri); /* EMIT SIGNAL */
153 }
154
155 void
156 AudioLibrary::get_groups (list<string>& groups, string parent_uri)
157 {
158         lrdf_statement pattern;
159
160         pattern.subject = 0;
161         pattern.predicate = subGroupOf;
162         if (parent_uri == ""){
163                 pattern.object = strdup(GROUP);
164         } else {
165                 pattern.object = strdup(parent_uri.c_str());
166         }
167
168         lrdf_statement* matches = lrdf_matches(&pattern);
169
170         lrdf_statement* current = matches;
171         while (current != 0) {
172                 groups.push_back(current->subject);
173                 current = current->next;
174         }
175
176         lrdf_free_statements(matches);
177         free (pattern.object);
178
179         UriSorter cmp;
180         groups.sort(cmp);
181         groups.unique();
182 }
183
184 string
185 AudioLibrary::add_member (string member, string parent_uri)
186 {
187         string local_member(string_compose("file:sfdb/soundfile/%1", get_uid()));
188         string file_uri(string_compose("file:%1", member));
189
190         lrdf_add_triple(src.c_str(), local_member.c_str(), RDF_TYPE, 
191                         SOUNDFILE, lrdf_uri);
192         lrdf_add_triple(src.c_str(), local_member.c_str(), hasFile,
193                                         file_uri.c_str(), lrdf_uri);
194
195         string::size_type size = member.find_last_of('/');
196         string label = member.substr(++size);
197
198         lrdf_add_triple(src.c_str(), local_member.c_str(), RDFS_BASE "label", 
199                         label.c_str(), lrdf_literal);
200
201         if (parent_uri == ""){
202                 lrdf_add_triple(src.c_str(), local_member.c_str(), memberOf,
203                                 GROUP, lrdf_uri);
204         } else {
205                 lrdf_add_triple(src.c_str(), local_member.c_str(), memberOf, 
206                         parent_uri.c_str(), lrdf_uri);
207         }
208
209         save_changes ();
210
211          added_member (local_member, parent_uri); /* EMIT SIGNAL */
212
213         return local_member;
214 }
215
216 void
217 AudioLibrary::remove_member (string uri)
218 {
219         lrdf_remove_uri_matches (uri.c_str());
220
221         save_changes ();
222
223          removed_member(uri); /* EMIT SIGNAL */
224 }
225
226 void
227 AudioLibrary::get_members (list<string>& members, string parent_uri)
228 {
229         lrdf_statement pattern;
230
231         pattern.subject = 0;
232         pattern.predicate = memberOf;
233         if (parent_uri == ""){
234                 pattern.object = strdup(GROUP);
235         } else {
236                 pattern.object = strdup(parent_uri.c_str());
237         }
238
239         lrdf_statement* matches = lrdf_matches(&pattern);
240
241         lrdf_statement* current = matches;
242         while (current != 0) {
243                 members.push_back(current->subject);
244                 current = current->next;
245         }
246
247         lrdf_free_statements(matches);
248         free (pattern.object);
249
250         UriSorter cmp;
251         members.sort(cmp);
252         members.unique();
253 }
254
255 void
256 AudioLibrary::search_members_and (list<string>& members, 
257                                                                   const map<string,string>& fields)
258 {
259         lrdf_statement **head;
260         lrdf_statement* pattern = 0;
261         lrdf_statement* old = 0;
262         head = &pattern;
263
264         map<string,string>::const_iterator i;
265         for (i = fields.begin(); i != fields.end(); ++i){
266                 pattern = new lrdf_statement;
267                 pattern->subject = "?";
268                 pattern->predicate = strdup(field_uri(i->first).c_str());
269                 pattern->object = strdup((i->second).c_str());
270                 pattern->next = old;
271
272                 old = pattern;
273         }
274
275         if (*head != 0) {
276                 lrdf_uris* ulist = lrdf_match_multi(*head);
277                 for (uint32_t j = 0; ulist && j < ulist->count; ++j) {
278 //                      printf("AND: %s\n", ulist->items[j]);
279                         members.push_back(ulist->items[j]);
280                 }
281                 lrdf_free_uris(ulist);
282
283                 UriSorter cmp;
284                 members.sort(cmp);
285                 members.unique();
286         }
287
288         // memory clean up
289         pattern = *head;
290         while(pattern){
291                 free(pattern->predicate);
292                 free(pattern->object);
293                 old = pattern;
294                 pattern = pattern->next;
295                 delete old;
296         }
297 }
298
299 void
300 AudioLibrary::search_members_or (list<string>& members, 
301                                                                  const map<string,string>& fields)
302 {
303         map<string,string>::const_iterator i;
304
305         lrdf_statement pattern;
306         for (i = fields.begin(); i != fields.end(); ++i) {
307                 pattern.subject = 0;
308                 pattern.predicate = strdup(field_uri(i->first).c_str());
309                 pattern.object = strdup((i->second).c_str());
310                 pattern.object_type = lrdf_literal;
311
312                 lrdf_statement* matched = lrdf_matches(&pattern);
313
314                 lrdf_statement* old = matched;
315                 while(matched) {
316 //                      printf ("OR: %s\n", matched->subject);
317                         members.push_back(matched->subject);
318                         matched = matched->next;
319                 }
320
321                 free(pattern.predicate);
322                 free(pattern.object);
323                 lrdf_free_statements (old);
324         }
325
326         UriSorter cmp;
327         members.sort(cmp);
328         members.unique();
329 }
330
331 string
332 AudioLibrary::get_member_filename (string uri)
333 {
334         lrdf_statement pattern;
335         pattern.subject = strdup(uri.c_str());
336         pattern.predicate = hasFile;
337         pattern.object = 0;
338         pattern.object_type = lrdf_uri;
339         
340         lrdf_statement* matches = lrdf_matches(&pattern);
341         if (matches) {
342                 string file = matches->object;
343                 lrdf_free_statements(matches);
344
345                 string::size_type pos = file.find(":");
346                 return file.substr(++pos);
347         } else {
348                 warning << _("Could not find member filename") << endmsg;
349                 return "-Unknown-";
350         }
351 }
352
353 void
354 AudioLibrary::add_field (string name)
355 {
356         string local_field = field_uri(name);
357         lrdf_statement pattern;
358         pattern.subject = strdup(local_field.c_str());
359         pattern.predicate = RDF_TYPE;
360         pattern.object = RDF_BASE "Property";
361         pattern.object_type = lrdf_uri;
362
363         if(lrdf_exists_match(&pattern)) {
364                 return;
365         }
366
367         // of type rdf:Property
368         lrdf_add_triple(src.c_str(), local_field.c_str(), RDF_TYPE, 
369                         RDF_BASE "Property", lrdf_uri);
370         // of range ardour:Soundfile
371         lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "range",
372                         SOUNDFILE, lrdf_uri);
373         // of domain rdf:Literal
374         lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "domain", 
375                                         RDF_BASE "Literal", lrdf_uri);
376
377         set_label (local_field, name);
378         
379         save_changes();
380
381          fields_changed(); /* EMIT SIGNAL */
382 }
383
384 void
385 AudioLibrary::get_fields (list<string>& fields)
386 {
387         lrdf_statement pattern;
388
389         pattern.subject = 0;
390         pattern.predicate = RDFS_BASE "range";
391         pattern.object = SOUNDFILE;
392         pattern.object_type = lrdf_uri;
393
394         lrdf_statement* matches = lrdf_matches(&pattern);
395
396         lrdf_statement* current = matches;
397         while (current != 0) {
398                 fields.push_back(get_label(current->subject));
399
400                 current = current->next;
401         }
402
403         lrdf_free_statements(matches);
404
405         fields.sort();
406         fields.unique();
407 }
408
409 void
410 AudioLibrary::remove_field (string name)
411 {
412         lrdf_remove_uri_matches(field_uri(name).c_str());
413         save_changes();
414          fields_changed (); /* EMIT SIGNAL */
415 }
416
417 string 
418 AudioLibrary::get_field (string uri, string field)
419 {
420         lrdf_statement pattern;
421
422         pattern.subject = strdup(uri.c_str());
423
424         pattern.predicate = strdup(field_uri(field).c_str());
425
426         pattern.object = 0;
427         pattern.object_type = lrdf_literal;
428
429         lrdf_statement* matches = lrdf_matches(&pattern);
430         free(pattern.subject);
431         free(pattern.predicate);
432
433         stringstream object;
434         if (matches != 0){
435                 object << matches->object;
436         }
437
438         lrdf_free_statements(matches);
439         return object.str();
440 }
441
442 void 
443 AudioLibrary::set_field (string uri, string field, string literal)
444 {
445         lrdf_statement pattern;
446
447         pattern.subject = strdup(uri.c_str());
448
449         string local_field = field_uri(field);
450         pattern.predicate = strdup(local_field.c_str());
451
452         pattern.object = 0;
453         pattern.object_type = lrdf_literal;
454
455         lrdf_remove_matches(&pattern);
456         free(pattern.subject);
457         free(pattern.predicate);
458
459         lrdf_add_triple(src.c_str(), uri.c_str(), local_field.c_str(), 
460                         literal.c_str(), lrdf_literal);
461
462         save_changes();
463
464          fields_changed(); /* EMIT SIGNAL */
465 }
466
467 string
468 AudioLibrary::field_uri (string name)
469 {
470         stringstream local_field;
471         local_field << "file:sfdb/fields/" << name;
472
473         return local_field.str();
474 }
475
476 string
477 AudioLibrary::get_label (string uri)
478 {
479         lrdf_statement pattern;
480         pattern.subject = strdup(uri.c_str());
481         pattern.predicate = RDFS_BASE "label";
482         pattern.object = 0;
483         pattern.object_type = lrdf_literal;
484
485         lrdf_statement* matches = lrdf_matches (&pattern);
486         free(pattern.subject);
487
488         stringstream label;
489         if (matches != 0){
490                 label << matches->object;
491         }
492
493         lrdf_free_statements(matches);
494
495         return label.str();
496 }
497
498 void
499 AudioLibrary::set_label (string uri, string label)
500 {
501         lrdf_statement pattern;
502         pattern.subject = strdup(uri.c_str());
503         pattern.predicate = RDFS_BASE "label";
504         pattern.object = 0;
505         pattern.object_type = lrdf_literal;
506
507         lrdf_remove_matches(&pattern);
508         free(pattern.subject);
509
510         lrdf_add_triple(src.c_str(), uri.c_str(), RDFS_BASE "label", 
511                         label.c_str(), lrdf_literal);
512 }
513