Chris@32: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@32: Chris@32: #include "Objects.h" Chris@32: #include "TypeRegistrar.h" Chris@32: Chris@32: #include Chris@32: #include Chris@32: #include Chris@32: #include Chris@32: #include Chris@32: #include Chris@32: #include Chris@32: Chris@32: #include "data/fileio/AudioFileReaderFactory.h" Chris@32: #include "data/fileio/AudioFileReader.h" Chris@33: #include "base/TempDirectory.h" Chris@33: Chris@33: #include "Matcher.h" Chris@32: Chris@32: #include Chris@32: Chris@32: #include Chris@32: #include Chris@32: Chris@32: #include Chris@32: Chris@32: using namespace Dataquay; Chris@32: using namespace ClassicalData; Chris@32: using namespace std; Chris@32: using namespace Vamp; Chris@32: using namespace Vamp::HostExt; Chris@32: Chris@52: /* Chris@32: ostream &operator<<(ostream &target, const QString &str) Chris@32: { Chris@32: return target << str.toLocal8Bit().data(); Chris@32: } Chris@32: Chris@32: ostream &operator<<(ostream &target, const QUrl &u) Chris@32: { Chris@32: return target << "<" << u.toString() << ">"; Chris@32: } Chris@52: */ Chris@32: Chris@32: bool Chris@32: load(BasicStore *store, QString fileName) Chris@32: { Chris@32: QUrl url = QUrl::fromLocalFile(fileName); Chris@32: Chris@32: cerr << "Importing from URL " << url << " ..."; Chris@32: try { Chris@32: store->import(url, BasicStore::ImportPermitDuplicates); Chris@32: } catch (RDFException e) { Chris@32: cerr << " retrying with explicit ntriples type..."; Chris@32: try { Chris@32: store->import(url, BasicStore::ImportPermitDuplicates, "ntriples"); Chris@32: } catch (RDFException e) { Chris@32: cerr << "failed" << endl; Chris@32: cerr << "Import failed: " << e.what() << endl; Chris@32: return false; Chris@32: } Chris@32: } Chris@32: Chris@32: cerr << " done" << endl; Chris@32: return true; Chris@32: } Chris@32: Chris@32: void Chris@32: usage(char *name) Chris@32: { Chris@32: int s = 0; Chris@32: for (int i = 0; name[i]; ++i) if (name[i] == '/') s = i + 1; Chris@32: name = name + s; Chris@32: cerr << "Usage:" << endl; Chris@32: cerr << " " << name << " guess [ ...]" << endl; Chris@32: exit(-1); Chris@32: } Chris@32: Chris@32: static QList allComposers; Chris@33: static QHash composerAliases; Chris@33: static QHash composerUris; Chris@37: static QMap > worksMap; Chris@34: static QList allWorks; Chris@32: Chris@32: void Chris@32: show(Composer *c) Chris@32: { Chris@32: cout << c->property("uri").value() << endl; Chris@32: cout << c->getSortName(true); Chris@32: QString d = c->getDisplayDates(); Chris@32: if (d != "") cout << " (" << d << ")"; Chris@32: if (!c->nationality().empty() || c->period() != "") { Chris@32: cout << " ["; Chris@32: bool first = true; Chris@32: foreach (QString n, c->nationality()) { Chris@32: if (!first) cout << "/"; Chris@32: cout << n; Chris@32: first = false; Chris@32: } Chris@32: if (c->period() != "") { Chris@32: if (!first) cout << ", "; Chris@32: cout << c->period(); Chris@32: } Chris@32: cout << "]"; Chris@32: } Chris@32: if (c->gender() != "") { Chris@32: cout << " *" << c->gender(); Chris@32: } Chris@32: if (!worksMap[c].empty()) { Chris@32: cout << " [" << worksMap[c].size() << " work(s)]"; Chris@32: } Chris@32: cout << endl; Chris@32: foreach (QString a, c->aliases()) { Chris@32: cout << " - " << a << endl; Chris@32: } Chris@32: if (c->remarks() != "") { Chris@32: cout << " " << c->remarks() << endl; Chris@32: } Chris@32: foreach (Document *d, c->pages()) { Chris@32: cout << d->siteName() << " -> " << d->uri() << endl; Chris@32: } Chris@32: foreach (Uri u, c->otherURIs()) { Chris@32: cout << "Same as " << u << endl; Chris@32: } Chris@32: } Chris@32: Chris@32: void Chris@32: showBrief(Composer *c) Chris@32: { Chris@32: cout << c->property("uri").value() << endl; Chris@32: cout << c->getSortName(false); Chris@32: QString d = c->getDisplayDates(); Chris@32: if (d != "") cout << " (" << d << ")"; Chris@32: if (!c->nationality().empty() || c->period() != "") { Chris@32: cout << " ["; Chris@32: bool first = true; Chris@32: foreach (QString n, c->nationality()) { Chris@32: if (!first) cout << "/"; Chris@32: cout << n; Chris@32: first = false; Chris@32: } Chris@32: if (c->period() != "") { Chris@32: if (!first) cout << " "; Chris@32: cout << c->period(); Chris@32: } Chris@32: cout << "]"; Chris@32: } Chris@32: if (c->gender() != "") { Chris@32: cout << " *" << c->gender(); Chris@32: } Chris@32: if (!worksMap[c].empty()) { Chris@32: cout << " [" << worksMap[c].size() << " work(s)]"; Chris@32: } Chris@32: cout << endl; Chris@32: } Chris@32: Chris@32: void Chris@32: listBrief(QList composers) Chris@32: { Chris@32: QMultiMap sorted; Chris@32: foreach (Composer *c, composers) { Chris@32: sorted.insert(c->getSortName(false), c); Chris@32: } Chris@32: foreach (Composer *c, sorted) { Chris@32: showBrief(c); Chris@32: } Chris@32: } Chris@32: Chris@32: void Chris@32: listUris(QList composers) Chris@32: { Chris@32: QMultiMap sorted; Chris@32: foreach (Composer *c, composers) { Chris@32: sorted.insert(c->property("uri").value(), c); Chris@32: } Chris@32: foreach (Uri uri, sorted.keys()) { Chris@32: cout << uri << endl; Chris@32: } Chris@32: } Chris@32: Chris@32: void Chris@32: showSearchResults(QMultiMap matches, int count) Chris@32: { Chris@32: int n = 0; Chris@32: for (QMultiMap::const_iterator i = matches.end(); Chris@32: i != matches.begin(); ) { Chris@32: --i; Chris@32: if (i.key() <= 0) continue; Chris@32: cout << endl; Chris@32: if (n == 0) { Chris@32: cout << "Best match:" << endl; Chris@32: } else if (n == 1) { Chris@32: cout << "Other candidate(s):" << endl; Chris@32: } Chris@32: cout << "[" << i.key() << "] "; Chris@32: if (n == 0) show(i.value()); Chris@32: else showBrief(i.value()); Chris@32: if (++n > count) break; Chris@32: } Chris@32: if (n == 0) cout << "No matches" << endl; Chris@32: cout << endl; Chris@32: } Chris@32: Chris@32: void Chris@33: getTrackData(FileSource source, QString &fingerprint, QString &puid, Chris@33: QString &title, QString &maker, AudioFileReader::TagMap &tags) Chris@32: { Chris@43: AudioFileReader *reader = AudioFileReaderFactory::createReader(source); Chris@43: // AudioFileReader *reader = AudioFileReaderFactory::createThreadingReader(source); Chris@32: if (!reader || !reader->isOK()) { Chris@32: cerr << "Failed to open audio file" << endl; Chris@32: return; Chris@32: } Chris@32: Chris@33: title = reader->getTitle(); Chris@33: maker = reader->getMaker(); Chris@34: // cout << "File tag title: " << reader->getTitle() << endl; Chris@34: // cout << "File tag maker: " << reader->getMaker() << endl; Chris@32: Chris@32: cout << "All tags:" << endl; Chris@33: tags = reader->getTags(); Chris@32: for (AudioFileReader::TagMap::const_iterator i = tags.begin(); Chris@32: i != tags.end(); ++i) { Chris@32: cout << i->first << " " << i->second << endl; Chris@32: } Chris@32: Chris@32: PluginLoader *pl = PluginLoader::getInstance(); Chris@32: Plugin *plugin = pl->loadPlugin Chris@32: ("ofa-vamp-plugin:ofa_puid_and_fingerprint", reader->getSampleRate(), PluginLoader::ADAPT_ALL); Chris@32: if (!plugin) { Chris@32: cerr << "Failed to load OFA Vamp plugin" << endl; Chris@32: return; Chris@32: } Chris@32: Chris@32: // 135 seconds... well, ok, let's have 136 Chris@32: int secs = 136; Chris@32: Chris@32: int want = int(secs * reader->getSampleRate()); Chris@32: int ch = reader->getChannelCount(); Chris@32: std::vector samples; Chris@32: reader->getDeInterleavedFrames(0, want, samples); Chris@32: int have = samples[0].size(); Chris@32: if (!plugin->initialise(ch, have, have)) { Chris@32: cerr << "Failed to initialise(" << ch << "," << have << "," << have << ") plugin" << endl; Chris@32: return; Chris@32: } Chris@32: Chris@32: float **input = new float *[ch]; Chris@32: for (int i = 0; i < ch; ++i) { Chris@32: input[i] = &samples[i][0]; Chris@32: } Chris@32: Plugin::FeatureSet features = plugin->process(input, RealTime::zeroTime); Chris@33: if (!features[0].empty()) { Chris@33: fingerprint = QString::fromStdString(features[0][0].label); Chris@33: } Chris@33: if (!features[1].empty()) { Chris@33: puid = QString::fromStdString(features[1][0].label); Chris@33: } Chris@32: features = plugin->getRemainingFeatures(); Chris@33: if (fingerprint == "" && !features[0].empty()) { Chris@33: fingerprint = QString::fromStdString(features[0][0].label); Chris@33: } Chris@33: if (puid == "" && !features[1].empty()) { Chris@33: puid = QString::fromStdString(features[1][0].label); Chris@33: } Chris@34: delete[] input; Chris@34: delete plugin; Chris@34: delete reader; Chris@34: } Chris@34: Chris@34: float Chris@34: bonusFactor(NamedEntity *e) Chris@34: { Chris@34: // tiny nudge to prefer composers we actually have works for Chris@34: Composer *c = qobject_cast(e); Chris@34: float f = 1.f; Chris@34: int sz = 0; Chris@34: if (c && worksMap.contains(c)) { Chris@34: sz = worksMap[c].size(); Chris@34: while (sz > 0) { Chris@34: f += 0.01; Chris@34: sz = sz / 10; Chris@34: } Chris@34: } Chris@34: return f; Chris@34: } Chris@34: Chris@34: void Chris@34: integrateGuesses(GuessSet &guesses, GuessSet newGuesses) Chris@34: { Chris@34: QHash ecmap; Chris@34: foreach (Guess g, guesses) { Chris@34: ecmap[g.entity()] += g.confidence() * bonusFactor(g.entity()); Chris@34: } Chris@34: foreach (Guess g, newGuesses) { Chris@34: if (ecmap.contains(g.entity())) { Chris@34: ecmap[g.entity()] += g.confidence() / 2; Chris@34: } else { Chris@34: ecmap[g.entity()] = g.confidence(); Chris@34: } Chris@34: } Chris@34: guesses.clear(); Chris@34: foreach (NamedEntity *e, ecmap.keys()) { Chris@34: guesses.insert(Guess(ecmap[e], e)); Chris@34: } Chris@34: } Chris@34: Chris@34: void Chris@34: guessFromMaker(QString maker, float scale, GuessSet &guesses) Chris@34: { Chris@34: if (maker == "") return; Chris@34: // cerr << "guessFromMaker: " << maker << endl; Chris@34: GuessSet myGuesses; Chris@34: if (composerAliases.contains(maker)) { Chris@34: QList matching = composerAliases.values(maker); Chris@34: foreach (Composer *c, matching) { Chris@34: myGuesses.insert(Guess(10 * scale, c)); Chris@34: } Chris@34: } else { Chris@34: ComposerFullTextMatcher matcher(allComposers); Chris@34: GuessList gl(matcher.match(maker, 5, 0.5)); Chris@34: if (!gl.empty()) { Chris@34: foreach (Guess guess, gl) { Chris@34: myGuesses.insert(Guess(guess.confidence() * scale, guess.entity())); Chris@34: } Chris@34: } Chris@34: } Chris@34: integrateGuesses(guesses, myGuesses); Chris@34: } Chris@34: Chris@34: void Chris@34: guessFromMakerTag(AudioFileReader::TagMap tags, QString tag, float scale, GuessSet &guesses) Chris@34: { Chris@34: if (tags.find(tag) != tags.end()) { Chris@34: guessFromMaker(tags[tag], scale, guesses); Chris@34: } Chris@34: } Chris@34: Chris@34: void Chris@34: guessFromTitle(QString title, float scale, GuessSet &guesses) Chris@34: { Chris@34: QStringList bits = title.split(QRegExp("[:,_-]"), Chris@34: QString::SkipEmptyParts); Chris@34: if (bits.size() > 1) { Chris@34: guessFromMaker(bits.first(), scale, guesses); Chris@34: } Chris@34: } Chris@34: Chris@34: void Chris@34: guessFromTitleTag(AudioFileReader::TagMap tags, QString tag, float scale, GuessSet &guesses) Chris@34: { Chris@34: if (tags.find(tag) != tags.end()) { Chris@34: guessFromTitle(tags[tag], scale, guesses); Chris@34: } Chris@34: } Chris@34: Chris@34: void Chris@34: guessFromFilename(QString filename, float scale, GuessSet &guesses) Chris@34: { Chris@34: cerr << "guessFromFilename: " << filename << endl; Chris@37: QString dirpart = QFileInfo(filename).path(); Chris@34: QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts); Chris@37: dirbits = dirbits.last() Chris@37: .replace(QRegExp("^\\d+"), "") Chris@37: .split(QRegExp("[^\\w]"), QString::SkipEmptyParts); Chris@34: if (!dirbits.empty()) { Chris@34: guessFromMaker(dirbits.first(), scale, guesses); Chris@34: } Chris@34: Chris@34: QString filepart = QFileInfo(filename).fileName().replace(QRegExp("\\d+"), ""); Chris@34: QStringList filebits = filepart.split(QRegExp("[^\\w]"), Chris@34: QString::SkipEmptyParts); Chris@34: if (!filebits.empty()) { Chris@34: guessFromMaker(filebits.first(), scale, guesses); Chris@34: } Chris@34: } Chris@34: Chris@37: void Chris@37: guessWorkFromTitleByCatalogue(QString title, float scale, Chris@37: Composer *composer, GuessSet &guesses) Chris@34: { Chris@34: if (title == "") return; Chris@37: WorkCatalogueMatcher matcher(composer ? worksMap.value(composer) : allWorks); Chris@37: GuessList gl(matcher.match(title, 0)); Chris@37: if (!gl.empty()) { Chris@37: foreach (Guess guess, gl) { Chris@37: guesses.insert(Guess(guess.confidence() * scale, guess.entity())); Chris@34: } Chris@34: } Chris@37: } Chris@37: Chris@37: void Chris@37: guessWorkFromTitle(QString title, float scale, Chris@37: Composer *composer, GuessSet &guesses) Chris@37: { Chris@37: if (title == "") return; Chris@37: WorkTitleMatcher matcher(composer ? worksMap.value(composer) : allWorks); Chris@37: GuessList gl(matcher.match(title, 0)); Chris@37: if (!gl.empty()) { Chris@37: foreach (Guess guess, gl) { Chris@37: guesses.insert(Guess(guess.confidence() * scale, guess.entity())); Chris@35: } Chris@35: } Chris@34: } Chris@34: Chris@34: void Chris@37: guessWorkFromTitleTag(AudioFileReader::TagMap tags, QString tag, float scale, Chris@37: Composer *composer, GuessSet &guesses) Chris@34: { Chris@40: cerr << "guessWorkFromTitleTag: " << tag << endl; Chris@40: Chris@34: if (tags.find(tag) != tags.end()) { Chris@40: cerr << "guessWorkFromTitleTag: tag is " << tags[tag] << endl; Chris@37: GuessSet myGuesses; Chris@37: guessWorkFromTitle(tags[tag], scale, composer, myGuesses); Chris@37: integrateGuesses(guesses, myGuesses); Chris@37: myGuesses.clear(); Chris@37: guessWorkFromTitle(tags[tag], scale, composer, myGuesses); Chris@37: integrateGuesses(guesses, myGuesses); Chris@34: } Chris@32: } Chris@32: Chris@33: void Chris@37: guessWorkFromFilenameByCatalogue(QString filename, float scale, Chris@37: Composer *composer, GuessSet &guesses) Chris@35: { Chris@35: cerr << "guessWorkFromFilename: " << filename << endl; Chris@37: Chris@37: QString dirpart = QFileInfo(filename).path(); Chris@37: QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts); Chris@37: if (!dirbits.empty()) { Chris@37: guessWorkFromTitleByCatalogue(dirbits.last(), scale * 0.7, composer, guesses); Chris@37: } Chris@37: Chris@37: QString filepart = QFileInfo(filename).fileName().replace Chris@37: (QRegExp("\\.[^\\.]*"), "").replace(QRegExp("^\\d+[^\\w]+"), ""); Chris@37: guessWorkFromTitleByCatalogue(filepart, scale, composer, guesses); Chris@37: } Chris@37: Chris@37: void Chris@37: guessWorkFromFilenameByTitle(QString filename, float scale, Chris@37: Composer *composer, GuessSet &guesses) Chris@37: { Chris@37: cerr << "guessWorkFromFilename: " << filename << endl; Chris@37: Chris@37: QString dirpart = QFileInfo(filename).path(); Chris@37: QStringList dirbits = dirpart.split("/", QString::SkipEmptyParts); Chris@37: if (!dirbits.empty()) { Chris@37: guessWorkFromTitle(dirbits.last(), scale * 0.7, composer, guesses); Chris@37: } Chris@37: Chris@37: QString filepart = QFileInfo(filename).fileName().replace Chris@37: (QRegExp("\\.[^\\.]*"), "").replace(QRegExp("^\\d+[^\\w]+"), ""); Chris@37: guessWorkFromTitle(filepart, scale, composer, guesses); Chris@35: } Chris@35: Chris@45: Signal * Chris@33: guess(QString track) Chris@33: { Chris@34: cout << endl; Chris@33: cout << "Guessing composer for: " << track << endl; Chris@33: Chris@45: // cerr << "Creating Signal..."; Chris@33: FileSource fs(track); Chris@45: Signal *tf = new Signal; Chris@48: AudioFile *af = new AudioFile(fs); Chris@48: tf->addAvailableAs(af); Chris@34: // cerr << "done" << endl; Chris@34: // cerr << "hash = " << tf->hash() << endl; Chris@33: Chris@33: QString fingerprint, puid, maker, title; Chris@33: AudioFileReader::TagMap tags; Chris@33: //!!! bad api!: Chris@33: getTrackData(fs, fingerprint, puid, title, maker, tags); Chris@33: Chris@48: for (AudioFileReader::TagMap::const_iterator i = tags.begin(); Chris@48: i != tags.end(); ++i) { Chris@48: if (i->second != "") { Chris@48: af->addTag(new AudioFileTag(i->first, i->second)); Chris@48: } Chris@48: } Chris@48: Chris@43: cout << "fingerprint: " << fingerprint.toStdString() << ", puid: " Chris@43: << puid.toStdString() << endl; Chris@43: Chris@34: GuessSet guesses; Chris@34: Chris@34: guessFromMakerTag(tags, "TCOM", 1.0, guesses); Chris@34: guessFromMakerTag(tags, "COMPOSER", 1.0, guesses); Chris@34: Chris@34: if (guesses.empty() || guesses.begin()->confidence() < 0.4) { Chris@34: guessFromMakerTag(tags, "TOPE", 0.8, guesses); Chris@34: guessFromMakerTag(tags, "TPE1", 0.8, guesses); Chris@34: Chris@34: guessFromMakerTag(tags, "ARTIST", 0.9, guesses); Chris@34: guessFromMakerTag(tags, "PERFORMER", 0.8, guesses); Chris@34: Chris@34: guessFromTitleTag(tags, "TIT1", 0.4, guesses); Chris@34: guessFromTitleTag(tags, "TIT2", 0.5, guesses); Chris@34: guessFromTitleTag(tags, "TALB", 0.5, guesses); Chris@34: guessFromTitleTag(tags, "TIT3", 0.3, guesses); Chris@34: Chris@34: guessFromTitleTag(tags, "TITLE", 0.5, guesses); Chris@34: guessFromTitleTag(tags, "ALBUM", 0.5, guesses); Chris@33: } Chris@33: Chris@33: if (tags.find("MUSICBRAINZ_ARTISTID") != tags.end()) { Chris@33: QString id = tags["MUSICBRAINZ_ARTISTID"]; Chris@33: Uri mbzUri = Uri("http://dbtune.org/musicbrainz/resource/artist/" + id); Chris@33: cout << "MBZ id found: " << id << endl; Chris@33: if (composerUris.contains(mbzUri)) { Chris@34: guesses.insert(Guess(2.0, composerUris[mbzUri])); Chris@33: } Chris@33: } Chris@33: Chris@34: cerr << "Composer guesses:" << endl; Chris@34: foreach (Guess g, guesses) { Chris@34: cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl; Chris@34: } Chris@34: Chris@34: float bc = 0.f; Chris@34: QString best; Chris@34: if (!guesses.empty()) { Chris@34: Guess bg = *guesses.begin(); Chris@34: best = bg.entity()->name(); Chris@34: bc = bg.confidence(); Chris@34: } Chris@34: Chris@34: guessFromFilename(track, 0.5, guesses); Chris@34: Chris@34: float bc2 = 0.f; Chris@34: QString best2; Chris@34: if (!guesses.empty()) { Chris@34: Guess bg = *guesses.begin(); Chris@34: best2 = bg.entity()->name(); Chris@34: bc2 = bg.confidence(); Chris@34: } Chris@34: Chris@37: // If we have only one confident composer guess, consider only Chris@37: // works from that composer (really this should permit considering Chris@37: // works from all confident composers) Chris@37: Composer *confidentComposer = 0; Chris@37: if (bc2 > 0.5) { Chris@37: confidentComposer = qobject_cast(guesses.begin()->entity()); Chris@37: } Chris@37: Chris@39: QString bestTitle; Chris@39: Chris@34: GuessSet workGuesses; Chris@40: if (tags["TIT2"] != "") { Chris@40: bestTitle = tags["TIT2"]; Chris@40: guessWorkFromTitleTag(tags, "TIT2", 0.5, confidentComposer, workGuesses); Chris@40: } Chris@37: if (tags["TITLE"] != "") { Chris@39: bestTitle = tags["TITLE"]; Chris@37: guessWorkFromTitleTag(tags, "TITLE", 0.5, confidentComposer, workGuesses); Chris@37: } Chris@37: if (workGuesses.empty()) { Chris@37: guessWorkFromTitleTag(tags, "TIT1", 0.2, confidentComposer, workGuesses); Chris@37: guessWorkFromTitleTag(tags, "TALB", 0.2, confidentComposer, workGuesses); Chris@37: guessWorkFromTitleTag(tags, "TIT3", 0.1, confidentComposer, workGuesses); Chris@37: guessWorkFromTitleTag(tags, "ALBUM", 0.4, confidentComposer, workGuesses); Chris@37: } Chris@37: if (workGuesses.empty() || workGuesses.begin()->confidence() < 0.3) { Chris@37: guessWorkFromFilenameByCatalogue(track, 0.4, confidentComposer, workGuesses); Chris@37: } Chris@37: if (workGuesses.empty()) { Chris@37: guessWorkFromFilenameByTitle(track, 0.3, confidentComposer, workGuesses); Chris@37: } Chris@33: Chris@34: cerr << "Work guesses:" << endl; Chris@34: foreach (Guess g, workGuesses) { Chris@34: cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl; Chris@34: } Chris@34: Chris@34: GuessSet consistentComposers; Chris@34: GuessSet consistentWorks; Chris@34: foreach (Guess wg, workGuesses) { Chris@34: Work *w = qobject_cast(wg.entity()); Chris@37: if (!w || !w->getComposer()) continue; Chris@37: Composer *wc = w->getComposer(); Chris@34: foreach (Guess g, guesses) { Chris@34: if (g.entity() == wc) { Chris@34: consistentComposers.insert(g); Chris@40: consistentWorks.insert(wg); Chris@34: } Chris@34: } Chris@34: } Chris@34: Chris@34: cerr << "Consistent composer guesses:" << endl; Chris@34: foreach (Guess g, consistentComposers) { Chris@34: cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl; Chris@34: } Chris@34: Chris@34: cerr << "Consistent work guesses:" << endl; Chris@34: foreach (Guess g, consistentWorks) { Chris@34: cerr << "[" << g.confidence() << "] " << g.entity()->uri() << endl; Chris@34: } Chris@34: Chris@34: float bc3 = bc2; Chris@34: QString best3 = best2; Chris@34: QString work; Chris@43: Work *bestWork = 0; Chris@37: if (!consistentWorks.empty()) { Chris@37: Guess bg = *consistentWorks.begin(); Chris@43: bestWork = qobject_cast(bg.entity()); Chris@43: if (bestWork) { Chris@37: bc3 = bg.confidence(); Chris@43: best3 = bestWork->getComposerName(); Chris@43: work = bestWork->getDisplayName(); Chris@37: } Chris@34: } Chris@34: Chris@39: cout << track << "|" << best << "|" << bc << "|" << best2 << "|" << bc2 << "|" << best3 << "|" << bc3 << "|" << work << "|" << bestTitle << endl; Chris@43: Chris@43: tf->setOfaFingerprint(fingerprint); Chris@43: tf->setPuid(puid); Chris@43: tf->setComposer(confidentComposer); Chris@43: tf->setWork(bestWork); Chris@43: return tf; Chris@33: } Chris@32: Chris@32: int Chris@32: main(int argc, char **argv) Chris@32: { Chris@32: if (argc < 3) usage(argv[0]); Chris@32: QString inRDFFileName = argv[1]; Chris@32: QString command = argv[2]; Chris@32: QStringList args; Chris@32: for (int i = 3; i < argc; ++i) { Chris@32: args.push_back(argv[i]); Chris@32: } Chris@32: Chris@34: //!!! unit test! Chris@34: int c = Work::compareCatalogueNumberTexts("Op. 1 no 4", "Op. 3 no 2"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("1 no 4", "3 no 2"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("4 no 2", "3 no 2"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Opus 4 no 2", "3 no 2"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Op 141", "K. 21"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Op 14", "K. 21"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Op 6a", "Op 6b"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Op 6a", "Op 7"); Chris@34: std::cerr << c << std::endl; Chris@34: c = Work::compareCatalogueNumberTexts("Hob XXIIId:Es1", "Hob XXII:B04"); Chris@34: std::cerr << c << std::endl; Chris@34: Chris@32: BasicStore *store = new BasicStore(); Chris@32: store->setBaseUri(Uri("http://dbtune.org/classical/resource/")); Chris@32: ObjectLoader *loader = new ObjectLoader(store); Chris@32: Chris@32: TypeMapping tm; Chris@32: Chris@32: TypeRegistrar::registerTypes(); Chris@32: TypeRegistrar::addMappings(store, &tm); Chris@32: Chris@32: loader->setTypeMapping(tm); Chris@32: Chris@32: if (!load(store, inRDFFileName)) { Chris@32: cerr << "Failed to load data source" << endl; Chris@32: return 1; Chris@32: } Chris@32: Chris@32: cerr << "Imported RDF data, mapping to objects..."; Chris@32: QObjectList objects = loader->loadAll(); Chris@32: cerr << " done" << endl; Chris@32: Chris@32: delete loader; Chris@32: Chris@32: foreach (QObject *o, objects) { Chris@32: Composer *c = qobject_cast(o); Chris@33: if (c) { Chris@33: allComposers.push_back(c); Chris@33: composerAliases.insert(c->name(), c); Chris@33: foreach (QString alias, c->aliases()) { Chris@33: composerAliases.insert(alias, c); Chris@33: } Chris@33: composerUris.insert(c->property("uri").value(), c); Chris@33: foreach (Uri otherUri, c->otherURIs()) { Chris@33: composerUris.insert(otherUri, c); Chris@33: } Chris@33: } Chris@32: } Chris@32: Chris@32: QList works; Chris@32: foreach (QObject *o, objects) { Chris@32: Work *w = qobject_cast(o); Chris@32: if (w) works.push_back(w); Chris@32: } Chris@32: Chris@32: foreach (Work *w, works) { Chris@34: allWorks.push_back(w); Chris@32: Composition *c = w->composition(); Chris@32: if (c) { Chris@32: Composer *cp = c->composer(); Chris@37: if (cp) worksMap[cp].push_back(w); Chris@32: } Chris@32: } Chris@32: Chris@43: BasicStore localStore; Chris@52: localStore.setBaseUri(store->getBaseUri()); Chris@43: TypeRegistrar::addMappings(&localStore, &tm); Chris@43: Chris@43: ObjectStorer *localStorer = new ObjectStorer(&localStore); Chris@43: localStorer->setTypeMapping(tm); Chris@45: localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties); Chris@45: localStorer->setPropertyStorePolicy(ObjectStorer::StoreIfChanged); Chris@43: Chris@32: if (command == "guess") { Chris@32: if (args.empty()) usage(argv[0]); Chris@32: foreach (QString track, args) { Chris@45: Signal *tf = guess(track); Chris@43: localStorer->store(tf); Chris@32: } Chris@45: } else { Chris@45: usage(argv[0]); Chris@45: } Chris@43: Chris@43: delete localStorer; Chris@43: localStore.save("local.ttl"); Chris@32: Chris@32: delete store; Chris@33: TempDirectory::getInstance()->cleanup(); Chris@32: } Chris@32: