changeset 45:0033259c6772

* More Music Ontology-like structure for audio files & signals
author Chris Cannam
date Fri, 14 May 2010 17:58:04 +0100
parents ed2befdf1e98
children c8b777862198
files common/FeatureFileIndex.cpp common/FeatureFileIndex.h common/Objects.cpp common/Objects.h common/TypeRegistrar.cpp utilities/the-application/the-application.cpp utilities/track/track.cpp
diffstat 7 files changed, 174 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/common/FeatureFileIndex.cpp	Mon May 10 17:33:00 2010 +0100
+++ b/common/FeatureFileIndex.cpp	Fri May 14 17:58:04 2010 +0100
@@ -22,6 +22,7 @@
 }
 
 FeatureFileIndex::FeatureFileIndex() :
+    m_bs(0),
     m_index(0)
 {
     try {
@@ -31,19 +32,22 @@
         return;
     }
 
-    m_index = new BasicStore;
+    m_bs = new BasicStore;
+    m_index = new TransactionalStore(m_bs);
 
-    TypeRegistrar::addMappings(m_index, 0);
+    TypeRegistrar::addMappings(m_bs, 0);
 
     if (QFile(m_indexFileName).exists()) {
-	m_index->import(QUrl::fromLocalFile(m_indexFileName),
-			BasicStore::ImportIgnoreDuplicates);
+	m_bs->import(QUrl::fromLocalFile(m_indexFileName),
+                     BasicStore::ImportIgnoreDuplicates);
 	//!!! catch
     }
 }
 
 FeatureFileIndex::~FeatureFileIndex()
 {
+    delete m_index;
+    delete m_bs;
 }
 
 QString
@@ -77,7 +81,7 @@
 }
 
 void
-FeatureFileIndex::loadFor(TrackFile *tf, BasicStore *store)
+FeatureFileIndex::loadFor(AudioFile *tf, Store *store)
 {
     if (!m_index) {
 	std::cerr << "FeatureFileIndex::loadFor: No index!" << std::endl;
@@ -85,10 +89,37 @@
     }
     updateIndex();
 
-    //...
+    // The AudioFile object has a URI and a hash.  Feature files
+    // generated for this AudioFile should ideally have a matching
+    // hash; if they have no hash, then the URI should match.  If the
+    // hash is present in the feature file but does not match, then it
+    // cannot be the right track even if the URI matches.
+
+    Triple t(Node(), "foaf:primaryTopic", tf->uri());
+    Triples results = m_index->match(t);
+    std::cerr << "FeatureFileIndex::loadFor: " << results.size() << " feature file(s) for audio file " << tf->uri() << std::endl;
+
+    t = Triple(Node(), "foaf:primaryTopic",
+               Uri(QString::fromUtf8(QUrl(tf->uri().toString()).toEncoded())));
+    Triples moreResults = m_index->match(t);
+    std::cerr << "FeatureFileIndex::loadFor: " << moreResults.size() << " feature file(s) for audio file " << t.c << std::endl;
+
+    //!!! what's the right approach here?
+
+    if (results.empty() && moreResults.empty()) {
+        return;
+    }
+
+    
 }
 
 void
+FeatureFileIndex::featureFileAdded(QString filepath)
+{
+    index(QUrl::fromLocalFile(filepath));
+}    
+
+void
 FeatureFileIndex::updateIndex()
 {
     QMutexLocker locker(&m_mutex);
@@ -106,27 +137,40 @@
     featureDir.setFilter(QDir::Files);
 
     for (unsigned int i = 0; i < featureDir.count(); ++i) {
-
         QFileInfo fi(featureDir.filePath(featureDir[i]));
-
         if (fi.isFile() && fi.isReadable()) {
-	    QUrl fileUrl(QUrl::fromLocalFile(fi.filePath()));
-            try {
-                BasicStore *b = BasicStore::load(fileUrl);
-                Triples ts = b->match
-                    (Triple(Node(), "a", m_index->expand("mo:AudioFile")));
-                foreach (Triple t, ts) {
-                    m_index->add(Triple(Uri(fileUrl), "a", m_index->expand("foaf:Document")));
-                    m_index->add(Triple(Uri(fileUrl), "foaf:primaryTopic", t.a));;
-                }
-            } catch (...) { }
+            index(QUrl::fromLocalFile(fi.filePath()));
         }
     }
 
     //!!! remove triples from index that refer to nonexistent files?
 
     std::cerr << "Saving index to " << m_indexFileName.toStdString() << std::endl;
-    m_index->save(m_indexFileName);
+    m_bs->save(m_indexFileName);
+}
+
+void
+FeatureFileIndex::index(QUrl fileUrl)
+{
+    Triple typeTriple(Uri(fileUrl), "a", m_index->expand("foaf:Document"));
+
+    if (m_index->contains(typeTriple)) {
+        return;
+    }
+
+    Transaction *tx = m_index->startTransaction();
+    tx->add(typeTriple);
+
+    try {
+        BasicStore *b = BasicStore::load(fileUrl);
+        Triples ts = b->match
+            (Triple(Node(), "a", m_index->expand("mo:AudioFile")));
+        foreach (Triple t, ts) {
+            tx->add(Triple(Uri(fileUrl), "foaf:primaryTopic", t.a));;
+        }
+    } catch (...) { }
+
+    delete tx;
 }
 
 
