To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / utilities / the-application / the-application.cpp @ 45:0033259c6772

History | View | Annotate | Download (23.6 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2

    
3
#include "Objects.h"
4
#include "TypeRegistrar.h"
5
#include "FeatureFileIndex.h"
6

    
7
#include <dataquay/BasicStore.h>
8
#include <dataquay/TransactionalStore.h>
9
#include <dataquay/RDFException.h>
10
#include <dataquay/objectmapper/ObjectLoader.h>
11
#include <dataquay/objectmapper/ObjectStorer.h>
12
#include <dataquay/objectmapper/ObjectMapper.h>
13
#include <dataquay/objectmapper/TypeMapping.h>
14
#include <dataquay/Debug.h>
15

    
16
#include "data/fileio/AudioFileReaderFactory.h"
17
#include "data/fileio/AudioFileReader.h"
18
#include "base/TempDirectory.h"
19

    
20
#include "Matcher.h"
21

    
22
#include <vamp-hostsdk/PluginLoader.h>
23

    
24
#include <QMultiMap>
25
#include <QFileInfo>
26
#include <QDir>
27
#include <QCoreApplication>
28

    
29
#include <iostream>
30

    
31
using namespace Dataquay;
32
using namespace ClassicalData;
33
using namespace std;
34
using namespace Vamp;
35
using namespace Vamp::HostExt;
36

    
37
ostream &operator<<(ostream &target, const QString &str)
38
{
39
    return target << str.toLocal8Bit().data();
40
}
41

    
42
ostream &operator<<(ostream &target, const QUrl &u)
43
{
44
    return target << "<" << u.toString() << ">";
45
}
46

    
47

    
48
bool
49
load(BasicStore *store, QString fileName)
50
{
51
    QUrl url = QUrl::fromLocalFile(fileName);
52

    
53
    cerr << "Importing from URL " << url << " ...";
54
    try {
55
        store->import(url, BasicStore::ImportPermitDuplicates);
56
    } catch (RDFException e) {
57
        cerr << " retrying with explicit ntriples type...";
58
        try {
59
            store->import(url, BasicStore::ImportPermitDuplicates, "ntriples");
60
        } catch (RDFException e) {
61
            cerr << "failed" << endl;
62
            cerr << "Import failed: " << e.what() << endl;
63
            return false;
64
        }
65
    }
66

    
67
    cerr << " done" << endl;
68
    return true;
69
}
70

    
71
void
72
usage(char *name)
73
{
74
    int s = 0;
75
    for (int i = 0; name[i]; ++i) if (name[i] == '/') s = i + 1;
76
    name = name + s;
77
    cerr << "Usage:" << endl;
78
    cerr << "  " << name << " <input-rdf-file> guess <track.wav> [<track.wav> ...]" << endl;
79
    exit(-1);
80
}
81

    
82
static QList<Composer *> allComposers;
83
static QHash<QString, Composer *> composerAliases;
84
static QHash<Uri, Composer *> composerUris;
85
static QMap<Composer *, QList<Work *> > worksMap;
86
static QList<Work *> allWorks;
87

    
88
void
89
show(Composer *c)
90
{
91
    cout << c->property("uri").value<Uri>() << endl;
92
    cout << c->getSortName(true);
93
    QString d = c->getDisplayDates();
94
    if (d != "") cout << " (" << d << ")";
95
    if (!c->nationality().empty() || c->period() != "") {
96
        cout << " [";
97
        bool first = true;
98
        foreach (QString n, c->nationality()) {
99
            if (!first) cout << "/";
100
            cout << n;
101
            first = false;
102
        }
103
        if (c->period() != "") {
104
            if (!first) cout << ", ";
105
            cout << c->period();
106
        }
107
        cout << "]";
108
    }
109
    if (c->gender() != "") {
110
        cout << " *" << c->gender();
111
    }
112
    if (!worksMap[c].empty()) {
113
        cout << " [" << worksMap[c].size() << " work(s)]";
114
    }
115
    cout << endl;
116
    foreach (QString a, c->aliases()) {
117
        cout << " - " << a << endl;
118
    }
119
    if (c->remarks() != "") {
120
        cout << " " << c->remarks() << endl;
121
    }
122
    foreach (Document *d, c->pages()) {
123
        cout << d->siteName() << " -> " << d->uri() << endl;
124
    }
125
    foreach (Uri u, c->otherURIs()) {
126
        cout << "Same as " << u << endl;
127
    }
128
}
129

    
130
void
131
showBrief(Composer *c)
132
{
133
    cout << c->property("uri").value<Uri>() << endl;
134
    cout << c->getSortName(false);
135
    QString d = c->getDisplayDates();
136
    if (d != "") cout << " (" << d << ")";
137
    if (!c->nationality().empty() || c->period() != "") {
138
        cout << " [";
139
        bool first = true;
140
        foreach (QString n, c->nationality()) {
141
            if (!first) cout << "/";
142
            cout << n;
143
            first = false;
144
        }
145
        if (c->period() != "") {
146
            if (!first) cout << " ";
147
            cout << c->period();
148
        }
149
        cout << "]";
150
    }
151
    if (c->gender() != "") {
152
        cout << " *" << c->gender();
153
    }
154
    if (!worksMap[c].empty()) {
155
        cout << " [" << worksMap[c].size() << " work(s)]";
156
    }
157
    cout << endl;
158
}
159

    
160
void
161
listBrief(QList<Composer *> composers)
162
{
163
    QMultiMap<QString, Composer *> sorted;
164
    foreach (Composer *c, composers) {
165
        sorted.insert(c->getSortName(false), c);
166
    }
167
    foreach (Composer *c, sorted) {
168
        showBrief(c);
169
    }
170
}
171

    
172
void
173
listUris(QList<Composer *> composers)
174
{
175
    QMultiMap<Uri, Composer *> sorted;
176
    foreach (Composer *c, composers) {
177
        sorted.insert(c->property("uri").value<Uri>(), c);
178
    }
179
    foreach (Uri uri, sorted.keys()) {
180
        cout << uri << endl;
181
    }
182
}
183

    
184
void
185
showSearchResults(QMultiMap<float, Composer *> matches, int count)
186
{
187
    int n = 0;
188
    for (QMultiMap<float, Composer *>::const_iterator i = matches.end();
189
         i != matches.begin(); ) {
190
        --i;
191
        if (i.key() <= 0) continue;
192
        cout << endl;
193
        if (n == 0) {
194
            cout << "Best match:" << endl;
195
        } else if (n == 1) {
196
            cout << "Other candidate(s):" << endl;
197
        }
198
        cout << "[" << i.key() << "] ";
199
        if (n == 0) show(i.value());
200
        else showBrief(i.value());
201
        if (++n > count) break;
202
    }
203
    if (n == 0) cout << "No matches" << endl;
204
    cout << endl;
205
}
206

    
207
void
208
getTrackData(FileSource source, QString &fingerprint, QString &puid,
209
             QString &title, QString &maker, AudioFileReader::TagMap &tags)
210
{
211
    AudioFileReader *reader = AudioFileReaderFactory::createReader(source);
212
//    AudioFileReader *reader = AudioFileReaderFactory::createThreadingReader(source);
213
    if (!reader || !reader->isOK()) {
214
        cerr << "Failed to open audio file" << endl;
215
        return;
216
    }
217

    
218
    title = reader->getTitle();
219
    maker = reader->getMaker();
220
//    cout << "File tag title: " << reader->getTitle() << endl;
221
//    cout << "File tag maker: " << reader->getMaker() << endl;
222

    
223
    cout << "All tags:" << endl;
224
    tags = reader->getTags();
225
    for (AudioFileReader::TagMap::const_iterator i = tags.begin();
226
         i != tags.end(); ++i) {
227
        cout << i->first << " " << i->second << endl;
228
    }
229

    
230
    PluginLoader *pl = PluginLoader::getInstance();
231
    Plugin *plugin = pl->loadPlugin
232
        ("ofa-vamp-plugin:ofa_puid_and_fingerprint", reader->getSampleRate(), PluginLoader::ADAPT_ALL);
233
    if (!plugin) {
234
        cerr << "Failed to load OFA Vamp plugin" << endl;
235
        return;
236
    }
237

    
238
    // 135 seconds... well, ok, let's have 136
239
    int secs = 136;
240
    
241
    int want = int(secs * reader->getSampleRate());
242
    int ch = reader->getChannelCount();
243
    std::vector<SampleBlock> samples;
244
    reader->getDeInterleavedFrames(0, want, samples);
245
    int have = samples[0].size();
246
    if (!plugin->initialise(ch, have, have)) {
247
        cerr << "Failed to initialise(" << ch << "," << have << "," << have << ") plugin" << endl;
248
        return;
249
    }
250
    
251
    float **input = new float *[ch];
252
    for (int i = 0; i < ch; ++i) {
253
        input[i] = &samples[i][0];
254
    }
255
    Plugin::FeatureSet features = plugin->process(input, RealTime::zeroTime);
256
    if (!features[0].empty()) {
257
        fingerprint = QString::fromStdString(features[0][0].label);
258
    }
259
    if (!features[1].empty()) {
260
        puid = QString::fromStdString(features[1][0].label);
261
    }
262
    features = plugin->getRemainingFeatures();
263
    if (fingerprint == "" && !features[0].empty()) {
264
        fingerprint = QString::fromStdString(features[0][0].label);
265
    }
266
    if (puid == "" && !features[1].empty()) {
267
        puid = QString::fromStdString(features[1][0].label);
268
    }
269
    delete[] input;
270
    delete plugin;
271
    delete reader;
272
}
273

    
274
float
275
bonusFactor(NamedEntity *e)
276
{
277
    // tiny nudge to prefer composers we actually have works for
278
    Composer *c = qobject_cast<Composer *>(e);
279
    float f = 1.f;
280
    int sz = 0;
281
    if (c && worksMap.contains(c)) {
282
        sz = worksMap[c].size();
283
        while (sz > 0) {
284
            f += 0.01;
285
            sz = sz / 10;
286
        }
287
    }
288
    return f;
289
}
290

    
291
void
292
integrateGuesses(GuessSet &guesses, GuessSet newGuesses)
293
{
294
    QHash<NamedEntity *, float> ecmap;
295
    foreach (Guess g, guesses) {
296
        ecmap[g.entity()] += g.confidence() * bonusFactor(g.entity());
297
    }
298
    foreach (Guess g, newGuesses) {
299
        if (ecmap.contains(g.entity())) {
300
            ecmap[g.entity()] += g.confidence() / 2;
301
        } else {
302
            ecmap[g.entity()] = g.confidence();
303
        }
304
    }
305
    guesses.clear();
306
    foreach (NamedEntity *e, ecmap.keys()) {
307
        guesses.insert(Guess(ecmap[e], e));
308
    }
309
}
310

    
311
void
312
guessFromMaker(QString maker, float scale, GuessSet &guesses)
313
{
314
    if (maker == "") return;
315
//    cerr << "guessFromMaker: " << maker << endl;
316
    GuessSet myGuesses;
317
    if (composerAliases.contains(maker)) {
318
        QList<Composer *> matching = composerAliases.values(maker);
319
        foreach (Composer *c, matching) {
320
            myGuesses.insert(Guess(10 * scale, c));
321
        }
322
    } else {
323
        ComposerFullTextMatcher matcher(allComposers);
324
        GuessList gl(matcher.match(maker, 5, 0.5));
325
        if (!gl.empty()) {
326
            foreach (Guess guess, gl) {
327
                myGuesses.insert(Guess(guess.confidence() * scale, guess.entity()));
328
            }
329
        }
330
    }
331
    integrateGuesses(guesses, myGuesses);
332
}
333

    
334
void
335
guessFromMakerTag(AudioFileReader::TagMap tags, QString tag, float scale, GuessSet &guesses)
336
{
337
    if (tags.find(tag) != tags.end()) {
338
        guessFromMaker(tags[tag], scale, guesses);
339
    }
340
}
341

    
342
void
343
guessFromTitle(QString title, float scale, GuessSet &guesses)
344
{
345
    QStringList bits = title.split(QRegExp("[:,_-]"),
346
                                   QString::SkipEmptyParts);
347
    if (bits.size() > 1) {
348
        guessFromMaker(bits.first(), scale, guesses);
349
    }
350
}    
351

    
352
void
353
guessFromTitleTag(AudioFileReader::TagMap tags, QString tag, float scale, GuessSet &guesses)
354
{
355
    if (tags.find(tag) != tags.end()) {
356
        guessFromTitle(tags[tag], scale, guesses);
357
    }
358
}
359

    
360
void
361
guessFromFilename(QString filename, float scale, GuessSet &guesses)
362
{
363
    cerr << "guessFromFilename: " << filename << endl;
364
    QString dirpart = QFileInfo(filename).path();
365
    QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts);
366
    dirbits = dirbits.last()
367
        .replace(QRegExp("^\\d+"), "")
368
        .split(QRegExp("[^\\w]"), QString::SkipEmptyParts);
369
    if (!dirbits.empty()) {
370
        guessFromMaker(dirbits.first(), scale, guesses);
371
    }
372

    
373
    QString filepart = QFileInfo(filename).fileName().replace(QRegExp("\\d+"), "");
374
    QStringList filebits = filepart.split(QRegExp("[^\\w]"),
375
                                          QString::SkipEmptyParts);
376
    if (!filebits.empty()) {
377
        guessFromMaker(filebits.first(), scale, guesses);
378
    }
379
}
380

    
381
void
382
guessWorkFromTitleByCatalogue(QString title, float scale,
383
                              Composer *composer, GuessSet &guesses)
