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 @ 53:bcea875d8d2f

History | View | Annotate | Download (23.7 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

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

    
19
#include "Matcher.h"
20

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

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

    
28
#include <iostream>
29

    
30
using namespace Dataquay;
31
using namespace ClassicalData;
32
using namespace std;
33
using namespace Vamp;
34
using namespace Vamp::HostExt;
35
/*
36
ostream &operator<<(ostream &target, const QString &str)
37
{
38
    return target << str.toLocal8Bit().data();
39
}
40

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
480
    GuessSet guesses;
481

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

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

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

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

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

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

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

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

    
523
    guessFromFilename(track, 0.5, guesses);
524

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

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

    
541
    QString bestTitle;
542

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

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

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

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

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

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

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

    
617

    
618

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

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

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

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

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

    
650
    std::cerr << "Dumping out our local store to local2.ttl" << std::endl;
651
    bs.save("local2.ttl");
652
    
653
/*
654
    BasicStore *index = new BasicStore;
655

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

680
    
681

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

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

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

719
    TypeMapping tm;
720

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

724
    loader->setTypeMapping(tm);
725

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

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

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

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

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

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

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

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

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