--- a/common/FeatureFileIndex.h	Mon May 10 17:33:00 2010 +0100
+++ b/common/FeatureFileIndex.h	Fri May 14 17:58:04 2010 +0100
@@ -6,28 +6,36 @@
 #include "Objects.h"
 
 #include <dataquay/BasicStore.h>
+#include <dataquay/TransactionalStore.h>
 
 #include <QMutex>
 
 namespace ClassicalData {
 
-class FeatureFileIndex
+class FeatureFileIndex : public QObject
 {
+    Q_OBJECT
+
 public:
     static FeatureFileIndex *getInstance();
     
     FeatureFileIndex();
     ~FeatureFileIndex();
 
-    void loadFor(TrackFile *, Dataquay::BasicStore *);
+    void loadFor(AudioFile *, Dataquay::Store *);
+
+public slots:
+    void featureFileAdded(QString filepath);
 
 private:
     QMutex m_mutex;
     QString m_indexFileName;
-    Dataquay::BasicStore *m_index;
+    Dataquay::BasicStore *m_bs;
+    Dataquay::TransactionalStore *m_index;
     QString getIndexFileName();
     QString getFeatureDirectoryName();
     void updateIndex();
+    void index(QUrl);
 };
 
 }
--- a/common/Objects.cpp	Mon May 10 17:33:00 2010 +0100
+++ b/common/Objects.cpp	Fri May 14 17:58:04 2010 +0100
@@ -825,24 +825,18 @@
     }
 }
 
-TrackFile::TrackFile(QObject *parent) :
-    QObject(parent),
-    m_composer(0),
-    m_work(0),
-    m_movement(0)
+AudioFile::AudioFile(QObject *parent) :
+    QObject(parent)
 {
 }
 
-TrackFile::TrackFile(FileSource source, QObject *parent) :
-    QObject(parent),
-    m_composer(0),
-    m_work(0),
-    m_movement(0)
+AudioFile::AudioFile(FileSource source, QObject *parent) :
+    QObject(parent)
 {
     if (source.isAvailable()) {
         QFile f(source.getLocalFilename());
         f.open(QIODevice::ReadOnly);
-        //!!! data may be too large!
+        //!!! stream this!
         QByteArray ba = f.readAll();
         m_hash = QString::fromAscii
             (QCryptographicHash::hash(ba, QCryptographicHash::Sha1).toHex());
@@ -859,7 +853,8 @@
             m_uri = Dataquay::Uri("file://" + QFileInfo(location).canonicalFilePath());
         }
     }
-    std::cerr << "TrackFile::TrackFile: hash = " << m_hash.toStdString()
+
+    std::cerr << "AudioFile::AudioFile: hash = " << m_hash.toStdString()
               << ", uri = " << m_uri.toString().toStdString() << std::endl;
 }
 
--- a/common/Objects.h	Mon May 10 17:33:00 2010 +0100
+++ b/common/Objects.h	Fri May 14 17:58:04 2010 +0100
@@ -580,30 +580,49 @@
     static QMutex m_mutex;
 };
 
-class TrackFile : public QObject
+// Separate AudioFile and Signal, to correspond with 
+
+class AudioFile : public QObject
 {
     Q_OBJECT
 
     Q_PROPERTY(QString hash READ hash WRITE setHash NOTIFY hashChanged STORED true)
     Q_PROPERTY(Dataquay::Uri uri READ uri WRITE setUri NOTIFY uriChanged STORED true)
-    Q_PROPERTY(QString ofaFingerprint READ ofaFingerprint WRITE setOfaFingerprint NOTIFY ofaFingerprintChanged STORED true)
-    Q_PROPERTY(QString puid READ puid WRITE setPuid NOTIFY puidChanged STORED true)
-    Q_PROPERTY(ClassicalData::Composer *composer READ composer WRITE setComposer NOTIFY composerChanged STORED true)
-    Q_PROPERTY(ClassicalData::Work *work READ work WRITE setWork NOTIFY workChanged STORED true)
-    Q_PROPERTY(ClassicalData::Movement *movement READ movement WRITE setMovement NOTIFY movementChanged STORED true)
-    Q_PROPERTY(QSet<Dataquay::Uri> otherURIs READ otherURIs WRITE setOtherURIs NOTIFY otherURIsChanged STORED true)
 
 public:
-    TrackFile(QObject *parent = 0);
-    TrackFile(FileSource file, QObject *parent = 0);
-    
+    AudioFile(QObject *parent = 0);
+    AudioFile(FileSource file, QObject *parent = 0);
+
+    /// The URI is set automatically from the FileSource at construction
+    Dataquay::Uri uri() const { return m_uri; }
+    void setUri(Dataquay::Uri u) { m_uri = u; emit uriChanged(u); }
+
     /// The hash is set automatically from the FileSource at construction
     QString hash() const { return m_hash; }
     void setHash(QString hash) { m_hash = hash; emit hashChanged(hash); }
 
-    /// The URI is set automatically from the FileSource at construction
-    Dataquay::Uri uri() const { return m_uri; }
-    void setUri(Dataquay::Uri u) { m_uri = u; emit uriChanged(u); }
+signals:
+    void uriChanged(Dataquay::Uri);
+    void hashChanged(QString);
+
+private:
+    Dataquay::Uri m_uri;
+    QString m_hash;
+};
+
+class Signal : public QObject
+{
+    Q_OBJECT
+
+    Q_PROPERTY(QString ofaFingerprint READ ofaFingerprint WRITE setOfaFingerprint NOTIFY ofaFingerprintChanged STORED true)
+    Q_PROPERTY(QString puid READ puid WRITE setPuid NOTIFY puidChanged STORED true)
+    Q_PROPERTY(QSet<ClassicalData::AudioFile *> availableAs READ availableAs WRITE setAvailableAs NOTIFY availableAsChanged STORED true)
+    Q_PROPERTY(ClassicalData::Composer *composer READ composer WRITE setComposer NOTIFY composerChanged STORED true)
+    Q_PROPERTY(ClassicalData::Work *work READ work WRITE setWork NOTIFY workChanged STORED true)
+    Q_PROPERTY(ClassicalData::Movement *movement READ movement WRITE setMovement NOTIFY movementChanged STORED true)
+
+public:
+    Signal() : m_composer(0), m_work(0), m_movement(0) { }
 
     QString ofaFingerprint() const { return m_ofaFingerprint; }
     void setOfaFingerprint(QString fprint) { m_ofaFingerprint = fprint; emit ofaFingerprintChanged(fprint); }
@@ -611,6 +630,10 @@
     QString puid() const { return m_puid; }
     void setPuid(QString puid) { m_puid = puid; emit puidChanged(puid); }
 
+    QSet<AudioFile *> availableAs() const { return m_availableAs; }
+    void setAvailableAs(QSet<AudioFile *> aa) { m_availableAs = aa; emit availableAsChanged(m_availableAs); }
+    void addAvailableAs(AudioFile *aa) { m_availableAs.insert(aa); emit availableAsChanged(m_availableAs); }
+
     Composer *composer() const { return m_composer; }
     void setComposer(Composer *c) { m_composer = c; emit composerChanged(c); }
 
@@ -620,29 +643,21 @@
     Movement *movement() const { return m_movement; }
     void setMovement(Movement *m) { m_movement = m; emit movementChanged(m); }
 
-    QSet<Dataquay::Uri> otherURIs() const { return m_otherURIs; }
-    void addOtherURI(Dataquay::Uri u) { m_otherURIs.insert(u); emit otherURIsChanged(m_otherURIs); }
-    void setOtherURIs(QSet<Dataquay::Uri> u) { m_otherURIs = u; emit otherURIsChanged(u); }
-
 signals:
-    void uriChanged(Dataquay::Uri);
-    void hashChanged(QString);
     void ofaFingerprintChanged(QString);
     void puidChanged(QString);
     void composerChanged(Composer *);
     void workChanged(Work *);
     void movementChanged(Movement *);
-    void otherURIsChanged(QSet<Dataquay::Uri>);
+    void availableAsChanged(QSet<AudioFile *>);
 
 private:
-    Dataquay::Uri m_uri;
-    QString m_hash;
     QString m_ofaFingerprint;
     QString m_puid;
     Composer *m_composer;
     Work *m_work;
     Movement *m_movement;
-    QSet<Dataquay::Uri> m_otherURIs;
+    QSet<AudioFile *> m_availableAs;
 };
 
 }
@@ -655,11 +670,15 @@
 Q_DECLARE_METATYPE(ClassicalData::Work*);
 Q_DECLARE_METATYPE(ClassicalData::Movement*);
 Q_DECLARE_METATYPE(ClassicalData::Document*);
+Q_DECLARE_METATYPE(ClassicalData::AudioFile*);
+Q_DECLARE_METATYPE(ClassicalData::Signal*);
 Q_DECLARE_METATYPE(QSet<QString>);
 Q_DECLARE_METATYPE(QSet<Dataquay::Uri>);
 Q_DECLARE_METATYPE(QSet<ClassicalData::Work*>);
 Q_DECLARE_METATYPE(QSet<ClassicalData::Movement*>);
 Q_DECLARE_METATYPE(QSet<ClassicalData::Document*>);
+Q_DECLARE_METATYPE(QSet<ClassicalData::AudioFile*>);
+Q_DECLARE_METATYPE(QSet<ClassicalData::Signal*>);
 Q_DECLARE_METATYPE(ClassicalData::Composer*);
 Q_DECLARE_METATYPE(ClassicalData::Form*);
 Q_DECLARE_METATYPE(QSet<ClassicalData::Form*>);
--- a/common/TypeRegistrar.cpp	Mon May 10 17:33:00 2010 +0100
+++ b/common/TypeRegistrar.cpp	Fri May 14 17:58:04 2010 +0100
@@ -47,6 +47,12 @@
 	("QSet<QString>");
     qRegisterMetaType<QSet<Dataquay::Uri> >
 	("QSet<Dataquay::Uri>");
+    qRegisterMetaType<AudioFile *>
+	("ClassicalData::AudioFile*");
+    qRegisterMetaType<QSet<AudioFile *> >
+	("QSet<ClassicalData::AudioFile*>");
+    qRegisterMetaType<Signal *>
+	("ClassicalData::Signal*");
 
     Node::registerDatatype(Uri("http://www.w3.org/2001/XMLSchema#gYear"),
 			   "ClassicalData::Year", new Year::Encoder());
@@ -69,6 +75,8 @@
 	<Document, QObject>("ClassicalData::Document*");
     ObjectBuilder::getInstance()->registerClass
 	<Form, QObject>("ClassicalData::Form*");
+    ObjectBuilder::getInstance()->registerClass
+	<AudioFile>("ClassicalData::AudioFile*");
 
     ContainerBuilder::getInstance()->registerContainer
 	<QString, QSet<QString> >
@@ -97,6 +105,11 @@
 	<Form*, QSet<Form*> >
 	("ClassicalData::Form*", "QSet<ClassicalData::Form*>",
 	 ContainerBuilder::SetKind);
+
+    ContainerBuilder::getInstance()->registerContainer
+	<AudioFile*, QSet<AudioFile*> >
+	("ClassicalData::AudioFile*", "QSet<ClassicalData::AudioFile*>",
+	 ContainerBuilder::SetKind);
 }
 
 void
@@ -180,7 +193,15 @@
 	mapping->addPropertyMapping("ClassicalData::Composition", "composer", store->expand("mo:composer"));
 	mapping->addPropertyMapping("ClassicalData::Composition", "works", store->expand("mo:produced_work"));
 