384
{
385
    if (title == "") return;
386
    WorkCatalogueMatcher matcher(composer ? worksMap.value(composer) : allWorks);
387
    GuessList gl(matcher.match(title, 0));
388
    if (!gl.empty()) {
389
        foreach (Guess guess, gl) {
390
            guesses.insert(Guess(guess.confidence() * scale, guess.entity()));
391
        }
392
    }
393
}
394

    
395
void
396
guessWorkFromTitle(QString title, float scale,
397
                   Composer *composer, GuessSet &guesses)
398
{
399
    if (title == "") return;
400
    WorkTitleMatcher matcher(composer ? worksMap.value(composer) : allWorks);
401
    GuessList gl(matcher.match(title, 0));
402
    if (!gl.empty()) {
403
        foreach (Guess guess, gl) {
404
            guesses.insert(Guess(guess.confidence() * scale, guess.entity()));
405
        }
406
    }
407
}    
408

    
409
void
410
guessWorkFromTitleTag(AudioFileReader::TagMap tags, QString tag, float scale,
411
                      Composer *composer, GuessSet &guesses)
412
{
413
    cerr << "guessWorkFromTitleTag: " << tag << endl;
414

    
415
    if (tags.find(tag) != tags.end()) {
416
        cerr << "guessWorkFromTitleTag: tag is " << tags[tag] << endl;
417
        GuessSet myGuesses;
418
        guessWorkFromTitle(tags[tag], scale, composer, myGuesses);
419
        integrateGuesses(guesses, myGuesses);
420
        myGuesses.clear();
421
        guessWorkFromTitle(tags[tag], scale, composer, myGuesses);
422
        integrateGuesses(guesses, myGuesses);
423
    }
424
}
425

    
426
void
427
guessWorkFromFilenameByCatalogue(QString filename, float scale,
428
                      Composer *composer, GuessSet &guesses)
