Updated config.guess file
[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 <cerrno>
24 #include <iostream>
25 #include <sstream>
26 #include <cctype>
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fts.h>
31
32 #include <lrdf.h>
33
34 #include <pbd/compose.h>
35
36 #include <ardour/ardour.h>
37 #include <ardour/configuration.h>
38 #include <ardour/audio_library.h>
39 #include <ardour/utils.h>
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46
47 static char* SOUNDFILE = "http://ardour.org/ontology/Soundfile";
48
49 AudioLibrary::AudioLibrary ()
50 {
51 //      sfdb_paths.push_back("/Users/taybin/sounds");
52
53         src = "file:" + get_user_ardour_path() + "sfdb";
54
55         // workaround for possible bug in raptor that crashes when saving to a
56         // non-existant file.
57         touch_file(get_user_ardour_path() + "sfdb");
58
59         lrdf_read_file(src.c_str());
60
61         lrdf_statement pattern;
62
63         pattern.subject = SOUNDFILE;
64         pattern.predicate = RDF_TYPE;
65         pattern.object = RDFS_CLASS;
66         pattern.object_type = lrdf_uri;
67
68         lrdf_statement* matches = lrdf_matches(&pattern);
69
70         // if empty DB, create basic schema
71         if (matches == 0) {
72                 initialize_db ();
73                 save_changes();
74         } 
75
76         lrdf_free_statements(matches);
77
78         XMLNode* state = instant_xml(X_("AudioLibrary"), get_user_ardour_path());
79         if (state) {
80                 set_state(*state);
81         }
82         scan_paths();
83 }
84
85 AudioLibrary::~AudioLibrary ()
86 {
87 }
88
89 void
90 AudioLibrary::initialize_db ()
91 {
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 void
111 AudioLibrary::add_member (string member)
112 {
113         string file_uri(string_compose("file:%1", member));
114
115         lrdf_add_triple(src.c_str(), file_uri.c_str(), RDF_TYPE, 
116                         SOUNDFILE, lrdf_uri);
117 }
118
119 void
120 AudioLibrary::remove_member (string uri)
121 {
122         lrdf_remove_uri_matches (uri.c_str());
123 }
124
125 void
126 AudioLibrary::search_members_and (vector<string>& members, 
127                                                                   const map<string,string>& fields)
128 {
129         lrdf_statement **head;
130         lrdf_statement* pattern = 0;
131         lrdf_statement* old = 0;
132         head = &pattern;
133
134         map<string,string>::const_iterator i;
135         for (i = fields.begin(); i != fields.end(); ++i){
136                 pattern = new lrdf_statement;
137                 pattern->subject = "?";
138                 pattern->predicate = strdup(field_uri(i->first).c_str());
139                 pattern->object = strdup((i->second).c_str());
140                 pattern->next = old;
141
142                 old = pattern;
143         }
144
145         if (*head != 0) {
146                 lrdf_uris* ulist = lrdf_match_multi(*head);
147                 for (uint32_t j = 0; ulist && j < ulist->count; ++j) {
148 //                      printf("AND: %s\n", ulist->items[j]);
149                         members.push_back(ulist->items[j]);
150                 }
151                 lrdf_free_uris(ulist);
152
153                 compact_vector(members);
154         }
155
156         // memory clean up
157         pattern = *head;
158         while(pattern){
159                 free(pattern->predicate);
160                 free(pattern->object);
161                 old = pattern;
162                 pattern = pattern->next;
163                 delete old;
164         }
165 }
166
167 void
168 AudioLibrary::search_members_or (vector<string>& members, 
169                                                                  const map<string,string>& fields)
170 {
171         map<string,string>::const_iterator i;
172
173         lrdf_statement pattern;
174         for (i = fields.begin(); i != fields.end(); ++i) {
175                 pattern.subject = 0;
176                 pattern.predicate = strdup(field_uri(i->first).c_str());
177                 pattern.object = strdup((i->second).c_str());
178                 pattern.object_type = lrdf_literal;
179
180                 lrdf_statement* matched = lrdf_matches(&pattern);
181
182                 lrdf_statement* old = matched;
183                 while(matched) {
184 //                      printf ("OR: %s\n", matched->subject);
185                         members.push_back(matched->subject);
186                         matched = matched->next;
187                 }
188
189                 free(pattern.predicate);
190                 free(pattern.object);
191                 lrdf_free_statements (old);
192         }
193
194         compact_vector(members);
195 }
196
197 void
198 AudioLibrary::add_field (string name)
199 {
200         string local_field = field_uri(name);
201         lrdf_statement pattern;
202         pattern.subject = strdup(local_field.c_str());
203         pattern.predicate = RDF_TYPE;
204         pattern.object = RDF_BASE "Property";
205         pattern.object_type = lrdf_uri;
206
207         if(lrdf_exists_match(&pattern)) {
208                 return;
209         }
210
211         // of type rdf:Property
212         lrdf_add_triple(src.c_str(), local_field.c_str(), RDF_TYPE, 
213                         RDF_BASE "Property", lrdf_uri);
214         // of range ardour:Soundfile
215         lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "range",
216                         SOUNDFILE, lrdf_uri);
217         // of domain rdf:Literal
218         lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "domain", 
219                                         RDF_BASE "Literal", lrdf_uri);
220
221         set_label (local_field, name);
222         
223         fields_changed(); /* EMIT SIGNAL */
224 }
225
226 void
227 AudioLibrary::get_fields (vector<string>& fields)
228 {
229         lrdf_statement pattern;
230
231         pattern.subject = 0;
232         pattern.predicate = RDFS_BASE "range";
233         pattern.object = SOUNDFILE;
234         pattern.object_type = lrdf_uri;
235
236         lrdf_statement* matches = lrdf_matches(&pattern);
237
238         lrdf_statement* current = matches;
239         while (current != 0) {
240                 fields.push_back(get_label(current->subject));
241
242                 current = current->next;
243         }
244
245         lrdf_free_statements(matches);
246
247         compact_vector(fields);
248 }
249
250 void
251 AudioLibrary::remove_field (string name)
252 {
253         lrdf_remove_uri_matches(field_uri(name).c_str());
254         fields_changed (); /* EMIT SIGNAL */
255 }
256
257 string 
258 AudioLibrary::get_field (string uri, string field)
259 {
260         lrdf_statement pattern;
261
262         pattern.subject = strdup(uri.c_str());
263
264         pattern.predicate = strdup(field_uri(field).c_str());
265
266         pattern.object = 0;
267         pattern.object_type = lrdf_literal;
268
269         lrdf_statement* matches = lrdf_matches(&pattern);
270         free(pattern.subject);
271         free(pattern.predicate);
272
273         stringstream object;
274         if (matches != 0){
275                 object << matches->object;
276         }
277
278         lrdf_free_statements(matches);
279         return object.str();
280 }
281
282 void 
283 AudioLibrary::set_field (string uri, string field, string literal)
284 {
285         lrdf_statement pattern;
286
287         pattern.subject = strdup(uri.c_str());
288
289         string local_field = field_uri(field);
290         pattern.predicate = strdup(local_field.c_str());
291
292         pattern.object = 0;
293         pattern.object_type = lrdf_literal;
294
295         lrdf_remove_matches(&pattern);
296         free(pattern.subject);
297         free(pattern.predicate);
298
299         lrdf_add_triple(src.c_str(), uri.c_str(), local_field.c_str(), 
300                         literal.c_str(), lrdf_literal);
301
302          fields_changed(); /* EMIT SIGNAL */
303 }
304
305 string
306 AudioLibrary::field_uri (string name)
307 {
308         stringstream local_field;
309         local_field << "file:sfdb/fields/" << name;
310
311         return local_field.str();
312 }
313
314 string
315 AudioLibrary::get_label (string uri)
316 {
317         lrdf_statement pattern;
318         pattern.subject = strdup(uri.c_str());
319         pattern.predicate = RDFS_BASE "label";
320         pattern.object = 0;
321         pattern.object_type = lrdf_literal;
322
323         lrdf_statement* matches = lrdf_matches (&pattern);
324         free(pattern.subject);
325
326         stringstream label;
327         if (matches != 0){
328                 label << matches->object;
329         }
330
331         lrdf_free_statements(matches);
332
333         return label.str();
334 }
335
336 void
337 AudioLibrary::set_label (string uri, string label)
338 {
339         lrdf_statement pattern;
340         pattern.subject = strdup(uri.c_str());
341         pattern.predicate = RDFS_BASE "label";
342         pattern.object = 0;
343         pattern.object_type = lrdf_literal;
344
345         lrdf_remove_matches(&pattern);
346         free(pattern.subject);
347
348         lrdf_add_triple(src.c_str(), uri.c_str(), RDFS_BASE "label", 
349                         label.c_str(), lrdf_literal);
350 }
351
352 void
353 AudioLibrary::compact_vector(vector<string>& vec)
354 {
355     sort(vec.begin(), vec.end());
356     unique(vec.begin(), vec.end());
357 }
358
359 void 
360 AudioLibrary::set_paths (vector<string> paths)
361 {
362         sfdb_paths = paths;
363         
364         add_instant_xml(get_state(), get_user_ardour_path());
365 }
366
367 vector<string> 
368 AudioLibrary::get_paths ()
369 {
370         return sfdb_paths;
371 }
372
373 void
374 AudioLibrary::scan_paths ()
375 {
376         if (sfdb_paths.size() < 1) {
377                 return;
378         }
379
380         vector<char *> pathv(sfdb_paths.size());
381         unsigned int i;
382         for (i = 0; i < sfdb_paths.size(); ++i) {
383                 pathv[i] = new char[sfdb_paths[i].length() +1];
384                 sfdb_paths[i].copy(pathv[i], string::npos);
385                 pathv[i][sfdb_paths[i].length()] = 0;
386         }
387         pathv[i] = 0;
388
389         FTS* ft = fts_open(&pathv[0], FTS_LOGICAL|FTS_NOSTAT|FTS_PHYSICAL|FTS_XDEV, 0);
390         if (errno) {
391                 error << strerror(errno) << endmsg;
392                 return;
393         }
394
395         lrdf_statement s;
396         s.predicate = RDF_TYPE;
397         s.object = SOUNDFILE;
398         s.object_type = lrdf_uri;
399         string filename;
400         while (FTSENT* file = fts_read(ft)) {
401                 if ((file->fts_info & FTS_F) && (safe_file_extension(file->fts_name))) {
402                         filename = "file:";
403                         filename.append(file->fts_accpath);
404                         s.subject = strdup(filename.c_str());
405                         if (lrdf_exists_match(&s)) {
406                                 continue;
407                         } else {
408                                 add_member(file->fts_accpath);
409                                 cout << file->fts_accpath << endl;
410                         }
411                         free(s.subject);
412                 }
413         }
414         fts_close(ft);
415
416         for (i = 0; i < pathv.size(); ++i) {
417                 delete[] pathv[i];
418         }
419
420         save_changes();
421 }
422
423 bool
424 AudioLibrary::safe_file_extension(string file)
425 {
426         return !(file.rfind(".wav") == string::npos &&
427         file.rfind(".aiff")== string::npos &&
428         file.rfind(".aif") == string::npos &&
429         file.rfind(".snd") == string::npos &&
430         file.rfind(".au")  == string::npos &&
431         file.rfind(".raw") == string::npos &&
432         file.rfind(".sf")  == string::npos &&
433         file.rfind(".cdr") == string::npos &&
434         file.rfind(".smp") == string::npos &&
435         file.rfind(".maud")== string::npos &&
436         file.rfind(".vwe") == string::npos &&
437         file.rfind(".paf") == string::npos &&
438 #ifdef HAVE_COREAUDIO
439                 file.rfind(".mp3") == string::npos &&
440                 file.rfind(".aac") == string::npos &&
441                 file.rfind(".mp4") == string::npos &&
442 #endif // HAVE_COREAUDIO
443         file.rfind(".voc") == string::npos);
444 }
445
446 XMLNode&
447 AudioLibrary::get_state ()
448 {
449         XMLNode* root = new XMLNode(X_("AudioLibrary"));
450         
451         for (vector<string>::iterator i = sfdb_paths.begin(); i != sfdb_paths.end(); ++i) {
452                 XMLNode* node = new XMLNode(X_("Path"));
453                 node->add_property("value", *i);
454                 root->add_child_nocopy(*node);
455         }
456         
457         return *root;
458 }
459
460 int
461 AudioLibrary::set_state (const XMLNode& node)
462 {
463         if (node.name() != X_("AudioLibrary")) {
464                 fatal << "programming error: AudioLibrary: incorrect XML node sent to set_state()" << endmsg;
465                 return -1;
466         }
467         
468         XMLNodeList nodes = node.children(X_("Path"));
469         
470         vector<string> paths;
471         XMLProperty* prop;
472         XMLNode* child;
473         for (XMLNodeConstIterator iter = nodes.begin(); iter != nodes.end(); ++iter) {
474                 child = *iter;
475                 
476                 if ((prop = child->property(X_("value"))) != 0) {
477                         paths.push_back(prop->value());
478                 }
479         }
480         
481         sfdb_paths = paths;
482         
483         return 0;
484 }