# HG changeset patch # User Chris Cannam # Date 1457094575 0 # Node ID 59ae7e04f7e982f08be31c3d635dcee358c50861 # Parent fa1bec83441e85a606b394ec36fa5c2f56a98e5d# Parent 134ce7667256fb204f3bebd293c252bc57cbe07e Merge diff -r fa1bec83441e -r 59ae7e04f7e9 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/data/fileio/AudioFileReaderFactory.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -26,6 +26,8 @@ #include #include +//#define DEBUG_AUDIO_FILE_READER_FACTORY 1 + QString AudioFileReaderFactory::getKnownExtensions() { @@ -84,7 +86,9 @@ { QString err; - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\"): Requested rate: " << targetRate << endl; +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\"): Requested rate: " << targetRate << endl; +#endif if (!source.isOK()) { cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl; @@ -92,7 +96,7 @@ } if (!source.isAvailable()) { - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; + cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; return 0; } @@ -112,8 +116,10 @@ normalised || (targetRate != 0 && fileRate != targetRate))) { - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl; - +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl; +#endif + delete reader; reader = new DecodingWavFileReader (source, @@ -231,7 +237,9 @@ normalised || (targetRate != 0 && fileRate != targetRate))) { - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl; +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl; +#endif delete reader; reader = new DecodingWavFileReader @@ -327,7 +335,9 @@ if (reader) { if (reader->isOK()) { - SVDEBUG << "AudioFileReaderFactory: Reader is OK" << endl; +#ifdef DEBUG_AUDIO_FILE_READER_FACTORY + cerr << "AudioFileReaderFactory: Reader is OK" << endl; +#endif return reader; } cerr << "AudioFileReaderFactory: Preferred reader for " diff -r fa1bec83441e -r 59ae7e04f7e9 data/fileio/CodedAudioFileReader.cpp --- a/data/fileio/CodedAudioFileReader.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/data/fileio/CodedAudioFileReader.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -137,11 +137,27 @@ } fileInfo.samplerate = fileRate; fileInfo.channels = m_channelCount; - - // No point in writing 24-bit or float; generally this - // class is used for decoding files that have come from a - // 16 bit source or that decode to only 16 bits anyway. - fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + + // Previously we were writing SF_FORMAT_PCM_16 and in a + // comment I wrote: "No point in writing 24-bit or float; + // generally this class is used for decoding files that + // have come from a 16 bit source or that decode to only + // 16 bits anyway." That was naive -- we want to preserve + // the original values to the same float precision that we + // use internally. Saving PCM_16 obviously doesn't + // preserve values for sources at bit depths greater than + // 16, but it also doesn't always do so for sources at bit + // depths less than 16. + // + // (This came to light with a bug in libsndfile 1.0.26, + // which always reports every file as non-seekable, so + // that coded readers were being used even for WAV + // files. This changed the values that came from PCM_8 WAV + // sources, breaking Sonic Annotator's output comparison + // tests.) + // + // So: now we write floats. + fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(), SFM_WRITE, &fileInfo); diff -r fa1bec83441e -r 59ae7e04f7e9 data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/data/fileio/MP3FileReader.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -32,6 +32,7 @@ #ifdef HAVE_ID3TAG #include #endif + //#define DEBUG_ID3TAG 1 #include @@ -178,7 +179,7 @@ id3_tag *tag = id3_file_tag(file); if (!tag) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl; + cerr << "MP3FileReader::loadTags: No ID3 tag found" << endl; #endif id3_file_close(file); return; @@ -201,7 +202,7 @@ #else #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" + cerr << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl; #endif #endif @@ -216,20 +217,20 @@ id3_frame *frame = id3_tag_findframe(tag, name, 0); if (!frame) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: No \"" << name << "\" in ID3 tag" << endl; #endif return ""; } if (frame->nfields < 2) { - SVDEBUG << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: WARNING: Not enough fields (" << frame->nfields << ") for \"" << name << "\" in ID3 tag" << endl; return ""; } unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]); if (nstrings == 0) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << endl; #endif return ""; } @@ -237,7 +238,7 @@ id3_ucs4_t const *ustr = id3_field_getstrings(&frame->fields[1], 0); if (!ustr) { #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl; + cerr << "MP3FileReader::loadTags: Invalid or absent data for \"" << name << "\" in ID3 tag" << endl; #endif return ""; } @@ -252,7 +253,7 @@ free(u8str); #ifdef DEBUG_ID3TAG - SVDEBUG << "MP3FileReader::loadTags: tag \"" << name << "\" -> \"" + cerr << "MP3FileReader::loadTags: tag \"" << name << "\" -> \"" << rv << "\"" << endl; #endif diff -r fa1bec83441e -r 59ae7e04f7e9 data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/data/fileio/WavFileReader.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -60,20 +60,26 @@ m_seekable = (m_fileInfo.seekable != 0); - // Our m_seekable reports whether a file is rapidly seekable, - // so things like Ogg don't qualify. We cautiously report - // every file type of "at least" the historical period of Ogg - // or FLAC as non-seekable. int type = m_fileInfo.format & SF_FORMAT_TYPEMASK; -// cerr << "WavFileReader: format type is " << type << " (flac, ogg are " << SF_FORMAT_FLAC << ", " << SF_FORMAT_OGG << ")" << endl; + int subtype = m_fileInfo.format & SF_FORMAT_SUBMASK; + if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) { -// cerr << "WavFileReader: Recording as non-seekable" << endl; + // Our m_seekable reports whether a file is rapidly + // seekable, so things like Ogg don't qualify. We + // cautiously report every file type of "at least" the + // historical period of Ogg or FLAC as non-seekable. m_seekable = false; + } else if (type == SF_FORMAT_WAV && subtype <= SF_FORMAT_DOUBLE) { + // libsndfile 1.0.26 has a bug (subsequently fixed in the + // repo) that causes all files to be reported as + // non-seekable. We know that certain common file types + // are definitely seekable so, again cautiously, identify + // and mark those (basically only non-adaptive WAVs). + m_seekable = true; } } -// cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", seekable " << m_seekable << endl; - +// cerr << "WavFileReader: Filename " << m_path << ", frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", format " << m_fileInfo.format << ", seekable " << m_fileInfo.seekable << " adjusted to " << m_seekable << endl; } WavFileReader::~WavFileReader() diff -r fa1bec83441e -r 59ae7e04f7e9 plugin/FeatureExtractionPluginFactory.cpp --- a/plugin/FeatureExtractionPluginFactory.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/plugin/FeatureExtractionPluginFactory.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -30,6 +30,8 @@ #include "base/Profiler.h" +using namespace std; + //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper { @@ -77,25 +79,25 @@ return instance(type); } -std::vector +vector FeatureExtractionPluginFactory::getPluginPath() { if (!m_pluginPath.empty()) return m_pluginPath; - std::vector p = Vamp::PluginHostAdapter::getPluginPath(); + vector p = Vamp::PluginHostAdapter::getPluginPath(); for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str()); return m_pluginPath; } -std::vector +vector FeatureExtractionPluginFactory::getAllPluginIdentifiers() { FeatureExtractionPluginFactory *factory; - std::vector rv; + vector rv; factory = instance("vamp"); if (factory) { - std::vector tmp = factory->getPluginIdentifiers(); + vector tmp = factory->getPluginIdentifiers(); for (size_t i = 0; i < tmp.size(); ++i) { // cerr << "identifier: " << tmp[i] << endl; rv.push_back(tmp[i]); @@ -108,103 +110,165 @@ return rv; } -std::vector +vector +FeatureExtractionPluginFactory::getPluginCandidateFiles() +{ + vector path = getPluginPath(); + vector candidates; + + for (QString dirname : path) { + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << dirname << endl; +#endif + + QDir pluginDir(dirname, PLUGIN_GLOB, + QDir::Name | QDir::IgnoreCase, + QDir::Files | QDir::Readable); + + for (unsigned int j = 0; j < pluginDir.count(); ++j) { + QString soname = pluginDir.filePath(pluginDir[j]); + candidates.push_back(soname); + } + } + + return candidates; +} + +vector +FeatureExtractionPluginFactory::winnowPluginCandidates(vector candidates, + QString &warningMessage) +{ + vector good, bad; + vector badStatuses; + + for (QString c: candidates) { + + PluginLoadStatus status = + TestPluginLoadability(c, "vampGetPluginDescriptor"); + + if (status == PluginLoadOK) { + good.push_back(c); + } else if (status == UnknownPluginLoadStatus) { + cerr << "WARNING: Unknown load status for plugin candidate \"" + << c << "\", continuing" << endl; + good.push_back(c); + } else { + bad.push_back(c); + badStatuses.push_back(status); + } + } + + if (!bad.empty()) { + warningMessage = + QObject::tr("Failed to load plugins" + "

