# HG changeset patch # User Chris Cannam # Date 1228238245 0 # Node ID 83eae5239db62c507fa33fc97c82b56ac8cd64da # Parent b71116d3c180ff3fe6872995cd2df3206f8fa1cb * Permit viewing (though not editing) colour 3d plot layer data in the spreadsheet data viewer dialog * Add somewhat simplistic RDF export for layers * Fix display of peak frequencies in spectrum layer * Fix (I hope) sizing of plugin parameter dialog diff -r b71116d3c180 -r 83eae5239db6 data/fft/FFTDataServer.cpp --- a/data/fft/FFTDataServer.cpp Fri Nov 28 15:45:20 2008 +0000 +++ b/data/fft/FFTDataServer.cpp Tue Dec 02 17:17:25 2008 +0000 @@ -916,10 +916,14 @@ fillColumn(x, true); } + float mean = 0.f; for (size_t i = 0; i < count; ++i) { values[i] = cache->getMagnitudeAt(col, i * step + minbin); + mean += values[i]; } + if (count > 0) mean /= count; +// std::cerr << "FFTDataServer::getMagnitudeAt: returning " << count << " values of mean " << mean << std::endl; return true; } diff -r b71116d3c180 -r 83eae5239db6 data/model/DenseThreeDimensionalModel.h --- a/data/model/DenseThreeDimensionalModel.h Fri Nov 28 15:45:20 2008 +0000 +++ b/data/model/DenseThreeDimensionalModel.h Tue Dec 02 17:17:25 2008 +0000 @@ -17,12 +17,15 @@ #define _DENSE_THREE_DIMENSIONAL_MODEL_H_ #include "Model.h" +#include "TabularModel.h" #include "base/ZoomConstraint.h" +#include "base/RealTime.h" #include #include -class DenseThreeDimensionalModel : public Model +class DenseThreeDimensionalModel : public Model, + public TabularModel { Q_OBJECT @@ -108,6 +111,54 @@ virtual int getCompletion() const = 0; + /* + TabularModel methods. + This class is non-editable -- subclasses may be editable. + Row and column are transposed for the tabular view (which is + "on its side"). + */ + + virtual int getRowCount() const { return getWidth(); } + virtual int getColumnCount() const { return getHeight() + 2; } + + virtual QString getHeading(int column) const + { + switch (column) { + case 0: return tr("Time"); + case 1: return tr("Frame"); + default: return getBinName(column - 2); + } + } + + virtual QVariant getData(int row, int column, int role) const + { + switch (column) { + case 0: { + RealTime rt = RealTime::frame2RealTime(row * getResolution(), + getSampleRate()); + return rt.toText().c_str(); + } + case 1: + return int(row * getResolution()); + default: + return getValueAt(row, column - 2); + } + } + + virtual bool isColumnTimeValue(int col) const { + return col < 2; + } + virtual SortType getSortType(int col) const { + return SortNumeric; + } + + virtual long getFrameForRow(int row) const { + return row * getSampleRate(); + } + virtual int getRowForFrame(long frame) const { + return frame / getSampleRate(); + } + protected: DenseThreeDimensionalModel() { } }; diff -r b71116d3c180 -r 83eae5239db6 data/model/FFTModel.cpp --- a/data/model/FFTModel.cpp Fri Nov 28 15:45:20 2008 +0000 +++ b/data/model/FFTModel.cpp Tue Dec 02 17:17:25 2008 +0000 @@ -170,10 +170,13 @@ size_t h = getHeight(); float magnitudes[h]; + if (m_server->getMagnitudesAt(x << m_xshift, magnitudes)) { + for (size_t y = 0; y < h; ++y) { - result.push_back(magnitudes[h]); + result.push_back(magnitudes[y]); } + } else { for (size_t i = 0; i < h; ++i) result.push_back(0.f); } @@ -259,6 +262,10 @@ getColumn(x, values); + float mean = 0.f; + for (int i =0; i < values.size(); ++i) mean += values[i]; + if (values.size() >0) mean /= values.size(); + // For peak picking we use a moving median window, picking the // highest value within each continuous region of values that // exceed the median. For pitch adaptivity, we adjust the window @@ -269,6 +276,7 @@ std::deque window; std::vector inrange; float dist = 0.5; + size_t medianWinSize = getPeakPickWindowSize(type, sampleRate, ymin, dist); size_t halfWin = medianWinSize/2; @@ -280,6 +288,8 @@ if (ymax + halfWin < values.size()) binmax = ymax + halfWin; else binmax = values.size()-1; + size_t prevcentre = 0; + for (size_t bin = binmin; bin <= binmax; ++bin) { float value = values[bin]; @@ -290,7 +300,11 @@ medianWinSize = getPeakPickWindowSize(type, sampleRate, bin, dist); halfWin = medianWinSize/2; - while (window.size() > medianWinSize) window.pop_front(); + while (window.size() > medianWinSize) { + window.pop_front(); + } + + size_t actualSize = window.size(); if (type == MajorPitchAdaptivePeaks) { if (ymax + halfWin < values.size()) binmax = ymax + halfWin; @@ -301,25 +315,37 @@ std::sort(sorted.begin(), sorted.end()); float median = sorted[int(sorted.size() * dist)]; - if (value > median) { - inrange.push_back(bin); - } + size_t centrebin = 0; + if (bin > actualSize/2) centrebin = bin - actualSize/2; + + while (centrebin > prevcentre || bin == binmin) { - if (value <= median || bin+1 == values.size()) { - size_t peakbin = 0; - float peakval = 0.f; - if (!inrange.empty()) { - for (size_t i = 0; i < inrange.size(); ++i) { - if (i == 0 || values[inrange[i]] > peakval) { - peakval = values[inrange[i]]; - peakbin = inrange[i]; + if (centrebin > prevcentre) ++prevcentre; + + float centre = values[prevcentre]; + + if (centre > median) { + inrange.push_back(centrebin); + } + + if (centre <= median || centrebin+1 == values.size()) { + if (!inrange.empty()) { + size_t peakbin = 0; + float peakval = 0.f; + for (size_t i = 0; i < inrange.size(); ++i) { + if (i == 0 || values[inrange[i]] > peakval) { + peakval = values[inrange[i]]; + peakbin = inrange[i]; + } + } + inrange.clear(); + if (peakbin >= ymin && peakbin <= ymax) { + peaks.insert(peakbin); } } - inrange.clear(); - if (peakbin >= ymin && peakbin <= ymax) { - peaks.insert(peakbin); - } } + + if (bin == binmin) break; } } diff -r b71116d3c180 -r 83eae5239db6 rdf/RDFExporter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rdf/RDFExporter.cpp Tue Dec 02 17:17:25 2008 +0000 @@ -0,0 +1,176 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2008 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "RDFExporter.h" +#include "RDFFeatureWriter.h" + +#include + +#include "data/model/Model.h" +#include "data/model/RegionModel.h" +#include "data/model/NoteModel.h" +#include "data/model/SparseOneDimensionalModel.h" +#include "data/model/SparseTimeValueModel.h" +#include "data/model/TextModel.h" +#include "data/model/EditableDenseThreeDimensionalModel.h" + +bool +RDFExporter::canExportModel(Model *m) +{ + if (dynamic_cast(m)) return true; + if (dynamic_cast(m)) return true; + if (dynamic_cast(m)) return true; + if (dynamic_cast(m)) return true; + if (dynamic_cast(m)) return true; + if (dynamic_cast(m)) return true; + return false; +} + +RDFExporter::RDFExporter(QString path, Model *m) : + m_path(path), + m_model(m), + m_fw(new RDFFeatureWriter()) +{ + map params; + params["one-file"] = path.toStdString(); + params["force"] = "true"; + m_fw->setParameters(params); +} + +RDFExporter::~RDFExporter() +{ + delete m_fw; +} + +bool +RDFExporter::isOK() const +{ + return true; +} + +QString +RDFExporter::getError() const +{ + return ""; +} + +void +RDFExporter::write() +{ + QString trackId; // nil + Transform transform; // nil + Vamp::Plugin::OutputDescriptor output; // nil + std::string summaryType; // nil + + Vamp::Plugin::FeatureList features; + features.push_back(Vamp::Plugin::Feature()); + Vamp::Plugin::Feature &f = features[0]; + int sr = m_model->getSampleRate(); + + { + RegionModel *m = dynamic_cast(m_model); + if (m) { + f.hasTimestamp = true; + f.hasDuration = true; + const RegionModel::PointList &pl(m->getPoints()); + for (RegionModel::PointList::const_iterator i = pl.begin(); + i != pl.end(); ++i) { + f.timestamp = Vamp::RealTime::frame2RealTime(i->frame, sr); + f.duration = Vamp::RealTime::frame2RealTime(i->duration, sr); + f.values.clear(); + f.values.push_back(i->value); + f.label = i->label.toStdString(); + m_fw->write(trackId, transform, output, features, summaryType); + } + return; + } + } + { + NoteModel *m = dynamic_cast(m_model); + if (m) { + f.hasTimestamp = true; + f.hasDuration = true; + const NoteModel::PointList &pl(m->getPoints()); + for (NoteModel::PointList::const_iterator i = pl.begin(); + i != pl.end(); ++i) { + f.timestamp = Vamp::RealTime::frame2RealTime(i->frame, sr); + f.duration = Vamp::RealTime::frame2RealTime(i->duration, sr); + f.values.clear(); + f.values.push_back(i->value); + f.values.push_back(i->level); + f.label = i->label.toStdString(); + m_fw->write(trackId, transform, output, features, summaryType); + } + return; + } + } + { + SparseOneDimensionalModel *m = dynamic_cast(m_model); + if (m) { + f.hasTimestamp = true; + f.hasDuration = false; + const SparseOneDimensionalModel::PointList &pl(m->getPoints()); + for (SparseOneDimensionalModel::PointList::const_iterator i = pl.begin(); + i != pl.end(); ++i) { + f.timestamp = Vamp::RealTime::frame2RealTime(i->frame, sr); + f.values.clear(); + f.label = i->label.toStdString(); + m_fw->write(trackId, transform, output, features, summaryType); + } + return; + } + } + { + SparseTimeValueModel *m = dynamic_cast(m_model); + if (m) { + f.hasTimestamp = true; + f.hasDuration = false; + const SparseTimeValueModel::PointList &pl(m->getPoints()); + for (SparseTimeValueModel::PointList::const_iterator i = pl.begin(); + i != pl.end(); ++i) { + f.timestamp = Vamp::RealTime::frame2RealTime(i->frame, sr); + f.values.clear(); + f.values.push_back(i->value); + f.label = i->label.toStdString(); + m_fw->write(trackId, transform, output, features, summaryType); + } + return; + } + } + { + TextModel *m = dynamic_cast(m_model); + if (m) { + f.hasTimestamp = true; + f.hasDuration = false; + const TextModel::PointList &pl(m->getPoints()); + for (TextModel::PointList::const_iterator i = pl.begin(); + i != pl.end(); ++i) { + f.timestamp = Vamp::RealTime::frame2RealTime(i->frame, sr); + f.values.clear(); + f.values.push_back(i->height); + f.label = i->label.toStdString(); + m_fw->write(trackId, transform, output, features, summaryType); + } + return; + } + } +} + +QString +RDFExporter::getSupportedExtensions() +{ + return "*.n3 *.ttl"; +} + diff -r b71116d3c180 -r 83eae5239db6 rdf/RDFExporter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rdf/RDFExporter.h Tue Dec 02 17:17:25 2008 +0000 @@ -0,0 +1,49 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2008 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RDF_EXPORTER_H_ +#define _RDF_EXPORTER_H_ + +#include + +class Model; +class RDFFeatureWriter; + +class RDFExporter +{ +public: + /** + * Return the file extensions that we can write, in a format + * suitable for use with QFileDialog. For example, "*.ttl *.n3". + */ + static QString getSupportedExtensions(); + + RDFExporter(QString path, Model *model); + virtual ~RDFExporter(); + + static bool canExportModel(Model *); + + virtual bool isOK() const; + virtual QString getError() const; + + virtual void write(); + +protected: + QString m_path; + Model *m_model; + RDFFeatureWriter *m_fw; +}; + +#endif diff -r b71116d3c180 -r 83eae5239db6 rdf/RDFFeatureWriter.cpp --- a/rdf/RDFFeatureWriter.cpp Fri Nov 28 15:45:20 2008 +0000 +++ b/rdf/RDFFeatureWriter.cpp Tue Dec 02 17:17:25 2008 +0000 @@ -221,11 +221,18 @@ } QString timelineURI = m_trackTimelineURIs[trackId]; - stream << "\n<" << url.toEncoded().data() << "> a mo:AudioFile .\n\n" - << signalURI << " a mo:Signal ;\n" - << " mo:available_as <" << url.toEncoded().data() - << "> ;\n" - << " mo:time [\n" + if (trackId != "") { + stream << "\n<" << url.toEncoded().data() << "> a mo:AudioFile .\n\n"; + } + + stream << signalURI << " a mo:Signal ;\n"; + + if (trackId != "") { + stream << " mo:available_as <" << url.toEncoded().data() + << "> ;\n"; + } + + stream << " mo:time [\n" << " a tl:Interval ;\n" << " tl:onTimeLine " << timelineURI << "\n ] .\n\n"; @@ -284,8 +291,10 @@ m_transformURIs[transform] = transformUri; } - stream << RDFTransformFactory::writeTransformToRDF(transform, transformUri) - << endl; + if (transform.getIdentifier() != "") { + stream << RDFTransformFactory::writeTransformToRDF(transform, transformUri) + << endl; + } if (needEventType) { @@ -388,8 +397,10 @@ << "S\"^^xsd:duration ;\n ] "; } - stream << ";\n"; - stream << " vamp:computed_by " << m_transformURIs[transform] << " "; + if (transform.getIdentifier() != "") { + stream << ";\n"; + stream << " vamp:computed_by " << m_transformURIs[transform] << " "; + } if (feature.label.length() > 0) { stream << ";\n"; diff -r b71116d3c180 -r 83eae5239db6 rdf/RDFImporter.cpp --- a/rdf/RDFImporter.cpp Fri Nov 28 15:45:20 2008 +0000 +++ b/rdf/RDFImporter.cpp Tue Dec 02 17:17:25 2008 +0000 @@ -563,7 +563,7 @@ SimpleSPARQLQuery query(s, queryString); query.setProgressReporter(reporter); - cerr << "Query will be: " << queryString.toStdString() << endl; +// cerr << "Query will be: " << queryString.toStdString() << endl; SimpleSPARQLQuery::ResultList results = query.execute(); diff -r b71116d3c180 -r 83eae5239db6 rdf/rdf.pro --- a/rdf/rdf.pro Fri Nov 28 15:45:20 2008 +0000 +++ b/rdf/rdf.pro Tue Dec 02 17:17:25 2008 +0000 @@ -15,12 +15,14 @@ # Input HEADERS += PluginRDFDescription.h \ PluginRDFIndexer.h \ + RDFExporter.h \ RDFFeatureWriter.h \ RDFImporter.h \ RDFTransformFactory.h \ SimpleSPARQLQuery.h SOURCES += PluginRDFDescription.cpp \ PluginRDFIndexer.cpp \ + RDFExporter.cpp \ RDFFeatureWriter.cpp \ RDFImporter.cpp \ RDFTransformFactory.cpp \