-	mapping->addTypeMapping("ClassicalData::TrackFile", store->expand("mo:AudioFile"));
+	mapping->addTypeMapping("ClassicalData::AudioFile", store->expand("mo:AudioFile"));
+	mapping->addPropertyMapping("ClassicalData::AudioFile", "hash", store->expand("foaf:sha1"));
+
+	mapping->addTypeMapping("ClassicalData::Signal", store->expand("mo:Signal"));
+	mapping->addPropertyMapping("ClassicalData::Signal", "availableAs", store->expand("mo:available_as"));
+	mapping->addPropertyMapping("ClassicalData::Signal", "puid", store->expand("mo:puid"));
+	mapping->addPropertyMapping("ClassicalData::Signal", "composer", store->expand("mo:composer"));
+	mapping->addPropertyMapping("ClassicalData::Signal", "work", store->expand("mo:"));
+	mapping->addPropertyMapping("ClassicalData::Signal", "availableAs", store->expand("mo:available_as"));
     }
 }
 
--- a/utilities/the-application/the-application.cpp	Mon May 10 17:33:00 2010 +0100
+++ b/utilities/the-application/the-application.cpp	Fri May 14 17:58:04 2010 +0100
@@ -457,15 +457,16 @@
     guessWorkFromTitle(filepart, scale, composer, guesses);
 }
 
-TrackFile *
+Signal *
 guess(QString track)
 {
     cout << endl;
     cout << "Guessing composer for: " << track << endl;
 
-//    cerr << "Creating TrackFile object...";
+//    cerr << "Creating Signal object...";
     FileSource fs(track);
-    TrackFile *tf = new TrackFile(fs);
+    Signal *tf = new Signal;
+    tf->addAvailableAs(new AudioFile(fs));
 //    cerr << "done" << endl;
 //    cerr << "hash = " << tf->hash() << endl;
 
@@ -633,7 +634,20 @@
 
     FeatureFileIndex *ffi = FeatureFileIndex::getInstance();
 
-    ffi->loadFor(0, 0);
+    QStringList args;
+    for (int i = 1; i < argc; ++i) {
+        args.push_back(argv[i]);
+    }
+
+    BasicStore bs;
+    if (!args.empty()) {
+        foreach (QString track, args) {
+            FileSource fs(track);
+            AudioFile af(fs);
+            ffi->loadFor(&af, &bs);
+        }
+    } 
+
     
 /*
     BasicStore *index = new BasicStore;
@@ -759,7 +773,7 @@
     if (command == "guess") {
         if (args.empty()) usage(argv[0]);
         foreach (QString track, args) {
-            TrackFile *tf = guess(track);
+            Signal *tf = guess(track);
             localStorer->store(tf);
         }
     } 
--- a/utilities/track/track.cpp	Mon May 10 17:33:00 2010 +0100
+++ b/utilities/track/track.cpp	Fri May 14 17:58:04 2010 +0100
@@ -454,15 +454,16 @@
     guessWorkFromTitle(filepart, scale, composer, guesses);
 }
 
-TrackFile *
+Signal *
 guess(QString track)
 {
     cout << endl;
     cout << "Guessing composer for: " << track << endl;
 
-//    cerr << "Creating TrackFile object...";
+//    cerr << "Creating Signal...";
     FileSource fs(track);
-    TrackFile *tf = new TrackFile(fs);
+    Signal *tf = new Signal;
+    tf->addAvailableAs(new AudioFile(fs));
 //    cerr << "done" << endl;
 //    cerr << "hash = " << tf->hash() << endl;
 
@@ -701,15 +702,18 @@
 
     ObjectStorer *localStorer = new ObjectStorer(&localStore);
     localStorer->setTypeMapping(tm);
-//    localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
+    localStorer->setFollowPolicy(ObjectStorer::FollowObjectProperties);
+    localStorer->setPropertyStorePolicy(ObjectStorer::StoreIfChanged);
 
     if (command == "guess") {
         if (args.empty()) usage(argv[0]);
         foreach (QString track, args) {
-            TrackFile *tf = guess(track);
+            Signal *tf = guess(track);
             localStorer->store(tf);
         }
-    } 
+    } else {
+        usage(argv[0]);
+    }
 
     delete localStorer;
     localStore.save("local.ttl");