# HG changeset patch # User Chris Cannam # Date 1412345239 -3600 # Node ID 95de6db296a13153d1e48be7eedd006c4931e9a0 # Parent 74f7ad72fee6579f6916cd5262683e98e9898d4e# Parent 34a0dad473c3c98adc5837c29ced67b752fb8349 Merge from multiplex branch diff -r 74f7ad72fee6 -r 95de6db296a1 .hgsubstate --- a/.hgsubstate Thu Oct 02 15:21:16 2014 +0100 +++ b/.hgsubstate Fri Oct 03 15:07:19 2014 +0100 @@ -1,2 +1,2 @@ d16f0fd6db6104d87882bc43788a3bb1b0f8c528 dataquay -58c4d69b4dd8ff05908097ea2d5c520067b12aa4 svcore +c9d456b1fcde4ddc6e0347c4eb105a8b6e4ca527 svcore diff -r 74f7ad72fee6 -r 95de6db296a1 runner.pro --- a/runner.pro Thu Oct 02 15:21:16 2014 +0100 +++ b/runner.pro Fri Oct 03 15:07:19 2014 +0100 @@ -67,14 +67,16 @@ runner/AudioDBFeatureWriter.h \ runner/FeatureWriterFactory.h \ runner/DefaultFeatureWriter.h \ - runner/FeatureExtractionManager.h + runner/FeatureExtractionManager.h \ + runner/MultiplexedReader.h SOURCES += \ runner/main.cpp \ runner/DefaultFeatureWriter.cpp \ runner/FeatureExtractionManager.cpp \ runner/AudioDBFeatureWriter.cpp \ - runner/FeatureWriterFactory.cpp + runner/FeatureWriterFactory.cpp \ + runner/MultiplexedReader.cpp !win32 { QMAKE_POST_LINK=/bin/bash tests/test.sh diff -r 74f7ad72fee6 -r 95de6db296a1 runner/FeatureExtractionManager.cpp --- a/runner/FeatureExtractionManager.cpp Thu Oct 02 15:21:16 2014 +0100 +++ b/runner/FeatureExtractionManager.cpp Fri Oct 03 15:07:19 2014 +0100 @@ -14,6 +14,7 @@ */ #include "FeatureExtractionManager.h" +#include "MultiplexedReader.h" #include #include @@ -431,7 +432,7 @@ return addFeatureExtractor(transform, writers); } -void FeatureExtractionManager::addSource(QString audioSource) +void FeatureExtractionManager::addSource(QString audioSource, bool willMultiplex) { std::cerr << "Have audio source: \"" << audioSource.toStdString() << "\"" << std::endl; @@ -467,10 +468,12 @@ 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) { + if (m_channels == 0) { + m_channels = reader->getChannelCount(); + cerr << "Taking default channel count of " + << reader->getChannelCount() << " from file" << endl; + } } if (m_defaultSampleRate == 0) { @@ -482,9 +485,15 @@ m_readyReaders[audioSource] = reader; } + + if (willMultiplex) { + ++m_channels; // channel count is simply number of sources + cerr << "Multiplexing, incremented target channel count to " + << m_channels << endl; + } } -void FeatureExtractionManager::extractFeatures(QString audioSource, bool force) +void FeatureExtractionManager::extractFeatures(QString audioSource) { if (m_plugins.empty()) return; @@ -499,13 +508,45 @@ (audioSource, "internal error: have sources and plugins, but no channel count"); } + AudioFileReader *reader = prepareReader(audioSource); + extractFeaturesFor(reader, audioSource); // Note this also deletes reader +} + +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) +{ AudioFileReader *reader = 0; - - if (m_readyReaders.contains(audioSource)) { - reader = m_readyReaders[audioSource]; - m_readyReaders.remove(audioSource); - if (reader->getChannelCount() != m_channels || - reader->getSampleRate() != m_sampleRate) { + 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; @@ -513,23 +554,30 @@ } 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(source); + } + return reader; +} - if (!reader) { - throw FailedToOpenFile(audioSource); - } +void +FeatureExtractionManager::extractFeaturesFor(AudioFileReader *reader, + QString audioSource) +{ + // Note: This also deletes reader cerr << "Audio file \"" << audioSource.toStdString() << "\": " << reader->getChannelCount() << "ch at " << reader->getNativeRate() << "Hz" << endl; if (reader->getChannelCount() != m_channels || reader->getNativeRate() != m_sampleRate) { - cerr << "NOTE: File will be mixed or resampled for processing: " + cerr << "NOTE: File will be mixed or resampled for processing, to: " << m_channels << "ch at " << m_sampleRate << "Hz" << endl; } diff -r 74f7ad72fee6 -r 95de6db296a1 runner/FeatureExtractionManager.h --- a/runner/FeatureExtractionManager.h Thu Oct 02 15:21:16 2014 +0100 +++ b/runner/FeatureExtractionManager.h Fri Oct 03 15:07:19 2014 +0100 @@ -61,12 +61,17 @@ // 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); + void extractFeatures(QString audioSource); + + // 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 @@ -112,6 +117,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 74f7ad72fee6 -r 95de6db296a1 runner/MultiplexedReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/MultiplexedReader.cpp Fri Oct 03 15:07:19 2014 +0100 @@ -0,0 +1,106 @@ +/* -*- 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. +*/ + +#include "MultiplexedReader.h" + +MultiplexedReader::MultiplexedReader(QList readers) : + m_readers(readers) +{ + m_channelCount = readers.size(); + m_sampleRate = readers[0]->getSampleRate(); + + m_frameCount = 0; + m_quicklySeekable = true; + + foreach (AudioFileReader *r, m_readers) { + if (!r->isOK()) { + m_channelCount = 0; + m_error = r->getError(); + } else { + if (r->getFrameCount() > m_frameCount) { + m_frameCount = r->getFrameCount(); + } + if (!r->isQuicklySeekable()) { + m_quicklySeekable = false; + } + } + } +} + +MultiplexedReader::~MultiplexedReader() +{ + foreach (AudioFileReader *r, m_readers) { + delete r; + } +} + +void +MultiplexedReader::getInterleavedFrames(int start, int frameCount, + SampleBlock &block) const +{ + int out_chans = m_readers.size(); + + // Allocate and zero + block = SampleBlock(frameCount * out_chans, 0.f); + + for (int out_chan = 0; out_chan < out_chans; ++out_chan) { + + AudioFileReader *reader = m_readers[out_chan]; + SampleBlock readerBlock; + reader->getInterleavedFrames(start, frameCount, readerBlock); + + int in_chans = reader->getChannelCount(); + + for (int frame = 0; frame < frameCount; ++frame) { + + int out_index = frame * out_chans + out_chan; + + for (int in_chan = 0; in_chan < in_chans; ++in_chan) { + int in_index = frame * in_chans + in_chan; + if (in_index >= (int)readerBlock.size()) break; + block[out_index] += readerBlock[in_index]; + } + + if (in_chans > 1) { + block[out_index] /= float(in_chans); + } + } + } +} + +int +MultiplexedReader::getDecodeCompletion() const +{ + int completion = 100; + foreach (AudioFileReader *r, m_readers) { + int c = r->getDecodeCompletion(); + if (c < 100) { + completion = c; + } + } + return completion; +} + +bool +MultiplexedReader::isUpdating() const +{ + foreach (AudioFileReader *r, m_readers) { + if (r->isUpdating()) return true; + } + return false; +} + + + diff -r 74f7ad72fee6 -r 95de6db296a1 runner/MultiplexedReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runner/MultiplexedReader.h Fri Oct 03 15:07:19 2014 +0100 @@ -0,0 +1,49 @@ +/* -*- 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 + +class MultiplexedReader : public AudioFileReader +{ + Q_OBJECT + +public: + // I take ownership of readers + MultiplexedReader(QList readers); + virtual ~MultiplexedReader(); + + virtual QString getError() const { return m_error; } + virtual bool isQuicklySeekable() const { return m_quicklySeekable; } + + virtual void getInterleavedFrames(int start, int count, + SampleBlock &frames) const; + + virtual int getDecodeCompletion() const; + + virtual bool isUpdating() const; + +protected: + QString m_error; + bool m_quicklySeekable; + QList m_readers; +}; + +#endif diff -r 74f7ad72fee6 -r 95de6db296a1 runner/main.cpp --- a/runner/main.cpp Thu Oct 02 15:21:16 2014 +0100 +++ b/runner/main.cpp Fri Oct 03 15:07:19 2014 +0100 @@ -234,15 +234,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