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