Failed to load one or more plugin libraries:

\n"); + warningMessage += "
    "; + for (int i = 0; in_range_for(bad, i); ++i) { + QString m; + if (badStatuses[i] == PluginLoadFailedToLoadLibrary) { + m = QObject::tr("Failed to load library"); + } else if (badStatuses[i] == PluginLoadFailedToFindDescriptor) { + m = QObject::tr("Failed to query plugins from library after loading"); + } else if (badStatuses[i] == PluginLoadFailedElsewhere) { + m = QObject::tr("Unknown failure"); + } else { + m = QObject::tr("Success: internal error?"); + } + warningMessage += QString("
  • %1 (%2)
  • \n") + .arg(bad[i]) + .arg(m); + } + warningMessage += "
"; + } + return good; +} + +vector FeatureExtractionPluginFactory::getPluginIdentifiers() { Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers"); - std::vector rv; - std::vector path = getPluginPath(); + vector rv; + vector candidates = winnowPluginCandidates(getPluginCandidateFiles(), + m_pluginScanError); - for (std::vector::iterator i = path.begin(); i != path.end(); ++i) { + for (QString soname : candidates) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << *i << endl; + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl; #endif - QDir pluginDir(*i, PLUGIN_GLOB, - QDir::Name | QDir::IgnoreCase, - QDir::Files | QDir::Readable); - - for (unsigned int j = 0; j < pluginDir.count(); ++j) { - - QString soname = pluginDir.filePath(pluginDir[j]); + void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); + + if (!libraryHandle) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; + continue; + } #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname << endl; + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << endl; #endif - void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); - - if (!libraryHandle) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; - continue; + VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) + DLSYM(libraryHandle, "vampGetPluginDescriptor"); + + if (!fn) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + } + continue; + } + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl; +#endif + + const VampPluginDescriptor *descriptor = 0; + int index = 0; + + map known; + bool ok = true; + + while ((descriptor = fn(VAMP_API_VERSION, index))) { + + if (known.find(descriptor->identifier) != known.end()) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library " + << soname + << " returns the same plugin identifier \"" + << descriptor->identifier << "\" at indices " + << known[descriptor->identifier] << " and " + << index << endl; + cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; + ok = false; + break; + } else { + known[descriptor->identifier] = index; } -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << endl; -#endif + ++index; + } - VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) - DLSYM(libraryHandle, "vampGetPluginDescriptor"); + if (ok) { - if (!fn) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; - if (DLCLOSE(libraryHandle) != 0) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; - } - continue; - } - -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl; -#endif - - const VampPluginDescriptor *descriptor = 0; - int index = 0; - - std::map known; - bool ok = true; + index = 0; while ((descriptor = fn(VAMP_API_VERSION, index))) { - if (known.find(descriptor->identifier) != known.end()) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library " - << soname - << " returns the same plugin identifier \"" - << descriptor->identifier << "\" at indices " - << known[descriptor->identifier] << " and " - << index << endl; - cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; - ok = false; - break; - } else { - known[descriptor->identifier] = index; - } - + QString id = PluginIdentifier::createIdentifier + ("vamp", soname, descriptor->identifier); + rv.push_back(id); +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + SVDEBUG << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; +#endif ++index; } - - if (ok) { - - index = 0; - - while ((descriptor = fn(VAMP_API_VERSION, index))) { - - QString id = PluginIdentifier::createIdentifier - ("vamp", soname, descriptor->identifier); - rv.push_back(id); -#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE - cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; -#endif - ++index; - } - } + } - if (DLCLOSE(libraryHandle) != 0) { - cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; - } - } + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + } } generateTaxonomy(); @@ -279,8 +343,8 @@ if (file != "") return file; } - std::vector path = getPluginPath(); - for (std::vector::iterator i = path.begin(); + vector path = getPluginPath(); + for (vector::iterator i = path.begin(); i != path.end(); ++i) { if (*i != "") { file = findPluginFile(soname, *i); @@ -312,7 +376,9 @@ QString type, soname, label; PluginIdentifier::parseIdentifier(identifier, type, soname, label); if (type != "vamp") { - cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; +#endif return 0; } @@ -375,7 +441,9 @@ } } -// SVDEBUG << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl; +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl; +#endif return rv; } @@ -385,7 +453,9 @@ { void *handle = m_handleMap[plugin]; if (handle) { -// SVDEBUG << "unloading library " << handle << " for plugin " << plugin << endl; +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "unloading library " << handle << " for plugin " << plugin << endl; +#endif DLCLOSE(handle); } m_handleMap.erase(plugin); @@ -400,8 +470,8 @@ void FeatureExtractionPluginFactory::generateTaxonomy() { - std::vector pluginPath = getPluginPath(); - std::vector path; + vector pluginPath = getPluginPath(); + vector path; for (size_t i = 0; i < pluginPath.size(); ++i) { if (pluginPath[i].contains("/lib/")) { diff -r fa1bec83441e -r 59ae7e04f7e9 plugin/FeatureExtractionPluginFactory.h --- a/plugin/FeatureExtractionPluginFactory.h Fri Mar 04 09:50:09 2016 +0000 +++ b/plugin/FeatureExtractionPluginFactory.h Fri Mar 04 12:29:35 2016 +0000 @@ -38,6 +38,14 @@ virtual std::vector getPluginIdentifiers(); + /** + * Return any error message arising from the initial plugin + * scan. The return value will either be an empty string (nothing + * to report) or an HTML string suitable for dropping into a + * dialog and showing the user. + */ + virtual QString getPluginPopulationWarning() { return m_pluginScanError; } + virtual QString findPluginFile(QString soname, QString inDir = ""); // We don't set blockSize or channels on this -- they're @@ -57,8 +65,14 @@ friend class PluginDeletionNotifyAdapter; void pluginDeleted(Vamp::Plugin *); std::map m_handleMap; + + std::vector getPluginCandidateFiles(); + std::vector winnowPluginCandidates(std::vector candidates, + QString &warningMessage); + + void generateTaxonomy(); - void generateTaxonomy(); + QString m_pluginScanError; }; #endif diff -r fa1bec83441e -r 59ae7e04f7e9 rdf/RDFTransformFactory.cpp --- a/rdf/RDFTransformFactory.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/rdf/RDFTransformFactory.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -129,7 +129,12 @@ } m_store->import(qurl, BasicStore::ImportIgnoreDuplicates); m_isRDF = true; - } catch (...) { } + } catch (const std::exception &e) { + // The file is not RDF -- we report this by returning false + // from isRDF (because we have not reached the m_isRDF = true + // line above), but we also set the error string + m_errorString = e.what(); + } } RDFTransformFactoryImpl::~RDFTransformFactoryImpl() @@ -146,7 +151,7 @@ bool RDFTransformFactoryImpl::isOK() { - return (m_errorString == ""); + return m_isRDF && (m_errorString == ""); } QString @@ -160,6 +165,8 @@ { std::vector transforms; + if (!m_isRDF) return transforms; + std::map uriTransformMap; Nodes tnodes = m_store->match diff -r fa1bec83441e -r 59ae7e04f7e9 rdf/RDFTransformFactory.h --- a/rdf/RDFTransformFactory.h Fri Mar 04 09:50:09 2016 +0000 +++ b/rdf/RDFTransformFactory.h Fri Mar 04 12:29:35 2016 +0000 @@ -36,8 +36,28 @@ RDFTransformFactory(QString url); virtual ~RDFTransformFactory(); - bool isRDF(); // true if the file was parseable and had transforms in it - bool isOK(); // true if the transforms could be completely constructed + /** isRDF() may be queried at any point after construction. It + returns true if the file was parseable as RDF. + */ + bool isRDF(); + + /** isOK() may be queried at any point after getTransforms() has + been called. It is true if the file was parseable as RDF and + any transforms in it could be completely constructed. + + Note that even if isOK() returns true, it is still possible + that the file did not define any transforms; in this case, + getTransforms() would have returned an empty list. + + If isOK() is called before getTransforms() has been invoked to + query the file, it will return true iff isRDF() is true. + */ + bool isOK(); + + /** Return any error string resulting from loading or querying the + file. This will be non-empty if isRDF() or isOK() returns + false. + */ QString getErrorString() const; std::vector getTransforms(ProgressReporter *reporter); diff -r fa1bec83441e -r 59ae7e04f7e9 system/System.cpp --- a/system/System.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/system/System.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -325,3 +325,66 @@ double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } float princargf(float a) { return float(princarg(a)); } +#ifndef _WIN32 + +#include +#include + +PluginLoadStatus +TestPluginLoadability(QString soname, QString descriptorFn) +{ + //!!! This is POSIX only, no equivalent on Windows, where we'll + //!!! have to do something completely different + + pid_t pid = fork(); + + if (pid < 0) { + return UnknownPluginLoadStatus; // fork failed + } + + if (pid == 0) { // the child process + + void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); + if (!handle) { + cerr << "isPluginLibraryLoadable: Failed to open plugin library \"" + << soname << "\": " << dlerror() << "\n"; + cerr << "exiting with status 1" << endl; + exit(1); + } + + void *fn = DLSYM(handle, descriptorFn.toLocal8Bit().data()); + if (!fn) { + cerr << "isPluginLibraryLoadable: Failed to find plugin descriptor function \"" << descriptorFn << "\" in library \"" << soname << "\": " << dlerror() << "\n"; + exit(2); + } + +// cerr << "isPluginLibraryLoadable: Successfully loaded library \"" << soname << "\" and retrieved descriptor function" << endl; + + exit(0); + + } else { // the parent process + + int status = 0; + + do { + waitpid(pid, &status, 0); + } while (WIFSTOPPED(status)); + + if (WIFEXITED(status)) { + switch (WEXITSTATUS(status)) { + case 0: return PluginLoadOK; // success + case 1: return PluginLoadFailedToLoadLibrary; + case 2: return PluginLoadFailedToFindDescriptor; + default: return PluginLoadFailedElsewhere; + } + } + + if (WIFSIGNALED(status)) { + return PluginLoadFailedElsewhere; + } + + return UnknownPluginLoadStatus; + } +} + +#endif diff -r fa1bec83441e -r 59ae7e04f7e9 system/System.h --- a/system/System.h Fri Mar 04 09:50:09 2016 +0000 +++ b/system/System.h Fri Mar 04 12:29:35 2016 +0000 @@ -154,6 +154,21 @@ extern void StoreStartupLocale(); extern void RestoreStartupLocale(); +enum PluginLoadStatus { + UnknownPluginLoadStatus, + PluginLoadOK, + PluginLoadFailedToLoadLibrary, + PluginLoadFailedToFindDescriptor, + PluginLoadFailedElsewhere +}; + +// Check whether a plugin library is loadable without crashing (may +// need to spawn an external process to do it). Descriptor fn is the +// name of a LADSPA/DSSI/Vamp-style descriptor function to try +// calling; may be an empty string if the plugin doesn't follow that +// convention. +PluginLoadStatus TestPluginLoadability(QString soname, QString descriptorFn); + #include #ifndef M_PI diff -r fa1bec83441e -r 59ae7e04f7e9 transform/CSVFeatureWriter.cpp --- a/transform/CSVFeatureWriter.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/transform/CSVFeatureWriter.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -99,10 +99,10 @@ SVDEBUG << "CSVFeatureWriter::setParameters" << endl; for (map::iterator i = params.begin(); i != params.end(); ++i) { - cerr << i->first << " -> " << i->second << endl; + SVDEBUG << i->first << " -> " << i->second << endl; if (i->first == "separator") { m_separator = i->second.c_str(); - cerr << "m_separator = " << m_separator << endl; + SVDEBUG << "m_separator = " << m_separator << endl; if (m_separator == "\\t") { m_separator = QChar::Tabulation; } diff -r fa1bec83441e -r 59ae7e04f7e9 transform/Transform.cpp --- a/transform/Transform.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/transform/Transform.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -54,12 +54,8 @@ int errorColumn; if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) { - cerr << "Transform::Transform: Error in parsing XML: " - << error << " at line " << errorLine - << ", column " << errorColumn << endl; - cerr << "Input follows:" << endl; - cerr << xml << endl; - cerr << "Input ends." << endl; + m_errorString = QString("%1 at line %2, column %3") + .arg(error).arg(errorLine).arg(errorColumn); return; } diff -r fa1bec83441e -r 59ae7e04f7e9 transform/Transform.h --- a/transform/Transform.h Fri Mar 04 09:50:09 2016 +0000 +++ b/transform/Transform.h Fri Mar 04 12:29:35 2016 +0000 @@ -46,7 +46,8 @@ /** * Construct a Transform by parsing the given XML data string. - * This is the inverse of toXml. + * This is the inverse of toXml. If this fails, getErrorString() + * will return a non-empty string. */ Transform(QString xml); @@ -156,6 +157,8 @@ */ void setFromXmlAttributes(const QXmlAttributes &); + QString getErrorString() const { return m_errorString; } + static SummaryType stringToSummaryType(QString); static QString summaryTypeToString(SummaryType); @@ -195,6 +198,7 @@ RealTime m_startTime; RealTime m_duration; sv_samplerate_t m_sampleRate; + QString m_errorString; }; typedef std::vector Transforms; diff -r fa1bec83441e -r 59ae7e04f7e9 transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Fri Mar 04 09:50:09 2016 +0000 +++ b/transform/TransformFactory.cpp Fri Mar 04 12:29:35 2016 +0000 @@ -399,6 +399,18 @@ m_transformsPopulated = true; } +QString +TransformFactory::getPluginPopulationWarning() +{ + FeatureExtractionPluginFactory *vfactory = + FeatureExtractionPluginFactory::instance("vamp"); + QString warningMessage; + if (vfactory) { + warningMessage = vfactory->getPluginPopulationWarning(); + } + return warningMessage; +} + void TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) { diff -r fa1bec83441e -r 59ae7e04f7e9 transform/TransformFactory.h --- a/transform/TransformFactory.h Fri Mar 04 09:50:09 2016 +0000 +++ b/transform/TransformFactory.h Fri Mar 04 12:29:35 2016 +0000 @@ -196,6 +196,14 @@ void setParametersFromPluginConfigurationXml(Transform &transform, QString xml); + /** + * Return any error message arising from the initial plugin + * scan. The return value will either be an empty string (nothing + * to report) or an HTML string suitable for dropping into a + * dialog and showing the user. + */ + QString getPluginPopulationWarning(); + protected: typedef std::map TransformDescriptionMap;