annotate utilities/the-application/the-application.cpp @ 53:bcea875d8d2f tip

More build fixes
author Chris Cannam
date Thu, 16 Oct 2014 19:03:51 +0100
parents c8b777862198
children
rev   line source
Chris@32 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@32 2
Chris@32 3 #include "Objects.h"
Chris@32 4 #include "TypeRegistrar.h"
Chris@44 5 #include "FeatureFileIndex.h"
Chris@32 6
Chris@32 7 #include <dataquay/BasicStore.h>
Chris@32 8 #include <dataquay/TransactionalStore.h>
Chris@32 9 #include <dataquay/RDFException.h>
Chris@32 10 #include <dataquay/objectmapper/ObjectLoader.h>
Chris@32 11 #include <dataquay/objectmapper/ObjectStorer.h>
Chris@32 12 #include <dataquay/objectmapper/ObjectMapper.h>
Chris@32 13 #include <dataquay/objectmapper/TypeMapping.h>
Chris@32 14
Chris@32 15 #include "data/fileio/AudioFileReaderFactory.h"
Chris@32 16 #include "data/fileio/AudioFileReader.h"
Chris@33 17 #include "base/TempDirectory.h"
Chris@33 18
Chris@33 19 #include "Matcher.h"
Chris@32 20
Chris@32 21 #include <vamp-hostsdk/PluginLoader.h>
Chris@32 22
Chris@32 23 #include <QMultiMap>
Chris@32 24 #include <QFileInfo>
Chris@44 25 #include <QDir>
Chris@44 26 #include <QCoreApplication>
Chris@32 27
Chris@32 28 #include <iostream>
Chris@32 29
Chris@32 30 using namespace Dataquay;
Chris@32 31 using namespace ClassicalData;
Chris@32 32 using namespace std;
Chris@32 33 using namespace Vamp;
Chris@32 34 using namespace Vamp::HostExt;
Chris@53 35 /*
Chris@32 36 ostream &operator<<(ostream &target, const QString &str)
Chris@32 37 {
Chris@32 38 return target << str.toLocal8Bit().data();
Chris@32 39 }
Chris@32 40
Chris@32 41 ostream &operator<<(ostream &target, const QUrl &u)
Chris@32 42 {
Chris@32 43 return target << "<" << u.toString() << ">";
Chris@32 44 }
Chris@53 45 */
Chris@32 46
Chris@32 47 bool
Chris@32 48 load(BasicStore *store, QString fileName)
Chris@32 49 {
Chris@32 50 QUrl url = QUrl::fromLocalFile(fileName);
Chris@32 51
Chris@32 52 cerr << "Importing from URL " << url << " ...";
Chris@32 53 try {
Chris@32 54 store->import(url, BasicStore::ImportPermitDuplicates);
Chris@32 55 } catch (RDFException e) {
Chris@32 56 cerr << " retrying with explicit ntriples type...";
Chris@32 57 try {
Chris@32 58 store->import(url, BasicStore::ImportPermitDuplicates, "ntriples");
Chris@32 59 } catch (RDFException e) {
Chris@32 60 cerr << "failed" << endl;
Chris@32 61 cerr << "Import failed: " << e.what() << endl;
Chris@32 62 return false;
Chris@32 63 }
Chris@32 64 }
Chris@32 65
Chris@32 66 cerr << " done" << endl;
Chris@32 67 return true;
Chris@32 68 }
Chris@32 69
Chris@32 70 void
Chris@32 71 usage(char *name)
Chris@32 72 {
Chris@32 73 int s = 0;
Chris@32 74 for (int i = 0; name[i]; ++i) if (name[i] == '/') s = i + 1;
Chris@32 75 name = name + s;
Chris@32 76 cerr << "Usage:" << endl;
Chris@32 77 cerr << " " << name << " <input-rdf-file> guess <track.wav> [<track.wav> ...]" << endl;
Chris@32 78 exit(-1);
Chris@32 79 }
Chris@32 80
Chris@32 81 static QList<Composer *> allComposers;
Chris@33 82 static QHash<QString, Composer *> composerAliases;
Chris@33 83 static QHash<Uri, Composer *> composerUris;
Chris@37 84 static QMap<Composer *, QList<Work *> > worksMap;
Chris@34 85 static QList<Work *> allWorks;
Chris@32 86
Chris@32 87 void
Chris@32 88 show(Composer *c)
Chris@32 89 {
Chris@32 90 cout << c->property("uri").value<Uri>() << endl;
Chris@32 91 cout << c->getSortName(true);
Chris@32 92 QString d = c->getDisplayDates();
Chris@32 93 if (d != "") cout << " (" << d << ")";
Chris@32 94 if (!c->nationality().empty() || c->period() != "") {
Chris@32 95 cout << " [";
Chris@32 96 bool first = true;
Chris@32 97 foreach (QString n, c->nationality()) {
Chris@32 98 if (!first) cout << "/";
Chris@32 99 cout << n;
Chris@32 100 first = false;
Chris@32 101 }
Chris@32 102 if (c->period() != "") {
Chris@32 103 if (!first) cout << ", ";
Chris@32 104 cout << c->period();
Chris@32 105 }
Chris@32 106 cout << "]";
Chris@32 107 }
Chris@32 108 if (c->gender() != "") {
Chris@32 109 cout << " *" << c->gender();
Chris@32 110 }
Chris@32 111 if (!worksMap[c].empty()) {
Chris@32 112 cout << " [" << worksMap[c].size() << " work(s)]";
Chris@32 113 }
Chris@32 114 cout << endl;
Chris@32 115 foreach (QString a, c->aliases()) {
Chris@32 116 cout << " - " << a << endl;
Chris@32 117 }
Chris@32 118 if (c->remarks() != "") {
Chris@32 119 cout << " " << c->remarks() << endl;
Chris@32 120 }
Chris@32 121 foreach (Document *d, c->pages()) {
Chris@32 122 cout << d->siteName() << " -> " << d->uri() << endl;
Chris@32 123 }
Chris@32 124 foreach (Uri u, c->otherURIs()) {
Chris@32 125 cout << "Same as " << u << endl;
Chris@32 126 }
Chris@32 127 }
Chris@32 128
Chris@32 129 void
Chris@32 130 showBrief(Composer *c)
Chris@32 131 {
Chris@32 132 cout << c->property("uri").value<Uri>() << endl;
Chris@32 133 cout << c->getSortName(false);
Chris@32 134 QString d = c->getDisplayDates();
Chris@32 135 if (d != "") cout << " (" << d << ")";
Chris@32 136 if (!c->nationality().empty() || c->period() != "") {
Chris@32 137 cout << " [";
Chris@32 138 bool first = true;
Chris@32 139 foreach (QString n, c->nationality()) {
Chris@32 140 if (!first) cout << "/";
Chris@32 141 cout << n;
Chris@32 142 first = false;
Chris@32 143 }
Chris@32 144 if (c->period() != "") {
Chris@32 145 if (!first) cout << " ";
Chris@32 146 cout << c->period();
Chris@32 147 }
Chris@32 148 cout << "]";
Chris@32 149 }
Chris@32 150 if (c->gender() != "") {
Chris@32 151 cout << " *" << c->gender();
Chris@32 152 }
Chris@32 153 if (!worksMap[c].empty()) {
Chris@32 154 cout << " [" << worksMap[c].size() << " work(s)]";
Chris@32 155 }
Chris@32 156 cout << endl;
Chris@32 157 }
Chris@32 158
Chris@32 159 void
Chris@32 160 listBrief(QList<Composer *> composers)
Chris@32 161 {
Chris@32 162 QMultiMap<QString, Composer *> sorted;
Chris@32 163 foreach (Composer *c, composers) {
Chris@32 164 sorted.insert(c->getSortName(false), c);
Chris@32 165 }
Chris@32 166 foreach (Composer *c, sorted) {
Chris@32 167 showBrief(c);
Chris@32 168 }
Chris@32 169 }
Chris@32 170
Chris@32 171 void
Chris@32 172 listUris(QList<Composer *> composers)
Chris@32 173 {
Chris@32 174 QMultiMap<Uri, Composer *> sorted;
Chris@32 175 foreach (Composer *c, composers) {
Chris@32 176 sorted.insert(c->property("uri").value<Uri>(), c);
Chris@32 177 }
Chris@32 178 foreach (Uri uri, sorted.keys()) {
Chris@32 179 cout << uri << endl;
Chris@32 180 }
Chris@32 181 }
Chris@32 182
Chris@32 183 void
Chris@32 184 showSearchResults(QMultiMap<float, Composer *> matches, int count)
Chris@32 185 {
Chris@32 186 int n = 0;
Chris@32 187 for (QMultiMap<float, Composer *>::const_iterator i = matches.end();
Chris@32 188 i != matches.begin(); ) {
Chris@32 189 --i;
Chris@32 190 if (i.key() <= 0) continue;
Chris@32 191 cout << endl;
Chris@32 192 if (n == 0) {
Chris@32 193 cout << "Best match:" << endl;
Chris@32 194 } else if (n == 1) {
Chris@32 195 cout << "Other candidate(s):" << endl;
Chris@32 196 }
Chris@32 197 cout << "[" << i.key() << "] ";
Chris@32 198 if (n == 0) show(i.value());
Chris@32 199 else showBrief(i.value());
Chris@32 200 if (++n > count) break;
Chris@32 201 }
Chris@32 202 if (n == 0) cout << "No matches" << endl;
Chris@32 203 cout << endl;
Chris@32 204 }
Chris@32 205
Chris@32 206 void
Chris@33 207 getTrackData(FileSource source, QString &fingerprint, QString &puid,
Chris@33 208 QString &title, QString &maker, AudioFileReader::TagMap &tags)
Chris@32 209 {
Chris@43 210 AudioFileReader *reader = AudioFileReaderFactory::createReader(source);
Chris@43 211 // AudioFileReader *reader = AudioFileReaderFactory::createThreadingReader(source);
Chris@32 212 if (!reader || !reader->isOK()) {
Chris@32 213 cerr << "Failed to open audio file" << endl;
Chris@32 214 return;
Chris@32 215 }
Chris@32 216
Chris@33 217 title = reader->getTitle();
Chris@33 218 maker = reader->getMaker();
Chris@34 219 // cout << "File tag title: " << reader->getTitle() << endl;
Chris@34 220 // cout << "File tag maker: " << reader->getMaker() << endl;
Chris@32 221
Chris@32 222 cout << "All tags:" << endl;
Chris@33 223 tags = reader->getTags();
Chris@32 224 for (AudioFileReader::TagMap::const_iterator i = tags.begin();
Chris@32 225 i != tags.end(); ++i) {
Chris@32 226 cout << i->first << " " << i->second << endl;
Chris@32 227 }
Chris@32 228
Chris@32 229 PluginLoader *pl = PluginLoader::getInstance();
Chris@32 230 Plugin *plugin = pl->loadPlugin
Chris@32 231 ("ofa-vamp-plugin:ofa_puid_and_fingerprint", reader->getSampleRate(), PluginLoader::ADAPT_ALL);
Chris@32 232 if (!plugin) {
Chris@32 233 cerr << "Failed to load OFA Vamp plugin" << endl;
Chris@32 234 return;
Chris@32 235 }
Chris@32 236
Chris@32 237 // 135 seconds... well, ok, let's have 136
Chris@32 238 int secs = 136;
Chris@32 239
Chris@32 240 int want = int(secs * reader->getSampleRate());
Chris@32 241 int ch = reader->getChannelCount();
Chris@32 242 std::vector<SampleBlock> samples;
Chris@32 243 reader->getDeInterleavedFrames(0, want, samples);
Chris@32 244 int have = samples[0].size();
Chris@32 245 if (!plugin->initialise(ch, have, have)) {
Chris@32 246 cerr << "Failed to initialise(" << ch << "," << have << "," << have << ") plugin" << endl;
Chris@32 247 return;
Chris@32 248 }
Chris@32 249
Chris@32 250 float **input = new float *[ch];
Chris@32 251 for (int i = 0; i < ch; ++i) {
Chris@32 252 input[i] = &samples[i][0];
Chris@32 253 }
Chris@32 254 Plugin::FeatureSet features = plugin->process(input, RealTime::zeroTime);
Chris@33 255 if (!features[0].empty()) {
Chris@33 256 fingerprint = QString::fromStdString(features[0][0].label);
Chris@33 257 }
Chris@33 258 if (!features[1].empty()) {
Chris@33 259 puid = QString::fromStdString(features[1][0].label);
Chris@33 260 }
Chris@32 261 features = plugin->getRemainingFeatures();
Chris@33 262 if (fingerprint == "" && !features[0].empty()) {
Chris@33 263 fingerprint = QString::fromStdString(features[0][0].label);
Chris@33 264 }
Chris@33 265 if (puid == "" && !features[1].empty()) {
Chris@33 266 puid = QString::fromStdString(features[1][0].label);
Chris@33 267 }
Chris@34 268 delete[] input;
Chris@34 269 delete plugin;
Chris@34 270 delete reader;
Chris@34 271 }
Chris@34 272
Chris@34 273 float
Chris@34 274 bonusFactor(NamedEntity *e)
Chris@34 275 {
Chris@34 276 // tiny nudge to prefer composers we actually have works for
Chris@34 277 Composer *c = qobject_cast<Composer *>(e);
Chris@34 278 float f = 1.f;
Chris@34 279 int sz = 0;
Chris@34 280 if (c && worksMap.contains(c)) {
Chris@34 281 sz = worksMap[c].size();
Chris@34 282 while (sz > 0) {
Chris@34 283 f += 0.01;
Chris@34 284 sz = sz / 10;
Chris@34 285 }
Chris@34 286 }
Chris@34 287 return f;
Chris@34 288 }
Chris@34 289
Chris@34 290 void
Chris@34 291 integrateGuesses(GuessSet &guesses, GuessSet newGuesses)
Chris@34 292 {
Chris@34 293 QHash<NamedEntity *, float> ecmap;
Chris@34 294 foreach (Guess g, guesses) {
Chris@34 295 ecmap[g.entity()] += g.confidence() * bonusFactor(g.entity());
Chris@34 296 }
Chris@34 297 foreach (Guess g, newGuesses) {
Chris@34 298 if (ecmap.contains(g.entity())) {
Chris@34 299 ecmap[g.entity()] += g.confidence() / 2;
Chris@34 300 } else {
Chris@34 301 ecmap[g.entity()] = g.confidence();
Chris@34 302 }
Chris@34 303 }
Chris@34 304 guesses.clear();
Chris@34 305 foreach (NamedEntity *e, ecmap.keys()) {
Chris@34 306 guesses.insert(Guess(ecmap[e], e));
Chris@34 307 }
Chris@34 308 }
Chris@34 309
Chris@34 310 void
Chris@34 311 guessFromMaker(QString maker, float scale, GuessSet &guesses)
Chris@34 312 {
Chris@34 313 if (maker == "") return;
Chris@34 314 // cerr << "guessFromMaker: " << maker << endl;
Chris@34 315 GuessSet myGuesses;
Chris@34 316 if (composerAliases.contains(maker)) {
Chris@34 317 QList<Composer *> matching = composerAliases.values(maker);
Chris@34 318 foreach (Composer *c, matching) {
Chris@34 319 myGuesses.insert(Guess(10 * scale, c));
Chris@34 320 }
Chris@34 321 } else {
Chris@34 322 ComposerFullTextMatcher matcher(allComposers);
Chris@34 323 GuessList gl(matcher.match(maker, 5, 0.5));
Chris@34 324 if (!gl.empty()) {
Chris@34 325 foreach (Guess guess, gl) {
Chris@34 326 myGuesses.insert(Guess(guess.confidence() * scale, guess.entity()));
Chris@34 327 }
Chris@34 328 }
Chris@34 329 }
Chris@34 330 integrateGuesses(guesses, myGuesses);
Chris@34 331 }
Chris@34 332
Chris@34 333 void
Chris@34 334 guessFromMakerTag(AudioFileReader::TagMap tags, QString tag, float scale, GuessSet &guesses)
Chris@34 335 {
Chris@34 336 if (tags.find(tag) != tags.end()) {
Chris@34 337 guessFromMaker(tags[tag], scale, guesses);
Chris@34 338 }
Chris@34 339 }
Chris@34 340
Chris@34 341 void
Chris@34 342 guessFromTitle(QString title, float scale, GuessSet &guesses)
Chris@34 343 {
Chris@34 344 QStringList bits = title.split(QRegExp("[:,_-]"),
Chris@34 345 QString::SkipEmptyParts);
Chris@34 346 if (bits.size() > 1) {
Chris@34 347 guessFromMaker(bits.first(), scale, guesses);
Chris@34 348 }
Chris@34 349 }
Chris@34 350
Chris@34 351 void
Chris@34 352 guessFromTitleTag(AudioFileReader::TagMap tags, QString tag, float scale, GuessSet &guesses)
Chris@34 353 {
Chris@34 354 if (tags.find(tag) != tags.end()) {
Chris@34 355 guessFromTitle(tags[tag], scale, guesses);
Chris@34 356 }
Chris@34 357 }
Chris@34 358
Chris@34 359 void
Chris@34 360 guessFromFilename(QString filename, float scale, GuessSet &guesses)
Chris@34 361 {
Chris@34 362 cerr << "guessFromFilename: " << filename << endl;
Chris@37 363 QString dirpart = QFileInfo(filename).path();
Chris@34 364 QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts);
Chris@37 365 dirbits = dirbits.last()
Chris@37 366 .replace(QRegExp("^\\d+"), "")
Chris@37 367 .split(QRegExp("[^\\w]"), QString::SkipEmptyParts);
Chris@34 368 if (!dirbits.empty()) {
Chris@34 369 guessFromMaker(dirbits.first(), scale, guesses);
Chris@34 370 }
Chris@34 371
Chris@34 372 QString filepart = QFileInfo(filename).fileName().replace(QRegExp("\\d+"), "");
Chris@34 373 QStringList filebits = filepart.split(QRegExp("[^\\w]"),
Chris@34 374 QString::SkipEmptyParts);
Chris@34 375 if (!filebits.empty()) {
Chris@34 376 guessFromMaker(filebits.first(), scale, guesses);
Chris@34 377 }
Chris@34 378 }
Chris@34 379
Chris@37 380 void
Chris@37 381 guessWorkFromTitleByCatalogue(QString title, float scale,
Chris@37 382 Composer *composer, GuessSet &guesses)
Chris@34 383 {
Chris@34 384 if (title == "") return;
Chris@37 385 WorkCatalogueMatcher matcher(composer ? worksMap.value(composer) : allWorks);
Chris@37 386 GuessList gl(matcher.match(title, 0));
Chris@37 387 if (!gl.empty()) {
Chris@37 388 foreach (Guess guess, gl) {
Chris@37 389 guesses.insert(Guess(guess.confidence() * scale, guess.entity()));
Chris@34 390 }
Chris@34 391 }
Chris@37 392 }
Chris@37 393
Chris@37 394 void
Chris@37 395 guessWorkFromTitle(QString title, float scale,
Chris@37 396 Composer *composer, GuessSet &guesses)
Chris@37 397 {
Chris@37 398 if (title == "") return;
Chris@37 399 WorkTitleMatcher matcher(composer ? worksMap.value(composer) : allWorks);
Chris@37 400 GuessList gl(matcher.match(title, 0));
Chris@37 401 if (!gl.empty()) {
Chris@37 402 foreach (Guess guess, gl) {
Chris@37 403 guesses.insert(Guess(guess.confidence() * scale, guess.entity()));
Chris@35 404 }
Chris@35 405 }
Chris@34 406 }
Chris@34 407
Chris@34 408 void
Chris@37 409 guessWorkFromTitleTag(AudioFileReader::TagMap tags, QString tag, float scale,
Chris@37 410 Composer *composer, GuessSet &guesses)
Chris@34 411 {
Chris@40 412 cerr << "guessWorkFromTitleTag: " << tag << endl;
Chris@40 413
Chris@34 414 if (tags.find(tag) != tags.end()) {
Chris@40 415 cerr << "guessWorkFromTitleTag: tag is " << tags[tag] << endl;
Chris@37 416 GuessSet myGuesses;
Chris@37 417 guessWorkFromTitle(tags[tag], scale, composer, myGuesses);
Chris@37 418 integrateGuesses(guesses, myGuesses);
Chris@37 419 myGuesses.clear();
Chris@37 420 guessWorkFromTitle(tags[tag], scale, composer, myGuesses);
Chris@37 421 integrateGuesses(guesses, myGuesses);
Chris@34 422 }
Chris@32 423 }
Chris@32 424
Chris@33 425 void
Chris@37 426 guessWorkFromFilenameByCatalogue(QString filename, float scale,
Chris@37 427 Composer *composer, GuessSet &guesses)
Chris@35 428 {
Chris@35 429 cerr << "guessWorkFromFilename: " << filename << endl;
Chris@37 430
Chris@37 431 QString dirpart = QFileInfo(filename).path();
Chris@37 432 QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts);
Chris@37 433 if (!dirbits.empty()) {
Chris@37 434 guessWorkFromTitleByCatalogue(dirbits.last(), scale * 0.7, composer, guesses);
Chris@37 435 }
Chris@37 436
Chris@37 437 QString filepart = QFileInfo(filename).fileName().replace
Chris@37 438 (QRegExp("\\.[^\\.]*"), "").replace(QRegExp("^\\d+[^\\w]+"), "");
Chris@37 439 guessWorkFromTitleByCatalogue(filepart, scale, composer, guesses);
Chris@37 440 }
Chris@37 441
Chris@37 442 void
Chris@37 443 guessWorkFromFilenameByTitle(QString filename, float scale,
Chris@37 444 Composer *composer, GuessSet &guesses)
Chris@37 445 {
Chris@37 446 cerr << "guessWorkFromFilename: " << filename << endl;
Chris@37 447
Chris@37 448 QString dirpart = QFileInfo(filename).path();
Chris@37 449 QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts);
Chris@37 450 if (!dirbits.empty()) {
Chris@37 451 guessWorkFromTitle(dirbits.last(), scale * 0.7, composer, guesses);
Chris@37 452 }
Chris@37 453
Chris@37 454 QString filepart = QFileInfo(filename).fileName().replace
Chris@37 455 (QRegExp("\\.[^\\.]*"), "").replace(QRegExp("^\\d+[^\\w]+"), "");
Chris@37 456 guessWorkFromTitle(filepart, scale, composer, guesses);
Chris@35 457 }
Chris@35 458
Chris@45 459 Signal *
Chris@33 460 guess(QString track)
Chris@33 461 {
Chris@34 462 cout << endl;
Chris@33 463 cout << "Guessing composer for: " << track << endl;
Chris@33 464
Chris@45 465 // cerr << "Creating Signal object...";
Chris@33 466 FileSource fs(track);
Chris@45 467 Signal *tf = new Signal;
Chris@45 468 tf->addAvailableAs(new AudioFile(fs));
Chris@34 469 // cerr << "done" << endl;
Chris@34 470 // cerr << "hash = " << tf->hash() << endl;
Chris@33 471
Chris@33 472 QString fingerprint, puid, maker, title;
Chris@33 473 AudioFileReader::TagMap tags;
Chris@33 474 //!!! bad api!:
Chris@33 475 getTrackData(fs, fingerprint, puid, title, maker, tags);
Chris@33 476
Chris@43 477 cout << "fingerprint: " << fingerprint.toStdString() << ", puid: "
Chris@43 478 << puid.toStdString() << endl;
Chris@43 479
Chris@34 480 GuessSet guesses;
Chris@34 481
Chris@34 482 guessFromMakerTag(tags, "TCOM", 1.0, guesses);
Chris@34 483 guessFromMakerTag(tags, "COMPOSER", 1.0, guesses);
Chris@34 484
Chris@34 485 if (guesses.empty() || guesses.begin()->confidence() < 0.4) {
Chris@34 486 guessFromMakerTag(tags, "TOPE", 0.8, guesses);
Chris@34 487 guessFromMakerTag(tags, "TPE1", 0.8, guesses);
Chris@34 488
Chris@34 489 guessFromMakerTag(tags, "ARTIST", 0.9, guesses);
Chris@34 490 guessFromMakerTag(tags, "PERFORMER", 0.8, guesses);
Chris@34 491
Chris@34 492 guessFromTitleTag(tags, "TIT1", 0.4, guesses);
Chris@34 493 guessFromTitleTag(tags, "TIT2", 0.5, guesses);
Chris@34 494 guessFromTitleTag(tags, "TALB", 0.5, guesses);
Chris@34 495 guessFromTitleTag(tags, "TIT3", 0.3, guesses);
Chris@34 496
Chris@34 497 guessFromTitleTag(tags, "TITLE", 0.5, guesses);
Chris@34 498 guessFromTitleTag(tags, "ALBUM", 0.5, guesses);
Chris@33 499 }
Chris@33 500
Chris@33 501 if (tags.find("MUSICBRAINZ_ARTISTID") != tags.end()) {
Chris@33 502 QString id = tags["MUSICBRAINZ_ARTISTID"];
Chris@33 503 Uri mbzUri = Uri("http://dbtune.org/musicbrainz/resource/artist/" + id);
Chris@33 504 cout << "MBZ id found: " << id << endl;
Chris@33 505 if (composerUris.contains(mbzUri)) {
Chris@34 506 guesses.insert(Guess(2.0, composerUris[mbzUri]));
Chris@33 507 }
Chris@33 508 }
Chris@33 509
Chris@34 510 cerr << "Composer guesses:" << endl;
Chris@34 511 foreach (Guess g, guesses) {
Chris@34 512 cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
Chris@34 513 }
Chris@34 514
Chris@34 515 float bc = 0.f;
Chris@34 516 QString best;
Chris@34 517 if (!guesses.empty()) {
Chris@34 518 Guess bg = *guesses.begin();
Chris@34 519 best = bg.entity()->name();
Chris@34 520 bc = bg.confidence();
Chris@34 521 }
Chris@34 522
Chris@34 523 guessFromFilename(track, 0.5, guesses);
Chris@34 524
Chris@34 525 float bc2 = 0.f;
Chris@34 526 QString best2;
Chris@34 527 if (!guesses.empty()) {
Chris@34 528 Guess bg = *guesses.begin();
Chris@34 529 best2 = bg.entity()->name();
Chris@34 530 bc2 = bg.confidence();
Chris@34 531 }
Chris@34 532
Chris@37 533 // If we have only one confident composer guess, consider only
Chris@37 534 // works from that composer (really this should permit considering
Chris@37 535 // works from all confident composers)
Chris@37 536 Composer *confidentComposer = 0;
Chris@37 537 if (bc2 > 0.5) {
Chris@37 538 confidentComposer = qobject_cast<Composer *>(guesses.begin()->entity());
Chris@37 539 }
Chris@37 540
Chris@39 541 QString bestTitle;
Chris@39 542
Chris@34 543 GuessSet workGuesses;
Chris@40 544 if (tags["TIT2"] != "") {
Chris@40 545 bestTitle = tags["TIT2"];
Chris@40 546 guessWorkFromTitleTag(tags, "TIT2", 0.5, confidentComposer, workGuesses);
Chris@40 547 }
Chris@37 548 if (tags["TITLE"] != "") {
Chris@39 549 bestTitle = tags["TITLE"];
Chris@37 550 guessWorkFromTitleTag(tags, "TITLE", 0.5, confidentComposer, workGuesses);
Chris@37 551 }
Chris@37 552 if (workGuesses.empty()) {
Chris@37 553 guessWorkFromTitleTag(tags, "TIT1", 0.2, confidentComposer, workGuesses);
Chris@37 554 guessWorkFromTitleTag(tags, "TALB", 0.2, confidentComposer, workGuesses);
Chris@37 555 guessWorkFromTitleTag(tags, "TIT3", 0.1, confidentComposer, workGuesses);
Chris@37 556 guessWorkFromTitleTag(tags, "ALBUM", 0.4, confidentComposer, workGuesses);
Chris@37 557 }
Chris@37 558 if (workGuesses.empty() || workGuesses.begin()->confidence() < 0.3) {
Chris@37 559 guessWorkFromFilenameByCatalogue(track, 0.4, confidentComposer, workGuesses);
Chris@37 560 }
Chris@37 561 if (workGuesses.empty()) {
Chris@37 562 guessWorkFromFilenameByTitle(track, 0.3, confidentComposer, workGuesses);
Chris@37 563 }
Chris@33 564
Chris@34 565 cerr << "Work guesses:" << endl;
Chris@34 566 foreach (Guess g, workGuesses) {
Chris@34 567 cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
Chris@34 568 }
Chris@34 569
Chris@34 570 GuessSet consistentComposers;
Chris@34 571 GuessSet consistentWorks;
Chris@34 572 foreach (Guess wg, workGuesses) {
Chris@34 573 Work *w = qobject_cast<Work *>(wg.entity());
Chris@37 574 if (!w || !w->getComposer()) continue;
Chris@37 575 Composer *wc = w->getComposer();
Chris@34 576 foreach (Guess g, guesses) {
Chris@34 577 if (g.entity() == wc) {
Chris@34 578 consistentComposers.insert(g);
Chris@40 579 consistentWorks.insert(wg);
Chris@34 580 }
Chris@34 581 }
Chris@34 582 }
Chris@34 583
Chris@34 584 cerr << "Consistent composer guesses:" << endl;
Chris@34 585 foreach (Guess g, consistentComposers) {
Chris@34 586 cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
Chris@34 587 }
Chris@34 588
Chris@34 589 cerr << "Consistent work guesses:" << endl;
Chris@34 590 foreach (Guess g, consistentWorks) {
Chris@34 591 cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl;
Chris@34 592 }
Chris@34 593
Chris@34 594 float bc3 = bc2;
Chris@34 595 QString best3 = best2;
Chris@34 596 QString work;
Chris@43 597 Work *bestWork = 0;
Chris@37 598 if (!consistentWorks.empty()) {
Chris@37 599 Guess bg = *consistentWorks.begin();
Chris@43 600 bestWork = qobject_cast<Work *>(bg.entity());
Chris@43 601 if (bestWork) {
Chris@37 602 bc3 = bg.confidence();
Chris@43 603 best3 = bestWork->getComposerName();
Chris@43 604 work = bestWork->getDisplayName();
Chris@37 605 }
Chris@34 606 }
Chris@34 607
Chris@39 608 cout << track << "|" << best << "|" << bc << "|" << best2 << "|" << bc2 << "|" << best3 << "|" << bc3 << "|" << work << "|" << bestTitle << endl;
Chris@43 609
Chris@43 610 tf->setOfaFingerprint(fingerprint);
Chris@43 611 tf->setPuid(puid);
Chris@43 612 tf->setComposer(confidentComposer);
Chris@43 613 tf->setWork(bestWork);
Chris@43 614 return tf;
Chris@33 615 }
Chris@32 616
Chris@44 617
Chris@44 618
Chris@32 619 int
Chris@32 620 main(int argc, char **argv)
Chris@32 621 {
Chris@44 622 //!!! N.B. On Windows this will take the profile path over the
Chris@44 623 //!!! home drive path -- we don't want that
Chris@44 624 QString featuresRelPath(".classical-rdf/features");
Chris@44 625 if (!QDir(QDir::home().filePath(featuresRelPath)).exists() &&
Chris@44 626 !QDir::home().mkpath(featuresRelPath)) {
Chris@44 627 std::cerr << "Features directory $HOME/" << featuresRelPath.toStdString()
Chris@44 628 << " does not exist and creation failed" << std::endl;
Chris@44 629 return 2;
Chris@44 630 }
Chris@44 631
Chris@44 632 QCoreApplication::setApplicationName("classical-rdf");
Chris@44 633
Chris@44 634 FeatureFileIndex *ffi = FeatureFileIndex::getInstance();
Chris@44 635
Chris@45 636 QStringList args;
Chris@45 637 for (int i = 1; i < argc; ++i) {
Chris@45 638 args.push_back(argv[i]);
Chris@45 639 }
Chris@45 640
Chris@45 641 BasicStore bs;
Chris@45 642 if (!args.empty()) {
Chris@45 643 foreach (QString track, args) {
Chris@45 644 FileSource fs(track);
Chris@45 645 AudioFile af(fs);
Chris@45 646 ffi->loadFor(&af, &bs);
Chris@45 647 }
Chris@45 648 }
Chris@45 649
Chris@46 650 std::cerr << "Dumping out our local store to local2.ttl" << std::endl;
Chris@46 651 bs.save("local2.ttl");
Chris@44 652
Chris@44 653 /*
Chris@44 654 BasicStore *index = new BasicStore;
Chris@44 655
Chris@44 656 QDir features(QDir::home().filePath(featuresRelPath));
Chris@44 657 features.setFilter(QDir::Files);
Chris@44 658 for (unsigned int i = 0; i < features.count(); ++i) {
Chris@44 659 QFileInfo fi(features.filePath(features[i]));
Chris@44 660 if (fi.isFile() && fi.isReadable()) {
Chris@44 661 std::cout << fi.fileName().toStdString() << std::endl;
Chris@44 662 try {
Chris@44 663 QUrl fileUrl(QUrl::fromLocalFile(fi.filePath()));
Chris@44 664 BasicStore *b = BasicStore::load(fileUrl);
Chris@44 665 Triples ts = b->match
Chris@44 666 (Triple(Node(), "a",
Chris@44 667 Uri("http://purl.org/ontology/mo/AudioFile")));
Chris@44 668 foreach (Triple t, ts) {
Chris@44 669 std::cout << "Audio file: <" << t.a.value.toStdString() << ">" << std::endl;
Chris@44 670 index->add(Triple(Uri(fileUrl), "a",
Chris@44 671 Uri("http://xmlns.com/foaf/0.1/Document")));
Chris@44 672 index->add(Triple(Uri(fileUrl),
Chris@44 673 Uri("http://xmlns.com/foaf/0.1/primaryTopic"),
Chris@44 674 t.a));
Chris@44 675 }
Chris@44 676 } catch (...) { }
Chris@44 677 }
Chris@44 678 }
Chris@44 679
Chris@44 680
Chris@44 681
Chris@44 682 index->save("index.ttl");
Chris@44 683 */
Chris@44 684 /*
Chris@32 685 if (argc < 3) usage(argv[0]);
Chris@32 686 QString inRDFFileName = argv[1];
Chris@32 687 QString command = argv[2];
Chris@32 688 QStringList args;
Chris@32 689 for (int i = 3; i < argc; ++i) {
Chris@32 690 args.push_back(argv[i]);
Chris@32 691 }
Chris@32 692
Chris@34 693 //!!! unit test!
Chris@34 694 int c = Work::compareCatalogueNumberTexts("Op. 1 no 4", "Op. 3 no 2");
Chris@34 695 std::cerr << c << std::endl;
Chris@34 696 c = Work::compareCatalogueNumberTexts("1 no 4", "3 no 2");
Chris@34 697 std::cerr << c << std::endl;
Chris@34 698 c = Work::compareCatalogueNumberTexts("4 no 2", "3 no 2");
Chris@34 699 std::cerr << c << std::endl;
Chris@34 700 c = Work::compareCatalogueNumberTexts("Opus 4 no 2", "3 no 2");
Chris@34 701 std::cerr << c << std::endl;
Chris@34 702 c = Work::compareCatalogueNumberTexts("Op 141", "K. 21");
Chris@34 703 std::cerr << c << std::endl;
Chris@34 704 c = Work::compareCatalogueNumberTexts("Op 14", "K. 21");
Chris@34 705 std::cerr << c << std::endl;
Chris@34 706 c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6");
Chris@34 707 std::cerr << c << std::endl;
Chris@34 708 c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6b");
Chris@34 709 std::cerr << c << std::endl;
Chris@34 710 c = Work::compareCatalogueNumberTexts("Op 6a", "Op 7");
Chris@34 711 std::cerr << c << std::endl;
Chris@34 712 c = Work::compareCatalogueNumberTexts("Hob XXIIId:Es1", "Hob XXII:B04");
Chris@34 713 std::cerr << c << std::endl;
Chris@34 714
Chris@32 715 BasicStore *store = new BasicStore();
Chris@32 716 store->setBaseUri(Uri("http://dbtune.org/classical/resource/"));
Chris@32 717 ObjectLoader *loader = new ObjectLoader(store);
Chris@32 718
Chris@32 719 TypeMapping tm;
Chris@32 720
Chris@32 721 TypeRegistrar::registerTypes();
Chris@32 722 TypeRegistrar::addMappings(store, &tm);
Chris@32 723
Chris@32 724 loader->setTypeMapping(tm);
Chris@32 725
Chris@32 726 if (!load(store, inRDFFileName)) {
Chris@32 727 cerr << "Failed to load data source" << endl;
Chris@32 728 return 1;
Chris@32 729 }
Chris@32 730
Chris@32 731 cerr << "Imported RDF data, mapping to objects...";
Chris@32 732 QObjectList objects = loader->loadAll();
Chris@32 733 cerr << " done" << endl;
Chris@32 734
Chris@32 735 delete loader;
Chris@32 736
Chris@32 737 foreach (QObject *o, objects) {
Chris@32 738 Composer *c = qobject_cast<Composer *>(o);
Chris@33 739 if (c) {
Chris@33 740 allComposers.push_back(c);
Chris@33 741 composerAliases.insert(c->name(), c);
Chris@33 742 foreach (QString alias, c->aliases()) {
Chris@33 743 composerAliases.insert(alias, c);
Chris@33 744 }
Chris@33 745 composerUris.insert(c->property("uri").value<Uri>(), c);
Chris@33 746 foreach (Uri otherUri, c->otherURIs()) {
Chris@33 747 composerUris.insert(otherUri, c);
Chris@33 748 }
Chris@33 749 }
Chris@32 750 }
Chris@32 751
Chris@32 752 QList<Work *> works;
Chris@32 753 foreach (QObject *o, objects) {
Chris@32 754 Work *w = qobject_cast<Work *>(o);
Chris@32 755 if (w) works.push_back(w);
Chris@32 756 }
Chris@32 757
Chris@32 758 foreach (Work *w, works) {
Chris@34 759 allWorks.push_back(w);
Chris@32 760 Composition *c = w->composition();
Chris@32 761 if (c) {
Chris@32 762 Composer *cp = c->composer();
Chris@37 763 if (cp) worksMap[cp].push_back(w);
Chris@32 764 }
Chris@32 765 }
Chris@32 766
Chris@43 767 BasicStore localStore;
Chris@43 768 TypeRegistrar::addMappings(&localStore, &tm);
Chris@43 769
Chris@43 770 ObjectStorer *localStorer = new ObjectStorer(&localStore);
Chris@43 771 localStorer->setTypeMapping(tm);
Chris@43 772 // localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
Chris@43 773
Chris@32 774 if (command == "guess") {
Chris@32 775 if (args.empty()) usage(argv[0]);
Chris@32 776 foreach (QString track, args) {
Chris@45 777 Signal *tf = guess(track);
Chris@43 778 localStorer->store(tf);
Chris@32 779 }
Chris@32 780 }
Chris@43 781
Chris@43 782 delete localStorer;
Chris@43 783 localStore.save("local.ttl");
Chris@32 784
Chris@32 785 delete store;
Chris@44 786 */
Chris@44 787
Chris@33 788 TempDirectory::getInstance()->cleanup();
Chris@32 789 }
Chris@32 790