Revision 44:ed2befdf1e98

View differences:

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

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

  
6
#include <QMutexLocker>
7
#include <QDir>
8

  
9
#include "base/TempDirectory.h"
10
#include "base/Exceptions.h"
11

  
12
using namespace Dataquay;
13

  
14

  
15
namespace ClassicalData {
16

  
17
FeatureFileIndex *
18
FeatureFileIndex::getInstance()
19
{
20
    static FeatureFileIndex instance;
21
    return &instance;
22
}
23

  
24
FeatureFileIndex::FeatureFileIndex() :
25
    m_index(0)
26
{
27
    try {
28
	m_indexFileName = getIndexFileName();
29
    } catch (DirectoryCreationFailed f) {
30
        std::cerr << "FeatureFileIndex: ERROR: Failed to find or create index directory: " << f.what() << std::endl;
31
        return;
32
    }
33

  
34
    m_index = new BasicStore;
35

  
36
    TypeRegistrar::addMappings(m_index, 0);
37

  
38
    if (QFile(m_indexFileName).exists()) {
39
	m_index->import(QUrl::fromLocalFile(m_indexFileName),
40
			BasicStore::ImportIgnoreDuplicates);
41
	//!!! catch
42
    }
43
}
44

  
45
FeatureFileIndex::~FeatureFileIndex()
46
{
47
}
48

  
49
QString
50
FeatureFileIndex::getIndexFileName()
51
{
52
    QDir d = TempDirectory::getInstance()->getContainingPath();
53
    QString n("index");
54
    QFileInfo fi(d.filePath(n));
55

  
56
    if ((fi.exists() && !fi.isDir()) ||
57
        (!fi.exists() && !d.mkdir(n))) {
58
        throw DirectoryCreationFailed(fi.filePath());
59
    }
60

  
61
    return QDir(fi.filePath()).filePath("features.ttl");
62
}
63

  
64
QString
65
FeatureFileIndex::getFeatureDirectoryName()
66
{
67
    QDir d = TempDirectory::getInstance()->getContainingPath();
68
    QString n("features");
69
    QFileInfo fi(d.filePath(n));
70

  
71
    if ((fi.exists() && !fi.isDir()) ||
72
        (!fi.exists() && !d.mkdir(n))) {
73
        throw DirectoryCreationFailed(fi.filePath());
74
    }
75

  
76
    return fi.filePath();
77
}
78

  
79
void
80
FeatureFileIndex::loadFor(TrackFile *tf, BasicStore *store)
81
{
82
    if (!m_index) {
83
	std::cerr << "FeatureFileIndex::loadFor: No index!" << std::endl;
84
	return;
85
    }
86
    updateIndex();
87

  
88
    //...
89
}
90

  
91
void
92
FeatureFileIndex::updateIndex()
93
{
94
    QMutexLocker locker(&m_mutex);
95
    if (!m_index) return;
96

  
97
    QDir featureDir;
98
    try {
99
	QString s = getFeatureDirectoryName();
100
        featureDir = QDir(s);
101
    } catch (DirectoryCreationFailed f) {
102
        std::cerr << "FeatureFileIndex::updateIndex: ERROR: Failed to find or create feature directory: " << f.what() << std::endl;
103
        return;
104
    }
105

  
106
    featureDir.setFilter(QDir::Files);
107

  
108
    for (unsigned int i = 0; i < featureDir.count(); ++i) {
109

  
110
        QFileInfo fi(featureDir.filePath(featureDir[i]));
111

  
112
        if (fi.isFile() && fi.isReadable()) {
113
	    QUrl fileUrl(QUrl::fromLocalFile(fi.filePath()));
114
            try {
115
                BasicStore *b = BasicStore::load(fileUrl);
116
                Triples ts = b->match
117
                    (Triple(Node(), "a", m_index->expand("mo:AudioFile")));
118
                foreach (Triple t, ts) {
119
                    m_index->add(Triple(Uri(fileUrl), "a", m_index->expand("foaf:Document")));
120
                    m_index->add(Triple(Uri(fileUrl), "foaf:primaryTopic", t.a));;
121
                }
122
            } catch (...) { }
123
        }
124
    }
125

  
126
    //!!! remove triples from index that refer to nonexistent files?
127

  
128
    std::cerr << "Saving index to " << m_indexFileName.toStdString() << std::endl;
129
    m_index->save(m_indexFileName);
130
}
131

  
132

  
133
}
134

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

  
3
#ifndef _CLASSICAL_DATA_FEATURE_FILE_INDEX_H_
4
#define _CLASSICAL_DATA_FEATURE_FILE_INDEX_H_
5

  
6
#include "Objects.h"
7

  
8
#include <dataquay/BasicStore.h>
9

  
10
#include <QMutex>
11

  
12
namespace ClassicalData {
13

  
14
class FeatureFileIndex
15
{
16
public:
17
    static FeatureFileIndex *getInstance();
18
    
19
    FeatureFileIndex();
20
    ~FeatureFileIndex();
21

  
22
    void loadFor(TrackFile *, Dataquay::BasicStore *);
23

  
24
private:
25
    QMutex m_mutex;
26
    QString m_indexFileName;
27
    Dataquay::BasicStore *m_index;
28
    QString getIndexFileName();
29
    QString getFeatureDirectoryName();
30
    void updateIndex();
31
};
32

  
33
}
34

  
35
#endif
36

  
37

  
38
    
