annotate utilities/track/track.cpp @ 53:bcea875d8d2f tip

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