diff src/vamp-hostsdk/hostext/PluginSummarisingAdapter.cpp @ 227:6b30e064cab7 distinct-libraries

* more moving
author cannam
date Thu, 06 Nov 2008 14:13:12 +0000
parents
children 5ee166dccfff
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vamp-hostsdk/hostext/PluginSummarisingAdapter.cpp	Thu Nov 06 14:13:12 2008 +0000
@@ -0,0 +1,913 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2008 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include "PluginSummarisingAdapter.h"
+
+#include <map>
+#include <algorithm>
+#include <cmath>
+#include <climits>
+
+#define DEBUG_PLUGIN_SUMMARISING_ADAPTER 1
+//#define DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT 1
+
+namespace Vamp {
+
+namespace HostExt {
+
+class PluginSummarisingAdapter::Impl
+{
+public:
+    Impl(Plugin *plugin, float inputSampleRate);
+    ~Impl();
+
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+
+    FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
+    FeatureSet getRemainingFeatures();
+
+    void setSummarySegmentBoundaries(const SegmentBoundaries &);
+
+    FeatureList getSummaryForOutput(int output,
+                                    SummaryType type,
+                                    AveragingMethod avg);
+
+    FeatureSet getSummaryForAllOutputs(SummaryType type,
+                                       AveragingMethod avg);
+
+protected:
+    Plugin *m_plugin;
+    float m_inputSampleRate;
+    size_t m_stepSize;
+    size_t m_blockSize;
+
+    SegmentBoundaries m_boundaries;
+
+    typedef std::vector<float> ValueList;
+
+    struct Result { // smaller than Feature
+        RealTime time;
+        RealTime duration;
+        ValueList values; // bin number -> value
+    };
+
+    typedef std::vector<Result> ResultList;
+
+    struct OutputAccumulator {
+        int bins;
+        ResultList results;
+        OutputAccumulator() : bins(0) { }
+    };
+
+    typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
+    OutputAccumulatorMap m_accumulators; // output number -> accumulator
+
+    typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap;
+    typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap;
+    OutputSegmentAccumulatorMap m_segmentedAccumulators; // output -> segmented
+
+    typedef std::map<int, RealTime> OutputTimestampMap;
+    OutputTimestampMap m_prevTimestamps; // output number -> timestamp
+    OutputTimestampMap m_prevDurations; // output number -> durations
+
+    struct OutputBinSummary {
+
+        int count;
+
+        // extents
+        double minimum;
+        double maximum;
+        double sum;
+
+        // sample-average results
+        double median;
+        double mode;
+        double variance;
+
+        // continuous-time average results
+        double median_c;
+        double mode_c;
+        double mean_c;
+        double variance_c;
+    };
+
+    typedef std::map<int, OutputBinSummary> OutputSummary;
+    typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
+    typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
+
+    OutputSummarySegmentMap m_summaries;
+
+    bool m_reduced;
+    RealTime m_endTime;
+
+    void accumulate(const FeatureSet &fs, RealTime, bool final);
+    void accumulate(int output, const Feature &f, RealTime, bool final);
+    void accumulateFinalDurations();
+    void findSegmentBounds(RealTime t, RealTime &start, RealTime &end);
+    void segment();
+    void reduce();
+
+    std::string getSummaryLabel(SummaryType type, AveragingMethod avg);
+};
+
+static RealTime INVALID_DURATION(INT_MIN, INT_MIN);
+    
+PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
+    PluginWrapper(plugin)
+{
+    m_impl = new Impl(plugin, m_inputSampleRate);
+}
+
+PluginSummarisingAdapter::~PluginSummarisingAdapter()
+{
+    delete m_impl;
+}
+
+bool
+PluginSummarisingAdapter::initialise(size_t channels,
+                                     size_t stepSize, size_t blockSize)
+{
+    return
+        PluginWrapper::initialise(channels, stepSize, blockSize) &&
+        m_impl->initialise(channels, stepSize, blockSize);
+}
+
+Plugin::FeatureSet
+PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
+{
+    return m_impl->process(inputBuffers, timestamp);
+}
+
+Plugin::FeatureSet
+PluginSummarisingAdapter::getRemainingFeatures()
+{
+    return m_impl->getRemainingFeatures();
+}
+
+void
+PluginSummarisingAdapter::setSummarySegmentBoundaries(const SegmentBoundaries &b)
+{
+    m_impl->setSummarySegmentBoundaries(b);
+}
+
+Plugin::FeatureList
+PluginSummarisingAdapter::getSummaryForOutput(int output,
+                                              SummaryType type,
+                                              AveragingMethod avg)
+{
+    return m_impl->getSummaryForOutput(output, type, avg);
+}
+
+Plugin::FeatureSet
+PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
+                                                  AveragingMethod avg)
+{
+    return m_impl->getSummaryForAllOutputs(type, avg);
+}
+
+PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
+    m_plugin(plugin),
+    m_inputSampleRate(inputSampleRate),
+    m_reduced(false)
+{
+}
+
+PluginSummarisingAdapter::Impl::~Impl()
+{
+}
+
+bool
+PluginSummarisingAdapter::Impl::initialise(size_t channels,
+                                           size_t stepSize, size_t blockSize)
+{
+    m_stepSize = stepSize;
+    m_blockSize = blockSize;
+    return true;
+}
+
+Plugin::FeatureSet
+PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers,
+                                        RealTime timestamp)
+{
+    if (m_reduced) {
+        std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
+    }
+    FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
+    accumulate(fs, timestamp, false);
+    m_endTime = timestamp + 
+        RealTime::frame2RealTime(m_stepSize, m_inputSampleRate);
+    return fs;
+}
+
+Plugin::FeatureSet
+PluginSummarisingAdapter::Impl::getRemainingFeatures()
+{
+    if (m_reduced) {
+        std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
+    }
+    FeatureSet fs = m_plugin->getRemainingFeatures();
+    accumulate(fs, m_endTime, true);
+    return fs;
+}
+
+void
+PluginSummarisingAdapter::Impl::setSummarySegmentBoundaries(const SegmentBoundaries &b)
+{
+    m_boundaries = b;
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+    std::cerr << "PluginSummarisingAdapter::setSummarySegmentBoundaries: boundaries are:" << std::endl;
+    for (SegmentBoundaries::const_iterator i = m_boundaries.begin();
+         i != m_boundaries.end(); ++i) {
+        std::cerr << *i << "  ";
+    }
+    std::cerr << std::endl;
+#endif
+}
+
+Plugin::FeatureList
+PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
+                                                    SummaryType type,
+                                                    AveragingMethod avg)
+{
+    if (!m_reduced) {
+        accumulateFinalDurations();
+        segment();
+        reduce();
+        m_reduced = true;
+    }
+
+    bool continuous = (avg == ContinuousTimeAverage);
+
+    FeatureList fl;
+    for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
+         i != m_summaries[output].end(); ++i) {
+
+        Feature f;
+
+        f.hasTimestamp = true;
+        f.timestamp = i->first;
+
+        f.hasDuration = true;
+        SummarySegmentMap::const_iterator ii = i;
+        if (++ii == m_summaries[output].end()) {
+            f.duration = m_endTime - f.timestamp;
+        } else {
+            f.duration = ii->first - f.timestamp;
+        }
+
+        f.label = getSummaryLabel(type, avg);
+
+        for (OutputSummary::const_iterator j = i->second.begin();
+             j != i->second.end(); ++j) {
+
+            // these will be ordered by bin number, and no bin numbers
+            // will be missing except at the end (because of the way
+            // the accumulators were initially filled in accumulate())
+
+            const OutputBinSummary &summary = j->second;
+            double result = 0.f;
+
+            switch (type) {
+
+            case Minimum:
+                result = summary.minimum;
+                break;
+
+            case Maximum:
+                result = summary.maximum;
+                break;
+
+            case Mean:
+                if (continuous) {
+                    result = summary.mean_c;
+                } else if (summary.count) {
+                    result = summary.sum / summary.count;
+                }
+                break;
+
+            case Median:
+                if (continuous) result = summary.median_c;
+                else result = summary.median;
+                break;
+
+            case Mode:
+                if (continuous) result = summary.mode_c;
+                else result = summary.mode;
+                break;
+
+            case Sum:
+                result = summary.sum;
+                break;
+
+            case Variance:
+                if (continuous) result = summary.variance_c;
+                else result = summary.variance;
+                break;
+
+            case StandardDeviation:
+                if (continuous) result = sqrtf(summary.variance_c);
+                else result = sqrtf(summary.variance);
+                break;
+
+            case Count:
+                result = summary.count;
+                break;
+
+            case UnknownSummaryType:
+                break;
+
+            default:
+                break;
+            }
+            
+            f.values.push_back(result);
+        }
+
+        fl.push_back(f);
+    }
+    return fl;
+}
+
+Plugin::FeatureSet
+PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
+                                                        AveragingMethod avg)
+{
+    if (!m_reduced) {
+        accumulateFinalDurations();
+        segment();
+        reduce();
+        m_reduced = true;
+    }
+
+    FeatureSet fs;
+    for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
+         i != m_summaries.end(); ++i) {
+        fs[i->first] = getSummaryForOutput(i->first, type, avg);
+    }
+    return fs;
+}
+
+void
+PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
+                                           RealTime timestamp, 
+                                           bool final)
+{
+    for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
+        for (FeatureList::const_iterator j = i->second.begin();
+             j != i->second.end(); ++j) {
+            if (j->hasTimestamp) {
+                accumulate(i->first, *j, j->timestamp, final);
+            } else {
+                //!!! is this correct?
+                accumulate(i->first, *j, timestamp, final);
+            }
+        }
+    }
+}
+
+std::string
+PluginSummarisingAdapter::Impl::getSummaryLabel(SummaryType type,
+                                                AveragingMethod avg)
+{
+    std::string label;
+    std::string avglabel;
+
+    if (avg == SampleAverage) avglabel = ", sample average";
+    else avglabel = ", continuous-time average";
+
+    switch (type) {
+    case Minimum:  label = "(minimum value)"; break;
+    case Maximum:  label = "(maximum value)"; break;
+    case Mean:     label = "(mean value" + avglabel + ")"; break;
+    case Median:   label = "(median value" + avglabel + ")"; break;
+    case Mode:     label = "(modal value" + avglabel + ")"; break;
+    case Sum:      label = "(sum)"; break;
+    case Variance: label = "(variance" + avglabel + ")"; break;
+    case StandardDeviation: label = "(standard deviation" + avglabel + ")"; break;
+    case Count:    label = "(count)"; break;
+    case UnknownSummaryType: label = "(unknown summary)"; break;
+    }
+    
+    return label;
+}
+
+void
+PluginSummarisingAdapter::Impl::accumulate(int output,
+                                           const Feature &f,
+                                           RealTime timestamp,
+                                           bool final)
+{
+//!!! to do: use timestamp to determine which segment we're on
+    
+//!!! What should happen if a feature's duration spans a segment
+// boundary?  I think we probably want to chop it, and pretend that it
+// appears in both -- don't we? do we?  A very long feature (e.g. key,
+// if the whole audio is in a single key) might span many or all
+// segments, and we want that to be reflected in the results (e.g. it
+// is the modal key in all of those segments, not just the first).
+// That is actually quite complicated to do!
+
+//!!! This affects how we record things.  If features spanning a
+// boundary should be chopped, then we need to have per-segment
+// accumulators (and the feature value goes into both -- perhaps we
+// need a separate phase to split the accumulator up into segments).
+// If features spanning a boundary should be counted only in the first
+// segment, with their full duration, then we should store them in a
+// single accumulator and distribute into segments only on reduce.
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+    std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl;
+#endif
+
+    // At each process step, accumulate() is called once for each
+    // feature on each output within that process's returned feature
+    // list, and with the timestamp passed in being that of the start
+    // of the process block.
+
+    // At the end (in getRemainingFeatures), accumulate() is called
+    // once for each feature on each output within the feature list
+    // returned by getRemainingFeatures, and with the timestamp being
+    // the same as the last process block and final set to true.
+
+    // (What if getRemainingFeatures doesn't return any features?  We
+    // still need to ensure that the final duration is written.  Need
+    // a separate function to close the durations.)
+
+    // At each call, we pull out the value for the feature and stuff
+    // it into the accumulator's appropriate values array; and we
+    // calculate the duration for the _previous_ feature, or pull it
+    // from the prevDurations array if the previous feature had a
+    // duration in its structure, and stuff that into the
+    // accumulator's appropriate durations array.
+
+    if (m_prevDurations.find(output) != m_prevDurations.end()) {
+
+        // Not the first time accumulate has been called for this
+        // output -- there has been a previous feature
+
+        RealTime prevDuration;
+
+        // Note that m_prevDurations[output] only contains the
+        // duration field that was contained in the previous feature.
+        // If it didn't have an explicit duration,
+        // m_prevDurations[output] should be INVALID_DURATION and we
+        // will have to calculate the duration from the previous and
+        // current timestamps.
+
+        if (m_prevDurations[output] != INVALID_DURATION) {
+            prevDuration = m_prevDurations[output];
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+            std::cerr << "Previous duration from previous feature: " << prevDuration << std::endl;
+#endif
+        } else {
+            prevDuration = timestamp - m_prevTimestamps[output];
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+            std::cerr << "Previous duration from diff: " << timestamp << " - "
+                      << m_prevTimestamps[output] << std::endl;
+#endif
+        }
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+        std::cerr << "output " << output << ": ";
+        std::cerr << "Pushing previous duration as " << prevDuration << std::endl;
+#endif
+        
+        m_accumulators[output].results
+            [m_accumulators[output].results.size() - 1]
+            .duration = prevDuration;
+    }
+
+    if (f.hasDuration) m_prevDurations[output] = f.duration;
+    else m_prevDurations[output] = INVALID_DURATION;
+
+    m_prevTimestamps[output] = timestamp;
+
+    if (f.hasDuration) {
+        RealTime et = timestamp;
+        et = et + f.duration;
+        if (et > m_endTime) m_endTime = et;
+    }
+
+    Result result;
+    result.time = timestamp;
+    result.duration = INVALID_DURATION;
+
+    if (f.values.size() > m_accumulators[output].bins) {
+        m_accumulators[output].bins = f.values.size();
+    }
+
+    for (int i = 0; i < int(f.values.size()); ++i) {
+        result.values.push_back(f.values[i]);
+    }
+
+    m_accumulators[output].results.push_back(result);
+}
+
+void
+PluginSummarisingAdapter::Impl::accumulateFinalDurations()
+{
+    for (OutputTimestampMap::iterator i = m_prevTimestamps.begin();
+         i != m_prevTimestamps.end(); ++i) {
+
+        int output = i->first;
+
+        int acount = m_accumulators[output].results.size();
+
+        if (acount == 0) continue;
+
+        RealTime prevTimestamp = i->second;
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+        std::cerr << "output " << output << ": ";
+#endif
+
+        if (m_prevDurations.find(output) != m_prevDurations.end() &&
+            m_prevDurations[output] != INVALID_DURATION) {
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+            std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl;
+#endif
+
+            m_accumulators[output].results[acount - 1].duration =
+                m_prevDurations[output];
+
+        } else {
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+            std::cerr << "Pushing final duration from diff as " << m_endTime << " - " << m_prevTimestamps[output] << std::endl;
+#endif
+
+            m_accumulators[output].results[acount - 1].duration =
+                m_endTime - m_prevTimestamps[output];
+        }
+        
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+        std::cerr << "so duration for result no " << acount-1 << " is "
+                  << m_accumulators[output].results[acount-1].duration
+                  << std::endl;
+#endif
+    }
+}
+
+void
+PluginSummarisingAdapter::Impl::findSegmentBounds(RealTime t,
+                                                  RealTime &start,
+                                                  RealTime &end)
+{
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
+    std::cerr << "findSegmentBounds: t = " << t <<  std::endl;
+#endif
+
+    SegmentBoundaries::const_iterator i = std::upper_bound
+        (m_boundaries.begin(), m_boundaries.end(), t);
+
+    start = RealTime::zeroTime;
+    end = m_endTime;
+
+    if (i != m_boundaries.end()) {
+        end = *i;
+    }
+
+    if (i != m_boundaries.begin()) {
+        start = *--i;
+    }
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
+    std::cerr << "findSegmentBounds: " << t << " is in segment " << start << " -> " << end << std::endl;
+#endif
+}
+
+void
+PluginSummarisingAdapter::Impl::segment()
+{
+    SegmentBoundaries::iterator boundaryitr = m_boundaries.begin();
+    RealTime segmentStart = RealTime::zeroTime;
+    
+    for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
+         i != m_accumulators.end(); ++i) {
+
+        int output = i->first;
+        OutputAccumulator &source = i->second;
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
+        std::cerr << "segment: total results for output " << output << " = "
+                  << source.results.size() << std::endl;
+#endif
+
+        //!!! This is basically nonsense if the results have no values
+        //!!! (i.e. their times and counts are the only things of
+        //!!! interest) but perhaps it's the user's problem if they
+        //!!! ask for segmentation in that case
+
+        for (int n = 0; n < source.results.size(); ++n) {
+            
+            // This result spans source.results[n].time to
+            // source.results[n].time + source.results[n].duration.
+            // We need to dispose it into segments appropriately
+
+            RealTime resultStart = source.results[n].time;
+            RealTime resultEnd = resultStart + source.results[n].duration;
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
+            std::cerr << "output: " << output << ", result start = " << resultStart << ", end = " << resultEnd << std::endl;
+#endif
+
+            RealTime segmentStart = RealTime::zeroTime;
+            RealTime segmentEnd = resultEnd - RealTime(1, 0);
+            
+            while (segmentEnd < resultEnd) {
+
+                findSegmentBounds(resultStart, segmentStart, segmentEnd);
+                
+                RealTime chunkStart = resultStart;
+                if (chunkStart < segmentStart) chunkStart = segmentStart;
+
+                RealTime chunkEnd = resultEnd;
+                if (chunkEnd > segmentEnd) chunkEnd = segmentEnd;
+                
+                m_segmentedAccumulators[output][segmentStart].bins = source.bins;
+
+                Result chunk;
+                chunk.time = chunkStart;
+                chunk.duration = chunkEnd - chunkStart;
+                chunk.values = source.results[n].values;
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
+                std::cerr << "chunk for segment " << segmentStart << ": from " << chunk.time << ", duration " << chunk.duration << std::endl;
+#endif
+
+                m_segmentedAccumulators[output][segmentStart].results
+                    .push_back(chunk);
+
+                resultStart = chunkEnd;
+            }
+        }
+    }
+}
+
+struct ValueDurationFloatPair
+{
+    float value;
+    float duration;
+
+    ValueDurationFloatPair() : value(0), duration(0) { }
+    ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
+    ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
+        value = p.value;
+        duration = p.duration;
+        return *this;
+    }
+    bool operator<(const ValueDurationFloatPair &p) const {
+        return value < p.value;
+    }
+};
+
+static double toSec(const RealTime &r)
+{
+    return r.sec + double(r.nsec) / 1000000000.0;
+}
+
+void
+PluginSummarisingAdapter::Impl::reduce()
+{
+    for (OutputSegmentAccumulatorMap::iterator i =
+             m_segmentedAccumulators.begin();
+         i != m_segmentedAccumulators.end(); ++i) {
+
+        int output = i->first;
+        SegmentAccumulatorMap &segments = i->second;
+
+        for (SegmentAccumulatorMap::iterator j = segments.begin();
+             j != segments.end(); ++j) {
+
+            RealTime segmentStart = j->first;
+            OutputAccumulator &accumulator = j->second;
+
+            int sz = accumulator.results.size();
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+            std::cerr << "reduce: segment starting at " << segmentStart
+                      << " on output " << output << " has " << sz << " result(s)" << std::endl;
+#endif
+
+            double totalDuration = 0.0;
+            //!!! is this right?
+            if (sz > 0) {
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+                std::cerr << "last time = " << accumulator.results[sz-1].time 
+                          << ", duration = " << accumulator.results[sz-1].duration
+                          << " (step = " << m_stepSize << ", block = " << m_blockSize << ")"
+                          << std::endl;
+#endif
+                totalDuration = toSec((accumulator.results[sz-1].time +
+                                       accumulator.results[sz-1].duration) -
+                                      segmentStart);
+            }
+
+            for (int bin = 0; bin < accumulator.bins; ++bin) {
+
+                // work on all values over time for a single bin
+
+                OutputBinSummary summary;
+
+                summary.count = sz;
+
+                summary.minimum = 0.f;
+                summary.maximum = 0.f;
+
+                summary.median = 0.f;
+                summary.mode = 0.f;
+                summary.sum = 0.f;
+                summary.variance = 0.f;
+
+                summary.median_c = 0.f;
+                summary.mode_c = 0.f;
+                summary.mean_c = 0.f;
+                summary.variance_c = 0.f;
+
+                if (sz == 0) continue;
+
+                std::vector<ValueDurationFloatPair> valvec;
+
+                for (int k = 0; k < sz; ++k) {
+                    while (accumulator.results[k].values.size() <
+                           accumulator.bins) {
+                        accumulator.results[k].values.push_back(0.f);
+                    }
+                }
+
+                for (int k = 0; k < sz; ++k) {
+                    float value = accumulator.results[k].values[bin];
+                    valvec.push_back(ValueDurationFloatPair
+                                     (value,
+                                      toSec(accumulator.results[k].duration)));
+                }
+
+                std::sort(valvec.begin(), valvec.end());
+
+                summary.minimum = valvec[0].value;
+                summary.maximum = valvec[sz-1].value;
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+                std::cerr << "total duration = " << totalDuration << std::endl;
+#endif
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+/*
+                std::cerr << "value vector for medians:" << std::endl;
+                for (int k = 0; k < sz; ++k) {
+                    std::cerr << "(" << valvec[k].value << "," << valvec[k].duration << ") ";
+                }
+                std::cerr << std::endl;
+*/
+#endif
+
+                if (sz % 2 == 1) {
+                    summary.median = valvec[sz/2].value;
+                } else {
+                    summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
+                }
+            
+                double duracc = 0.0;
+                summary.median_c = valvec[sz-1].value;
+
+                for (int k = 0; k < sz; ++k) {
+                    duracc += valvec[k].duration;
+                    if (duracc > totalDuration/2) {
+                        summary.median_c = valvec[k].value;
+                        break;
+                    }
+                }
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+                std::cerr << "median_c = " << summary.median_c << std::endl;
+                std::cerr << "median = " << summary.median << std::endl;
+#endif
+                
+                std::map<float, int> distribution;
+
+                for (int k = 0; k < sz; ++k) {
+                    summary.sum += accumulator.results[k].values[bin];
+                    distribution[accumulator.results[k].values[bin]] += 1;
+                }
+
+                int md = 0;
+
+                for (std::map<float, int>::iterator di = distribution.begin();
+                     di != distribution.end(); ++di) {
+                    if (di->second > md) {
+                        md = di->second;
+                        summary.mode = di->first;
+                    }
+                }
+
+                distribution.clear();
+
+                std::map<float, double> distribution_c;
+
+                for (int k = 0; k < sz; ++k) {
+                    distribution_c[accumulator.results[k].values[bin]]
+                        += toSec(accumulator.results[k].duration);
+                }
+
+                double mrd = 0.0;
+
+                for (std::map<float, double>::iterator di = distribution_c.begin();
+                     di != distribution_c.end(); ++di) {
+                    if (di->second > mrd) {
+                        mrd = di->second;
+                        summary.mode_c = di->first;
+                    }
+                }
+
+                distribution_c.clear();
+
+                if (totalDuration > 0.0) {
+
+                    double sum_c = 0.0;
+
+                    for (int k = 0; k < sz; ++k) {
+                        double value = accumulator.results[k].values[bin]
+                            * toSec(accumulator.results[k].duration);
+                        sum_c += value;
+                    }
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+                    std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
+                              << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl;
+#endif
+                
+                    summary.mean_c = sum_c / totalDuration;
+
+                    for (int k = 0; k < sz; ++k) {
+                        double value = accumulator.results[k].values[bin];
+//                            * toSec(accumulator.results[k].duration);
+                        summary.variance_c +=
+                            (value - summary.mean_c) * (value - summary.mean_c)
+                            * toSec(accumulator.results[k].duration);
+                    }
+
+//                    summary.variance_c /= summary.count;
+                    summary.variance_c /= totalDuration;
+                }
+
+                double mean = summary.sum / summary.count;
+
+#ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
+                std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
+                          << summary.sum / summary.count << std::endl;
+#endif
+
+                for (int k = 0; k < sz; ++k) {
+                    float value = accumulator.results[k].values[bin];
+                    summary.variance += (value - mean) * (value - mean);
+                }
+                summary.variance /= summary.count;
+
+                m_summaries[output][segmentStart][bin] = summary;
+            }
+        }
+    }
+
+    m_segmentedAccumulators.clear();
+    m_accumulators.clear();
+}
+
+
+}
+
+}
+