common/TypeRegistrar.cpp
179 179
	mapping->addTypeUriPrefixMapping("ClassicalData::Composition", store->expand(":event/"));
180 180
	mapping->addPropertyMapping("ClassicalData::Composition", "composer", store->expand("mo:composer"));
181 181
	mapping->addPropertyMapping("ClassicalData::Composition", "works", store->expand("mo:produced_work"));
182

  
183
	mapping->addTypeMapping("ClassicalData::TrackFile", store->expand("mo:AudioFile"));
182 184
    }
183 185
}
184 186

  
common/common.pro
8 8

  
9 9
CONFIG += staticlib
10 10

  
11
HEADERS += EditDistance.h Objects.h Matcher.h TypeRegistrar.h
12
SOURCES += EditDistance.cpp Objects.cpp Matcher.cpp TypeRegistrar.cpp
11
HEADERS += EditDistance.h Objects.h Matcher.h TypeRegistrar.h FeatureFileIndex.h
12
SOURCES += EditDistance.cpp Objects.cpp Matcher.cpp TypeRegistrar.cpp FeatureFileIndex.cpp
13 13

  
utilities/the-application/the-application.cpp
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
TrackFile *
461
guess(QString track)
462
{
463
    cout << endl;
464
    cout << "Guessing composer for: " << track << endl;
465

  
466
//    cerr << "Creating TrackFile object...";
467
    FileSource fs(track);
468
    TrackFile *tf = new TrackFile(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
    ffi->loadFor(0, 0);
637
    
638
/*
639
    BasicStore *index = new BasicStore;
640

  
641
    QDir features(QDir::home().filePath(featuresRelPath));
642
    features.setFilter(QDir::Files);
643
    for (unsigned int i = 0; i < features.count(); ++i) {
644
        QFileInfo fi(features.filePath(features[i]));
645
        if (fi.isFile() && fi.isReadable()) {
646
            std::cout << fi.fileName().toStdString() << std::endl;
647
            try {
648
                QUrl fileUrl(QUrl::fromLocalFile(fi.filePath()));
649
                BasicStore *b = BasicStore::load(fileUrl);
650
                Triples ts = b->match
651
                    (Triple(Node(), "a",
652
                            Uri("http://purl.org/ontology/mo/AudioFile")));
653
                foreach (Triple t, ts) {
654
                    std::cout << "Audio file: <" << t.a.value.toStdString() << ">" << std::endl;
655
                    index->add(Triple(Uri(fileUrl), "a",
656
                                      Uri("http://xmlns.com/foaf/0.1/Document")));
657
                    index->add(Triple(Uri(fileUrl), 
658
                                      Uri("http://xmlns.com/foaf/0.1/primaryTopic"),
659
                                      t.a));
660
                }
661
            } catch (...) { }
662
        }
663
    }
664

  
665
    
666

  
667
    index->save("index.ttl");
668
*/
669
/*
670
    if (argc < 3) usage(argv[0]);
671
    QString inRDFFileName = argv[1];
672
    QString command = argv[2];
673
    QStringList args;
674
    for (int i = 3; i < argc; ++i) {
675
        args.push_back(argv[i]);
676
    }
677

  
678
    //!!! unit test!
679
    int c = Work::compareCatalogueNumberTexts("Op. 1 no 4", "Op. 3 no 2");
680
    std::cerr << c << std::endl;
681
    c = Work::compareCatalogueNumberTexts("1 no 4", "3 no 2");
682
    std::cerr << c << std::endl;
683
    c = Work::compareCatalogueNumberTexts("4 no 2", "3 no 2");
684
    std::cerr << c << std::endl;
685
    c = Work::compareCatalogueNumberTexts("Opus 4 no 2", "3 no 2");
686
    std::cerr << c << std::endl;
687
    c = Work::compareCatalogueNumberTexts("Op 141", "K. 21");
688
    std::cerr << c << std::endl;
689
    c = Work::compareCatalogueNumberTexts("Op 14", "K. 21");
690
    std::cerr << c << std::endl;
691
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6");
692
    std::cerr << c << std::endl;
693
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6b");
694
    std::cerr << c << std::endl;
695
    c = Work::compareCatalogueNumberTexts("Op 6a", "Op 7");
696
    std::cerr << c << std::endl;
697
    c = Work::compareCatalogueNumberTexts("Hob XXIIId:Es1", "Hob XXII:B04");
698
    std::cerr << c << std::endl;
699

  
700
    BasicStore *store = new BasicStore();
701
    store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
702
    ObjectLoader *loader = new ObjectLoader(store);
703

  
704
    TypeMapping tm;
705

  
706
    TypeRegistrar::registerTypes();
707
    TypeRegistrar::addMappings(store, &tm);
708

  
709
    loader->setTypeMapping(tm);
710

  
711
    if (!load(store, inRDFFileName)) {
712
	cerr << "Failed to load data source" << endl;
713
	return 1;
714
    }
715

  
716
    cerr << "Imported RDF data, mapping to objects...";
717
    QObjectList objects = loader->loadAll();
718
    cerr << " done" << endl;
719

  
720
    delete loader;
721
    
722
    foreach (QObject *o, objects) {
723
        Composer *c = qobject_cast<Composer *>(o);
724
        if (c) {
725
            allComposers.push_back(c);
726
            composerAliases.insert(c->name(), c);
727
            foreach (QString alias, c->aliases()) {
728
                composerAliases.insert(alias, c);
729
            }
730
            composerUris.insert(c->property("uri").value<Uri>(), c);
731
            foreach (Uri otherUri, c->otherURIs()) {
732
                composerUris.insert(otherUri, c);
733
            }
734
        }
735
    }
736
    
737
    QList<Work *> works;
738
    foreach (QObject *o, objects) {
739
        Work *w = qobject_cast<Work *>(o);
740
        if (w) works.push_back(w);
741
    }
742

  
743
    foreach (Work *w, works) {
744
        allWorks.push_back(w);
745
        Composition *c = w->composition();
746
        if (c) {
747
            Composer *cp = c->composer();
748
            if (cp) worksMap[cp].push_back(w);
749
        }
750
    }
751

  
752
    BasicStore localStore;
753
    TypeRegistrar::addMappings(&localStore, &tm);
754

  
755
    ObjectStorer *localStorer = new ObjectStorer(&localStore);
756
    localStorer->setTypeMapping(tm);
757
//    localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
758

  
759
    if (command == "guess") {
760
        if (args.empty()) usage(argv[0]);
761
        foreach (QString track, args) {
762
            TrackFile *tf = guess(track);
763
            localStorer->store(tf);
764
        }
765
    } 
766

  
767
    delete localStorer;
768
    localStore.save("local.ttl");
769
        
770
    delete store;
771
*/
772

  
773
    TempDirectory::getInstance()->cleanup();
774
}
775

  
utilities/the-application/the-application.pro
1
TEMPLATE = app
2
TARGET = the-application
3
#QT -= gui network xml
4

  
5
load(../../../all.prf)
6

  
7
SOURCES += the-application.cpp
8

  
9
PRE_TARGETDEPS += ../../common/libcommon.a
10

  
11
INCLUDEPATH += ../../common ../../../svcore
12

  
13
LIBS += ../../common/libcommon.a -L../../../svcore -lsvcore ../../../../turbot/dataquay/libdataquay.a ../../../../turbot/ext/libext.a
14

  

Also available in: Unified diff