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 / track / track.cpp @ 45:0033259c6772

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

    
6
#include <dataquay/BasicStore.h>
7
#include <dataquay/TransactionalStore.h>
8
#include <dataquay/RDFException.h>
9
#include <dataquay/objectmapper/ObjectLoader.h>
10
#include <dataquay/objectmapper/ObjectStorer.h>
11
#include <dataquay/objectmapper/ObjectMapper.h>
12
#include <dataquay/objectmapper/TypeMapping.h>
13
#include <dataquay/Debug.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

    
26
#include <iostream>
27

    
28
using namespace Dataquay;
29
using namespace ClassicalData;
30
using namespace std;
31
using namespace Vamp;
32
using namespace Vamp::HostExt;
33

    
34
ostream &operator<<(ostream &target, const QString &str)
35
{
36
    return target << str.toLocal8Bit().data();
37
}
38

    
39
ostream &operator<<(ostream &target, const QUrl &u)
40
{
41
    return target << "<" << u.toString() << ">";
42
}
43

    
44

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

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

    
64
    cerr << " done" << endl;
65
    return true;
66
}
67

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
475
    cout << "fingerprint: " << fingerprint.toStdString() << ", puid: "
476
         << puid.toStdString() << endl;
477

    
478
    GuessSet guesses;
479

    
480
    guessFromMakerTag(tags, "TCOM", 1.0, guesses);
481
    guessFromMakerTag(tags, "COMPOSER", 1.0, guesses);
482

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

    
487
        guessFromMakerTag(tags, "ARTIST", 0.9, guesses);
488
        guessFromMakerTag(tags, "PERFORMER", 0.8, guesses);
489

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

    
495
        guessFromTitleTag(tags, "TITLE", 0.5, guesses);
496
        guessFromTitleTag(tags, "ALBUM", 0.5, guesses);
497
    }
498

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

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

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

    
521
    guessFromFilename(track, 0.5, guesses);
522

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

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

    
539
    QString bestTitle;
540

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

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

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

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

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

    
606
    cout << track << "|" << best << "|" << bc << "|" << best2 << "|" << bc2 << "|" << best3 << "|" << bc3 << "|" << work << "|" << bestTitle << endl;
607

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

    
615
int
616
main(int argc, char **argv)
617
{
618
    if (argc < 3) usage(argv[0]);
619
    QString inRDFFileName = argv[1];
620
    QString command = argv[2];
621
    QStringList args;
622
    for (int i = 3; i < argc; ++i) {
623
        args.push_back(argv[i]);
624
    }
625

    
626
    //!!! unit test!
627
    int c = Work::compareCatalogueNumberTexts("Op. 1 no 4", "Op. 3 no 2");
628
    std::cerr << c << std::endl;
629
    c = Work::compareCatalogueNumberTexts("1 no 4", "3 no 2");
630
    std::cerr << c << std::endl;
631
    c = Work::compareCatalogueNumberTexts("4 no 2", "3 no 2");
632
    std::cerr << c << std::endl;
633
    c = Work::compareCatalogueNumberTexts("Opus 4 no 2", "3 no 2");
634
    std::cerr << c << std::endl;
635
    c = Work::compareCatalogueNumberTexts("Op 141", "K. 21");
636
    std::cerr << c << std::endl;
637
    c = Work::compareCatalogueNumberTexts("Op 14", "K. 21");
638
    std::cerr << c << std::endl;
639
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6");
640
    std::cerr << c << std::endl;
641
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6b");
642
    std::cerr << c << std::endl;
643
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 7");
644
    std::cerr << c << std::endl;
645
    c = Work::compareCatalogueNumberTexts("Hob XXIIId:Es1", "Hob XXII:B04");
646
    std::cerr << c << std::endl;
647

    
648
    BasicStore *store = new BasicStore();
649
    store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
650
    ObjectLoader *loader = new ObjectLoader(store);
651

    
652
    TypeMapping tm;
653

    
654
    TypeRegistrar::registerTypes();
655
    TypeRegistrar::addMappings(store, &tm);
656

    
657
    loader->setTypeMapping(tm);
658

    
659
    if (!load(store, inRDFFileName)) {
660
        cerr << "Failed to load data source" << endl;
661
        return 1;
662
    }
663

    
664
    cerr << "Imported RDF data, mapping to objects...";
665
    QObjectList objects = loader->loadAll();
666
    cerr << " done" << endl;
667

    
668
    delete loader;
669
    
670
    foreach (QObject *o, objects) {
671
        Composer *c = qobject_cast<Composer *>(o);
672
        if (c) {
673
            allComposers.push_back(c);
674
            composerAliases.insert(c->name(), c);
675
            foreach (QString alias, c->aliases()) {
676
                composerAliases.insert(alias, c);
677
            }
678
            composerUris.insert(c->property("uri").value<Uri>(), c);
679
            foreach (Uri otherUri, c->otherURIs()) {
680
                composerUris.insert(otherUri, c);
681
            }
682
        }
683
    }
684
    
685
    QList<Work *> works;
686
    foreach (QObject *o, objects) {
687
        Work *w = qobject_cast<Work *>(o);
688
        if (w) works.push_back(w);
689
    }
690

    
691
    foreach (Work *w, works) {
692
        allWorks.push_back(w);
693
        Composition *c = w->composition();
694
        if (c) {
695
            Composer *cp = c->composer();
696
            if (cp) worksMap[cp].push_back(w);
697
        }
698
    }
699

    
700
    BasicStore localStore;
701
    TypeRegistrar::addMappings(&localStore, &tm);
702

    
703
    ObjectStorer *localStorer = new ObjectStorer(&localStore);
704
    localStorer->setTypeMapping(tm);
705
    localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
706
    localStorer->setPropertyStorePolicy(ObjectStorer::StoreIfChanged);
707

    
708
    if (command == "guess") {
709
        if (args.empty()) usage(argv[0]);
710
        foreach (QString track, args) {
711
            Signal *tf = guess(track);
712
            localStorer->store(tf);
713
        }
714
    } else {
715
        usage(argv[0]);
716
    }
717

    
718
    delete localStorer;
719
    localStore.save("local.ttl");
720
        
721
    delete store;
722
    TempDirectory::getInstance()->cleanup();
723
}
724