429
{
430
    cerr << "guessWorkFromFilename: " << filename << endl;
431

    
432
    QString dirpart = QFileInfo(filename).path();
433
    QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts);
434
    if (!dirbits.empty()) {
435
        guessWorkFromTitleByCatalogue(dirbits.last(), scale * 0.7, composer, guesses);
436
    }
437

    
438
    QString filepart = QFileInfo(filename).fileName().replace
439
        (QRegExp("\\.[^\\.]*"), "").replace(QRegExp("^\\d+[^\\w]+"), "");
440
    guessWorkFromTitleByCatalogue(filepart, scale, composer, guesses);
441
}
442

    
443
void
444
guessWorkFromFilenameByTitle(QString filename, float scale,
445
                             Composer *composer, GuessSet &guesses)
446
{
447
    cerr << "guessWorkFromFilename: " << filename << endl;
448

    
449
    QString dirpart = QFileInfo(filename).path();
450
    QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts);
451
    if (!dirbits.empty()) {
452
        guessWorkFromTitle(dirbits.last(), scale * 0.7, composer, guesses);
453
    }
454

    
455
    QString filepart = QFileInfo(filename).fileName().replace
456
        (QRegExp("\\.[^\\.]*"), "").replace(QRegExp("^\\d+[^\\w]+"), "");
