# HG changeset patch # User Chris Cannam # Date 1412185112 -3600 # Node ID de76b2df518f93323f20be8849b0c9ec4f067b97 # Parent 76d9d86ae6cd9aa76c9384cf6398e72229f17de7 Start on multiplex implementation diff -r 76d9d86ae6cd -r de76b2df518f runner/FeatureExtractionManager.cpp --- a/runner/FeatureExtractionManager.cpp Wed Oct 01 17:50:58 2014 +0100 +++ b/runner/FeatureExtractionManager.cpp Wed Oct 01 18:38:32 2014 +0100 @@ -14,6 +14,7 @@ */ #include "FeatureExtractionManager.h" +#include "MultiplexedReader.h" #include #include @@ -428,7 +429,7 @@ return addFeatureExtractor(transform, writers); } -void FeatureExtractionManager::addSource(QString audioSource) +void FeatureExtractionManager::addSource(QString audioSource, bool willMultiplex) { if (QFileInfo(audioSource).suffix().toLower() == "m3u") { ProgressPrinter retrievalProgress("Opening playlist file..."); @@ -443,7 +444,7 @@ if (reader.isOK()) { vector files = reader.load(); for (int i = 0; i < (int)files.size(); ++i) { - addSource(files[i]); + addSource(files[i], willMultiplex); } return; } else { @@ -487,10 +488,14 @@ cerr << "File or URL \"" << audioSource.toStdString() << "\" opened successfully" << endl; - if (m_channels == 0) { - m_channels = reader->getChannelCount(); - cerr << "Taking default channel count of " - << reader->getChannelCount() << " from file" << endl; + if (willMultiplex) { + ++m_channels; // channel count is simply number of sources + } else { + if (m_channels == 0) { + m_channels = reader->getChannelCount(); + cerr << "Taking default channel count of " + << reader->getChannelCount() << " from file" << endl; + } } if (m_defaultSampleRate == 0) { @@ -509,6 +514,8 @@ if (m_plugins.empty()) return; if (QFileInfo(audioSource).suffix().toLower() == "m3u") { + //!!! This shouldn't happen here, it should be done in + //!!! main.cpp when assembling the sources list FileSource source(audioSource); PlaylistFileReader reader(source); if (reader.isOK()) { @@ -546,13 +553,44 @@ (audioSource, "internal error: have sources and plugins, but no channel count"); } - AudioFileReader *reader = 0; + AudioFileReader *reader = prepareReader(audioSource); + extractFeaturesFor(reader, audioSource); // Note this also deletes reader +} - if (m_readyReaders.contains(audioSource)) { - reader = m_readyReaders[audioSource]; - m_readyReaders.remove(audioSource); - if (reader->getChannelCount() != m_channels || - reader->getSampleRate() != m_sampleRate) { +void FeatureExtractionManager::extractFeaturesMultiplexed(QStringList sources) +{ + if (m_plugins.empty() || sources.empty()) return; + + QString nominalSource = sources[0]; + + testOutputFiles(nominalSource); + + if (m_sampleRate == 0) { + throw FileOperationFailed + (nominalSource, "internal error: have sources and plugins, but no sample rate"); + } + if (m_channels == 0) { + throw FileOperationFailed + (nominalSource, "internal error: have sources and plugins, but no channel count"); + } + + QList readers; + foreach (QString source, sources) { + AudioFileReader *reader = prepareReader(source); + readers.push_back(reader); + } + + AudioFileReader *reader = new MultiplexedReader(readers); + extractFeaturesFor(reader, nominalSource); // Note this also deletes reader +} + +AudioFileReader * +FeatureExtractionManager::prepareReader(QString source) +{ + if (m_readyReaders.contains(source)) { + reader = m_readyReaders[source]; + m_readyReaders.remove(source); + if (reader->getSampleRate() != m_sampleRate) { // can't use this; open it again delete reader; reader = 0; @@ -560,16 +598,23 @@ } if (!reader) { ProgressPrinter retrievalProgress("Retrieving audio data..."); - FileSource source(audioSource, &retrievalProgress); - source.waitForData(); + FileSource fs(source, &retrievalProgress); + fs.waitForData(); reader = AudioFileReaderFactory::createReader - (source, m_sampleRate, false, &retrievalProgress); + (fs, m_sampleRate, false, &retrievalProgress); retrievalProgress.done(); } - if (!reader) { throw FailedToOpenFile(audioSource); } + return reader; +} + +void +FeatureExtractionManager::extractFeaturesFor(AudioFileReader *reader, + QString audioSource) +{ + // Note: This also deletes reader cerr << "Audio file \"" << audioSource.toStdString() << "\": " << reader->getChannelCount() << "ch at " diff -r 76d9d86ae6cd -r de76b2df518f runner/FeatureExtractionManager.h --- a/runner/FeatureExtractionManager.h Wed Oct 01 17:50:58 2014 +0100 +++ b/runner/FeatureExtractionManager.h Wed Oct 01 18:38:32 2014 +0100 @@ -61,13 +61,18 @@ // Make a note of an audio or playlist file which will be passed // to extractFeatures later. Amongst other things, this may // initialise the default sample rate and channel count - void addSource(QString audioSource); + void addSource(QString audioSource, bool willMultiplex); // Extract features from the given audio or playlist file. If the // file is a playlist and force is true, continue extracting even // if a file in the playlist fails. void extractFeatures(QString audioSource, bool force); + // Extract features from the given audio files, multiplexing into + // a single "file" whose individual channels are mixdowns of the + // supplied sources. + void extractFeaturesMultiplexed(QStringList sources); + private: // A plugin may have many outputs, so we can have more than one // transform requested for a single plugin. The things we want to @@ -102,6 +107,10 @@ bool m_summariesOnly; Vamp::HostExt::PluginSummarisingAdapter::SegmentBoundaries m_boundaries; + AudioFileReader *prepareReader(QString audioSource); + + void extractFeaturesFor(AudioFileReader *reader, QString audioSource); + void writeSummaries(QString audioSource, Vamp::Plugin *); void writeFeatures(QString audioSource, diff -r 76d9d86ae6cd -r de76b2df518f runner/MultiplexedReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/MultiplexedReader.h Wed Oct 01 18:38:32 2014 +0100 @@ -0,0 +1,93 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Annotator + A utility for batch feature extraction from audio files. + Mark Levy, Chris Sutton and Chris Cannam, Queen Mary, University of London. + Copyright 2007-2014 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 _MULTIPLEXED_READER_H_ +#define _MULTIPLEXED_READER_H_ + +#include "data/fileio/AudioFileReader.h" + +#include +#include + +typedef std::vector SampleBlock; + +class MultiplexedReader : public QObject +{ + Q_OBJECT + +public: + MultiplexedReader(QList readers); + virtual ~MultiplexedReader() { } + + //!!! the rest of this is currently still from AudioFileReader.h! Finish! + + + + + bool isOK() const { return (m_channelCount > 0); } + + virtual QString getError() const { return ""; } + + int getFrameCount() const { return m_frameCount; } + int getChannelCount() const { return m_channelCount; } + int getSampleRate() const { return m_sampleRate; } + + virtual int getNativeRate() const { return m_sampleRate; } // if resampled + + /** + * Return true if this file supports fast seek and random + * access. Typically this will be true for uncompressed formats + * and false for compressed ones. + */ + virtual bool isQuicklySeekable() const = 0; + + /** + * Return interleaved samples for count frames from index start. + * The resulting sample block will contain count * + * getChannelCount() samples (or fewer if end of file is reached). + * + * The subclass implementations of this function must be + * thread-safe -- that is, safe to call from multiple threads with + * different arguments on the same object at the same time. + */ + virtual void getInterleavedFrames(int start, int count, + SampleBlock &frames) const = 0; + + /** + * Return de-interleaved samples for count frames from index + * start. Implemented in this class (it calls + * getInterleavedFrames and de-interleaves). The resulting vector + * will contain getChannelCount() sample blocks of count samples + * each (or fewer if end of file is reached). + */ + virtual void getDeInterleavedFrames(int start, int count, + std::vector &frames) const; + + // only subclasses that do not know exactly how long the audio + // file is until it's been completely decoded should implement this + virtual int getDecodeCompletion() const { return 100; } // % + + virtual bool isUpdating() const { return false; } + +signals: + void frameCountChanged(); + +protected: + int m_frameCount; + int m_channelCount; + int m_sampleRate; +}; + +#endif diff -r 76d9d86ae6cd -r de76b2df518f runner/main.cpp --- a/runner/main.cpp Wed Oct 01 17:50:58 2014 +0100 +++ b/runner/main.cpp Wed Oct 01 18:38:32 2014 +0100 @@ -233,15 +233,12 @@ cerr << " Summarise in segments, with segment boundaries" << endl; cerr << " at A, B, ... seconds." << endl; cerr << endl; - -/*!!! This feature not implemented yet (sniff) cerr << " -m, --multiplex If multiple input audio files are given, use mono" << endl; cerr << " mixdowns of all files as the input channels for a single" << endl; cerr << " invocation of each transform, instead of running the" << endl; - cerr << " transform against all files separately." << endl; + cerr << " transform against all files separately. The first file" << endl; + cerr << " will be used for output reference name and sample rate." << endl; cerr << endl; -*/ - cerr << " -r, --recursive If any of the