457
    guessWorkFromTitle(filepart, scale, composer, guesses);
458
}
459

    
460
Signal *
461
guess(QString track)
462
{
463
    cout << endl;
464
    cout << "Guessing composer for: " << track << endl;
465

    
466
//    cerr << "Creating Signal object...";
467
    FileSource fs(track);
468
    Signal *tf = new Signal;
469
    tf->addAvailableAs(new AudioFile(fs));
470
//    cerr << "done" << endl;
471
//    cerr << "hash = " << tf->hash() << endl;
472

    
473
    QString fingerprint, puid, maker, title;
474
    AudioFileReader::TagMap tags;
475
    //!!! bad api!:
476
    getTrackData(fs, fingerprint, puid, title, maker, tags);
477

    
478
    cout << "fingerprint: " << fingerprint.toStdString() << ", puid: "
479
         << puid.toStdString() << endl;
480

    
481
    GuessSet guesses;
482

    
483
    guessFromMakerTag(tags, "TCOM", 1.0, guesses);
484
    guessFromMakerTag(tags, "COMPOSER", 1.0, guesses);
485

    
486
    if (guesses.empty() || guesses.begin()->confidence() < 0.4) {
487
        guessFromMakerTag(tags, "TOPE", 0.8, guesses);
488
        guessFromMakerTag(tags, "TPE1", 0.8, guesses);
489

    
490
        guessFromMakerTag(tags, "ARTIST", 0.9, guesses);
491
        guessFromMakerTag(tags, "PERFORMER", 0.8, guesses);
492

    
493
        guessFromTitleTag(tags, "TIT1", 0.4, guesses);
494
        guessFromTitleTag(tags, "TIT2", 0.5, guesses);
495
        guessFromTitleTag(tags, "TALB", 0.5, guesses);
496
        guessFromTitleTag(tags, "TIT3", 0.3, guesses);
497

    
498
        guessFromTitleTag(tags, "TITLE", 0.5, guesses);
499
        guessFromTitleTag(tags, "ALBUM", 0.5, guesses);
500
    }
501

    
502
    if (tags.find("MUSICBRAINZ_ARTISTID") != tags.end()) {
503
        QString id = tags["MUSICBRAINZ_ARTISTID"];
504
        Uri mbzUri = Uri("http://dbtune.org/musicbrainz/resource/artist/" + id);
505
        cout << "MBZ id found: " << id << endl;
506
        if (composerUris.contains(mbzUri)) {
507
            guesses.insert(Guess(2.0, composerUris[mbzUri]));
508
        }
509
    }
510

    
511
    cerr << "Composer guesses:" << endl;
512
    foreach (Guess g, guesses) {
513
        cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
514
    }
515

    
516
    float bc = 0.f;
517
    QString best;
518
    if (!guesses.empty()) {
519
        Guess bg = *guesses.begin();
520
        best = bg.entity()->name();
521
        bc = bg.confidence();
522
    }
523

    
524
    guessFromFilename(track, 0.5, guesses);
525

    
526
    float bc2 = 0.f;
527
    QString best2;
528
    if (!guesses.empty()) {
529
        Guess bg = *guesses.begin();
530
        best2 = bg.entity()->name();
531
        bc2 = bg.confidence();
532
    }
533

    
534
    // If we have only one confident composer guess, consider only
535
    // works from that composer (really this should permit considering
536
    // works from all confident composers)
537
    Composer *confidentComposer = 0;
538
    if (bc2 > 0.5) {
539
        confidentComposer = qobject_cast<Composer *>(guesses.begin()->entity());
540
    }
541

    
542
    QString bestTitle;
543

    
544
    GuessSet workGuesses;
545
    if (tags["TIT2"] != "") {
546
        bestTitle = tags["TIT2"];
547
        guessWorkFromTitleTag(tags, "TIT2", 0.5, confidentComposer, workGuesses);
548
    }
549
    if (tags["TITLE"] != "") {
550
        bestTitle = tags["TITLE"];
551
        guessWorkFromTitleTag(tags, "TITLE", 0.5, confidentComposer, workGuesses);
552
    }
553
    if (workGuesses.empty()) {
554
        guessWorkFromTitleTag(tags, "TIT1", 0.2, confidentComposer, workGuesses);
555
        guessWorkFromTitleTag(tags, "TALB", 0.2, confidentComposer, workGuesses);
556
        guessWorkFromTitleTag(tags, "TIT3", 0.1, confidentComposer, workGuesses);
557
        guessWorkFromTitleTag(tags, "ALBUM", 0.4, confidentComposer, workGuesses);
558
    }
559
    if (workGuesses.empty() || workGuesses.begin()->confidence() < 0.3) {
560
        guessWorkFromFilenameByCatalogue(track, 0.4, confidentComposer, workGuesses);
561
    }
562
    if (workGuesses.empty()) {
563
        guessWorkFromFilenameByTitle(track, 0.3, confidentComposer, workGuesses);
564
    }
565
    
566
    cerr << "Work guesses:" << endl;
567
    foreach (Guess g, workGuesses) {
568
        cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
569
    }
570

    
571
    GuessSet consistentComposers;
572
    GuessSet consistentWorks;
573
    foreach (Guess wg, workGuesses) {
574
        Work *w = qobject_cast<Work *>(wg.entity());
575
        if (!w || !w->getComposer()) continue;
576
        Composer *wc = w->getComposer();
577
        foreach (Guess g, guesses) {
578
            if (g.entity() == wc) {
579
                consistentComposers.insert(g);
580
                consistentWorks.insert(wg);
581
            }
582
        }
583
    }
584

    
585
    cerr << "Consistent composer guesses:" << endl;
586
    foreach (Guess g, consistentComposers) {
587
        cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
588
    }
589

    
590
    cerr << "Consistent work guesses:" << endl;
591
    foreach (Guess g, consistentWorks) {
592
        cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
593
    }
594

    
595
    float bc3 = bc2;
596
    QString best3 = best2;
597
    QString work;
598
    Work *bestWork = 0;
599
    if (!consistentWorks.empty()) {
600
        Guess bg = *consistentWorks.begin();
601
        bestWork = qobject_cast<Work *>(bg.entity());
602
        if (bestWork) {
603
            bc3 = bg.confidence();
604
            best3 = bestWork->getComposerName();
605
            work = bestWork->getDisplayName();
606
        }
607
    }
608

    
609
    cout << track << "|" << best << "|" << bc << "|" << best2 << "|" << bc2 << "|" << best3 << "|" << bc3 << "|" << work << "|" << bestTitle << endl;
610

    
611
    tf->setOfaFingerprint(fingerprint);
612
    tf->setPuid(puid);
613
    tf->setComposer(confidentComposer);
614
    tf->setWork(bestWork);
615
    return tf;
616
}
617

    
618

    
619

    
620
int
621
main(int argc, char **argv)
622
{
623
    //!!! N.B. On Windows this will take the profile path over the
624
    //!!! home drive path -- we don't want that
625
    QString featuresRelPath(".classical-rdf/features");
626
    if (!QDir(QDir::home().filePath(featuresRelPath)).exists() &&
627
        !QDir::home().mkpath(featuresRelPath)) {
628
        std::cerr << "Features directory $HOME/" << featuresRelPath.toStdString()
629
                  << " does not exist and creation failed" << std::endl;
630
        return 2;
631
    }
632

    
633
    QCoreApplication::setApplicationName("classical-rdf");
634

    
635
    FeatureFileIndex *ffi = FeatureFileIndex::getInstance();
636

    
637
    QStringList args;
638
    for (int i = 1; i < argc; ++i) {
639
        args.push_back(argv[i]);
640
    }
641

    
642
    BasicStore bs;
643
    if (!args.empty()) {
644
        foreach (QString track, args) {
645
            FileSource fs(track);
646
            AudioFile af(fs);
647
            ffi->loadFor(&af, &bs);
648
        }
649
    } 
650

    
651
    
652
/*
653
    BasicStore *index = new BasicStore;
654

655
    QDir features(QDir::home().filePath(featuresRelPath));
656
    features.setFilter(QDir::Files);
657
    for (unsigned int i = 0; i < features.count(); ++i) {
658
        QFileInfo fi(features.filePath(features[i]));
659
        if (fi.isFile() && fi.isReadable()) {
660
            std::cout << fi.fileName().toStdString() << std::endl;
661
            try {
662
                QUrl fileUrl(QUrl::fromLocalFile(fi.filePath()));
663
                BasicStore *b = BasicStore::load(fileUrl);
664
                Triples ts = b->match
665
                    (Triple(Node(), "a",
666
                            Uri("http://purl.org/ontology/mo/AudioFile")));
667
                foreach (Triple t, ts) {
668
                    std::cout << "Audio file: <" << t.a.value.toStdString() << ">" << std::endl;
669
                    index->add(Triple(Uri(fileUrl), "a",
670
                                      Uri("http://xmlns.com/foaf/0.1/Document")));
671
                    index->add(Triple(Uri(fileUrl), 
672
                                      Uri("http://xmlns.com/foaf/0.1/primaryTopic"),
673
                                      t.a));
674
                }
675
            } catch (...) { }
676
        }
677
    }
678

679
    
680

681
    index->save("index.ttl");
682
*/
683
/*
684
    if (argc < 3) usage(argv[0]);
685
    QString inRDFFileName = argv[1];
686
    QString command = argv[2];
687
    QStringList args;
688
    for (int i = 3; i < argc; ++i) {
689
        args.push_back(argv[i]);
690
    }
691

692
    //!!! unit test!
693
    int c = Work::compareCatalogueNumberTexts("Op. 1 no 4", "Op. 3 no 2");
694
    std::cerr << c << std::endl;
695
    c = Work::compareCatalogueNumberTexts("1 no 4", "3 no 2");
696
    std::cerr << c << std::endl;
697
    c = Work::compareCatalogueNumberTexts("4 no 2", "3 no 2");
698
    std::cerr << c << std::endl;
699
    c = Work::compareCatalogueNumberTexts("Opus 4 no 2", "3 no 2");
700
    std::cerr << c << std::endl;
701
    c = Work::compareCatalogueNumberTexts("Op 141", "K. 21");
702
    std::cerr << c << std::endl;
703
    c = Work::compareCatalogueNumberTexts("Op 14", "K. 21");
704
    std::cerr << c << std::endl;
705
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6");
706
    std::cerr << c << std::endl;
707
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6b");
708
    std::cerr << c << std::endl;
709
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 7");
710
    std::cerr << c << std::endl;
711
    c = Work::compareCatalogueNumberTexts("Hob XXIIId:Es1", "Hob XXII:B04");
712
    std::cerr << c << std::endl;
713

714
    BasicStore *store = new BasicStore();
715
    store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
716
    ObjectLoader *loader = new ObjectLoader(store);
717

718
    TypeMapping tm;
719

720
    TypeRegistrar::registerTypes();
721
    TypeRegistrar::addMappings(store, &tm);
722

723
    loader->setTypeMapping(tm);
724

725
    if (!load(store, inRDFFileName)) {
726
        cerr << "Failed to load data source" << endl;
727
        return 1;
728
    }
729

730
    cerr << "Imported RDF data, mapping to objects...";
731
    QObjectList objects = loader->loadAll();
732
    cerr << " done" << endl;
733

734
    delete loader;
735
    
736
    foreach (QObject *o, objects) {
737
        Composer *c = qobject_cast<Composer *>(o);
738
        if (c) {
739
            allComposers.push_back(c);
740
            composerAliases.insert(c->name(), c);
741
            foreach (QString alias, c->aliases()) {
742
                composerAliases.insert(alias, c);
743
            }
744
            composerUris.insert(c->property("uri").value<Uri>(), c);
745
            foreach (Uri otherUri, c->otherURIs()) {
746
                composerUris.insert(otherUri, c);
747
            }
748
        }
749
    }
750
    
751
    QList<Work *> works;
752
    foreach (QObject *o, objects) {
753
        Work *w = qobject_cast<Work *>(o);
754
        if (w) works.push_back(w);
755
    }
756

757
    foreach (Work *w, works) {
758
        allWorks.push_back(w);
759
        Composition *c = w->composition();
760
        if (c) {
761
            Composer *cp = c->composer();
762
            if (cp) worksMap[cp].push_back(w);
763
        }
764
    }
765

766
    BasicStore localStore;
767
    TypeRegistrar::addMappings(&localStore, &tm);
768

769
    ObjectStorer *localStorer = new ObjectStorer(&localStore);
770
    localStorer->setTypeMapping(tm);
771
//    localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
772

773
    if (command == "guess") {
774
        if (args.empty()) usage(argv[0]);
775
        foreach (QString track, args) {
776
            Signal *tf = guess(track);
777
            localStorer->store(tf);
778
        }
779
    } 
780

781
    delete localStorer;
782
    localStore.save("local.ttl");
783
        
784
    delete store;
785
*/
786

    
787
    TempDirectory::getInstance()->cleanup();
788
}
789