annotate src/vamp-hostsdk/hostext/PluginSummarisingAdapter.cpp @ 229:3451f7dfa2be distinct-libraries

* more moving
author cannam
date Thu, 06 Nov 2008 16:55:15 +0000
parents 6b30e064cab7
children 5ee166dccfff
rev   line source
cannam@227 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@227 2
cannam@227 3 /*
cannam@227 4 Vamp
cannam@227 5
cannam@227 6 An API for audio analysis and feature extraction plugins.
cannam@227 7
cannam@227 8 Centre for Digital Music, Queen Mary, University of London.
cannam@227 9 Copyright 2006-2008 Chris Cannam and QMUL.
cannam@227 10
cannam@227 11 Permission is hereby granted, free of charge, to any person
cannam@227 12 obtaining a copy of this software and associated documentation
cannam@227 13 files (the "Software"), to deal in the Software without
cannam@227 14 restriction, including without limitation the rights to use, copy,
cannam@227 15 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@227 16 of the Software, and to permit persons to whom the Software is
cannam@227 17 furnished to do so, subject to the following conditions:
cannam@227 18
cannam@227 19 The above copyright notice and this permission notice shall be
cannam@227 20 included in all copies or substantial portions of the Software.
cannam@227 21
cannam@227 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@227 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@227 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@227 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@227 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@227 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@227 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@227 29
cannam@227 30 Except as contained in this notice, the names of the Centre for
cannam@227 31 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@227 32 shall not be used in advertising or otherwise to promote the sale,
cannam@227 33 use or other dealings in this Software without prior written
cannam@227 34 authorization.
cannam@227 35 */
cannam@227 36
cannam@227 37 #include "PluginSummarisingAdapter.h"
cannam@227 38
cannam@227 39 #include <map>
cannam@227 40 #include <algorithm>
cannam@227 41 #include <cmath>
cannam@227 42 #include <climits>
cannam@227 43
cannam@227 44 #define DEBUG_PLUGIN_SUMMARISING_ADAPTER 1
cannam@227 45 //#define DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT 1
cannam@227 46
cannam@227 47 namespace Vamp {
cannam@227 48
cannam@227 49 namespace HostExt {
cannam@227 50
cannam@227 51 class PluginSummarisingAdapter::Impl
cannam@227 52 {
cannam@227 53 public:
cannam@227 54 Impl(Plugin *plugin, float inputSampleRate);
cannam@227 55 ~Impl();
cannam@227 56
cannam@227 57 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
cannam@227 58
cannam@227 59 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
cannam@227 60 FeatureSet getRemainingFeatures();
cannam@227 61
cannam@227 62 void setSummarySegmentBoundaries(const SegmentBoundaries &);
cannam@227 63
cannam@227 64 FeatureList getSummaryForOutput(int output,
cannam@227 65 SummaryType type,
cannam@227 66 AveragingMethod avg);
cannam@227 67
cannam@227 68 FeatureSet getSummaryForAllOutputs(SummaryType type,
cannam@227 69 AveragingMethod avg);
cannam@227 70
cannam@227 71 protected:
cannam@227 72 Plugin *m_plugin;
cannam@227 73 float m_inputSampleRate;
cannam@227 74 size_t m_stepSize;
cannam@227 75 size_t m_blockSize;
cannam@227 76
cannam@227 77 SegmentBoundaries m_boundaries;
cannam@227 78
cannam@227 79 typedef std::vector<float> ValueList;
cannam@227 80
cannam@227 81 struct Result { // smaller than Feature
cannam@227 82 RealTime time;
cannam@227 83 RealTime duration;
cannam@227 84 ValueList values; // bin number -> value
cannam@227 85 };
cannam@227 86
cannam@227 87 typedef std::vector<Result> ResultList;
cannam@227 88
cannam@227 89 struct OutputAccumulator {
cannam@227 90 int bins;
cannam@227 91 ResultList results;
cannam@227 92 OutputAccumulator() : bins(0) { }
cannam@227 93 };
cannam@227 94
cannam@227 95 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
cannam@227 96 OutputAccumulatorMap m_accumulators; // output number -> accumulator
cannam@227 97
cannam@227 98 typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap;
cannam@227 99 typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap;
cannam@227 100 OutputSegmentAccumulatorMap m_segmentedAccumulators; // output -> segmented
cannam@227 101
cannam@227 102 typedef std::map<int, RealTime> OutputTimestampMap;
cannam@227 103 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
cannam@227 104 OutputTimestampMap m_prevDurations; // output number -> durations
cannam@227 105
cannam@227 106 struct OutputBinSummary {
cannam@227 107
cannam@227 108 int count;
cannam@227 109
cannam@227 110 // extents
cannam@227 111 double minimum;
cannam@227 112 double maximum;
cannam@227 113 double sum;
cannam@227 114
cannam@227 115 // sample-average results
cannam@227 116 double median;
cannam@227 117 double mode;
cannam@227 118 double variance;
cannam@227 119
cannam@227 120 // continuous-time average results
cannam@227 121 double median_c;
cannam@227 122 double mode_c;
cannam@227 123 double mean_c;
cannam@227 124 double variance_c;
cannam@227 125 };
cannam@227 126
cannam@227 127 typedef std::map<int, OutputBinSummary> OutputSummary;
cannam@227 128 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
cannam@227 129 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
cannam@227 130
cannam@227 131 OutputSummarySegmentMap m_summaries;
cannam@227 132
cannam@227 133 bool m_reduced;
cannam@227 134 RealTime m_endTime;
cannam@227 135
cannam@227 136 void accumulate(const FeatureSet &fs, RealTime, bool final);
cannam@227 137 void accumulate(int output, const Feature &f, RealTime, bool final);
cannam@227 138 void accumulateFinalDurations();
cannam@227 139 void findSegmentBounds(RealTime t, RealTime &start, RealTime &end);
cannam@227 140 void segment();
cannam@227 141 void reduce();
cannam@227 142
cannam@227 143 std::string getSummaryLabel(SummaryType type, AveragingMethod avg);
cannam@227 144 };
cannam@227 145
cannam@227 146 static RealTime INVALID_DURATION(INT_MIN, INT_MIN);
cannam@227 147
cannam@227 148 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
cannam@227 149 PluginWrapper(plugin)
cannam@227 150 {
cannam@227 151 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@227 152 }
cannam@227 153
cannam@227 154 PluginSummarisingAdapter::~PluginSummarisingAdapter()
cannam@227 155 {
cannam@227 156 delete m_impl;
cannam@227 157 }
cannam@227 158
cannam@227 159 bool
cannam@227 160 PluginSummarisingAdapter::initialise(size_t channels,
cannam@227 161 size_t stepSize, size_t blockSize)
cannam@227 162 {
cannam@227 163 return
cannam@227 164 PluginWrapper::initialise(channels, stepSize, blockSize) &&
cannam@227 165 m_impl->initialise(channels, stepSize, blockSize);
cannam@227 166 }
cannam@227 167
cannam@227 168 Plugin::FeatureSet
cannam@227 169 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
cannam@227 170 {
cannam@227 171 return m_impl->process(inputBuffers, timestamp);
cannam@227 172 }
cannam@227 173
cannam@227 174 Plugin::FeatureSet
cannam@227 175 PluginSummarisingAdapter::getRemainingFeatures()
cannam@227 176 {
cannam@227 177 return m_impl->getRemainingFeatures();
cannam@227 178 }
cannam@227 179
cannam@227 180 void
cannam@227 181 PluginSummarisingAdapter::setSummarySegmentBoundaries(const SegmentBoundaries &b)
cannam@227 182 {
cannam@227 183 m_impl->setSummarySegmentBoundaries(b);
cannam@227 184 }
cannam@227 185
cannam@227 186 Plugin::FeatureList
cannam@227 187 PluginSummarisingAdapter::getSummaryForOutput(int output,
cannam@227 188 SummaryType type,
cannam@227 189 AveragingMethod avg)
cannam@227 190 {
cannam@227 191 return m_impl->getSummaryForOutput(output, type, avg);
cannam@227 192 }
cannam@227 193
cannam@227 194 Plugin::FeatureSet
cannam@227 195 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
cannam@227 196 AveragingMethod avg)
cannam@227 197 {
cannam@227 198 return m_impl->getSummaryForAllOutputs(type, avg);
cannam@227 199 }
cannam@227 200
cannam@227 201 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@227 202 m_plugin(plugin),
cannam@227 203 m_inputSampleRate(inputSampleRate),
cannam@227 204 m_reduced(false)
cannam@227 205 {
cannam@227 206 }
cannam@227 207
cannam@227 208 PluginSummarisingAdapter::Impl::~Impl()
cannam@227 209 {
cannam@227 210 }
cannam@227 211
cannam@227 212 bool
cannam@227 213 PluginSummarisingAdapter::Impl::initialise(size_t channels,
cannam@227 214 size_t stepSize, size_t blockSize)
cannam@227 215 {
cannam@227 216 m_stepSize = stepSize;
cannam@227 217 m_blockSize = blockSize;
cannam@227 218 return true;
cannam@227 219 }
cannam@227 220
cannam@227 221 Plugin::FeatureSet
cannam@227 222 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers,
cannam@227 223 RealTime timestamp)
cannam@227 224 {
cannam@227 225 if (m_reduced) {
cannam@227 226 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
cannam@227 227 }
cannam@227 228 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
cannam@227 229 accumulate(fs, timestamp, false);
cannam@227 230 m_endTime = timestamp +
cannam@227 231 RealTime::frame2RealTime(m_stepSize, m_inputSampleRate);
cannam@227 232 return fs;
cannam@227 233 }
cannam@227 234
cannam@227 235 Plugin::FeatureSet
cannam@227 236 PluginSummarisingAdapter::Impl::getRemainingFeatures()
cannam@227 237 {
cannam@227 238 if (m_reduced) {
cannam@227 239 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
cannam@227 240 }
cannam@227 241 FeatureSet fs = m_plugin->getRemainingFeatures();
cannam@227 242 accumulate(fs, m_endTime, true);
cannam@227 243 return fs;
cannam@227 244 }
cannam@227 245
cannam@227 246 void
cannam@227 247 PluginSummarisingAdapter::Impl::setSummarySegmentBoundaries(const SegmentBoundaries &b)
cannam@227 248 {
cannam@227 249 m_boundaries = b;
cannam@227 250 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 251 std::cerr << "PluginSummarisingAdapter::setSummarySegmentBoundaries: boundaries are:" << std::endl;
cannam@227 252 for (SegmentBoundaries::const_iterator i = m_boundaries.begin();
cannam@227 253 i != m_boundaries.end(); ++i) {
cannam@227 254 std::cerr << *i << " ";
cannam@227 255 }
cannam@227 256 std::cerr << std::endl;
cannam@227 257 #endif
cannam@227 258 }
cannam@227 259
cannam@227 260 Plugin::FeatureList
cannam@227 261 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
cannam@227 262 SummaryType type,
cannam@227 263 AveragingMethod avg)
cannam@227 264 {
cannam@227 265 if (!m_reduced) {
cannam@227 266 accumulateFinalDurations();
cannam@227 267 segment();
cannam@227 268 reduce();
cannam@227 269 m_reduced = true;
cannam@227 270 }
cannam@227 271
cannam@227 272 bool continuous = (avg == ContinuousTimeAverage);
cannam@227 273
cannam@227 274 FeatureList fl;
cannam@227 275 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
cannam@227 276 i != m_summaries[output].end(); ++i) {
cannam@227 277
cannam@227 278 Feature f;
cannam@227 279
cannam@227 280 f.hasTimestamp = true;
cannam@227 281 f.timestamp = i->first;
cannam@227 282
cannam@227 283 f.hasDuration = true;
cannam@227 284 SummarySegmentMap::const_iterator ii = i;
cannam@227 285 if (++ii == m_summaries[output].end()) {
cannam@227 286 f.duration = m_endTime - f.timestamp;
cannam@227 287 } else {
cannam@227 288 f.duration = ii->first - f.timestamp;
cannam@227 289 }
cannam@227 290
cannam@227 291 f.label = getSummaryLabel(type, avg);
cannam@227 292
cannam@227 293 for (OutputSummary::const_iterator j = i->second.begin();
cannam@227 294 j != i->second.end(); ++j) {
cannam@227 295
cannam@227 296 // these will be ordered by bin number, and no bin numbers
cannam@227 297 // will be missing except at the end (because of the way
cannam@227 298 // the accumulators were initially filled in accumulate())
cannam@227 299
cannam@227 300 const OutputBinSummary &summary = j->second;
cannam@227 301 double result = 0.f;
cannam@227 302
cannam@227 303 switch (type) {
cannam@227 304
cannam@227 305 case Minimum:
cannam@227 306 result = summary.minimum;
cannam@227 307 break;
cannam@227 308
cannam@227 309 case Maximum:
cannam@227 310 result = summary.maximum;
cannam@227 311 break;
cannam@227 312
cannam@227 313 case Mean:
cannam@227 314 if (continuous) {
cannam@227 315 result = summary.mean_c;
cannam@227 316 } else if (summary.count) {
cannam@227 317 result = summary.sum / summary.count;
cannam@227 318 }
cannam@227 319 break;
cannam@227 320
cannam@227 321 case Median:
cannam@227 322 if (continuous) result = summary.median_c;
cannam@227 323 else result = summary.median;
cannam@227 324 break;
cannam@227 325
cannam@227 326 case Mode:
cannam@227 327 if (continuous) result = summary.mode_c;
cannam@227 328 else result = summary.mode;
cannam@227 329 break;
cannam@227 330
cannam@227 331 case Sum:
cannam@227 332 result = summary.sum;
cannam@227 333 break;
cannam@227 334
cannam@227 335 case Variance:
cannam@227 336 if (continuous) result = summary.variance_c;
cannam@227 337 else result = summary.variance;
cannam@227 338 break;
cannam@227 339
cannam@227 340 case StandardDeviation:
cannam@227 341 if (continuous) result = sqrtf(summary.variance_c);
cannam@227 342 else result = sqrtf(summary.variance);
cannam@227 343 break;
cannam@227 344
cannam@227 345 case Count:
cannam@227 346 result = summary.count;
cannam@227 347 break;
cannam@227 348
cannam@227 349 case UnknownSummaryType:
cannam@227 350 break;
cannam@227 351
cannam@227 352 default:
cannam@227 353 break;
cannam@227 354 }
cannam@227 355
cannam@227 356 f.values.push_back(result);
cannam@227 357 }
cannam@227 358
cannam@227 359 fl.push_back(f);
cannam@227 360 }
cannam@227 361 return fl;
cannam@227 362 }
cannam@227 363
cannam@227 364 Plugin::FeatureSet
cannam@227 365 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
cannam@227 366 AveragingMethod avg)
cannam@227 367 {
cannam@227 368 if (!m_reduced) {
cannam@227 369 accumulateFinalDurations();
cannam@227 370 segment();
cannam@227 371 reduce();
cannam@227 372 m_reduced = true;
cannam@227 373 }
cannam@227 374
cannam@227 375 FeatureSet fs;
cannam@227 376 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
cannam@227 377 i != m_summaries.end(); ++i) {
cannam@227 378 fs[i->first] = getSummaryForOutput(i->first, type, avg);
cannam@227 379 }
cannam@227 380 return fs;
cannam@227 381 }
cannam@227 382
cannam@227 383 void
cannam@227 384 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
cannam@227 385 RealTime timestamp,
cannam@227 386 bool final)
cannam@227 387 {
cannam@227 388 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
cannam@227 389 for (FeatureList::const_iterator j = i->second.begin();
cannam@227 390 j != i->second.end(); ++j) {
cannam@227 391 if (j->hasTimestamp) {
cannam@227 392 accumulate(i->first, *j, j->timestamp, final);
cannam@227 393 } else {
cannam@227 394 //!!! is this correct?
cannam@227 395 accumulate(i->first, *j, timestamp, final);
cannam@227 396 }
cannam@227 397 }
cannam@227 398 }
cannam@227 399 }
cannam@227 400
cannam@227 401 std::string
cannam@227 402 PluginSummarisingAdapter::Impl::getSummaryLabel(SummaryType type,
cannam@227 403 AveragingMethod avg)
cannam@227 404 {
cannam@227 405 std::string label;
cannam@227 406 std::string avglabel;
cannam@227 407
cannam@227 408 if (avg == SampleAverage) avglabel = ", sample average";
cannam@227 409 else avglabel = ", continuous-time average";
cannam@227 410
cannam@227 411 switch (type) {
cannam@227 412 case Minimum: label = "(minimum value)"; break;
cannam@227 413 case Maximum: label = "(maximum value)"; break;
cannam@227 414 case Mean: label = "(mean value" + avglabel + ")"; break;
cannam@227 415 case Median: label = "(median value" + avglabel + ")"; break;
cannam@227 416 case Mode: label = "(modal value" + avglabel + ")"; break;
cannam@227 417 case Sum: label = "(sum)"; break;
cannam@227 418 case Variance: label = "(variance" + avglabel + ")"; break;
cannam@227 419 case StandardDeviation: label = "(standard deviation" + avglabel + ")"; break;
cannam@227 420 case Count: label = "(count)"; break;
cannam@227 421 case UnknownSummaryType: label = "(unknown summary)"; break;
cannam@227 422 }
cannam@227 423
cannam@227 424 return label;
cannam@227 425 }
cannam@227 426
cannam@227 427 void
cannam@227 428 PluginSummarisingAdapter::Impl::accumulate(int output,
cannam@227 429 const Feature &f,
cannam@227 430 RealTime timestamp,
cannam@227 431 bool final)
cannam@227 432 {
cannam@227 433 //!!! to do: use timestamp to determine which segment we're on
cannam@227 434
cannam@227 435 //!!! What should happen if a feature's duration spans a segment
cannam@227 436 // boundary? I think we probably want to chop it, and pretend that it
cannam@227 437 // appears in both -- don't we? do we? A very long feature (e.g. key,
cannam@227 438 // if the whole audio is in a single key) might span many or all
cannam@227 439 // segments, and we want that to be reflected in the results (e.g. it
cannam@227 440 // is the modal key in all of those segments, not just the first).
cannam@227 441 // That is actually quite complicated to do!
cannam@227 442
cannam@227 443 //!!! This affects how we record things. If features spanning a
cannam@227 444 // boundary should be chopped, then we need to have per-segment
cannam@227 445 // accumulators (and the feature value goes into both -- perhaps we
cannam@227 446 // need a separate phase to split the accumulator up into segments).
cannam@227 447 // If features spanning a boundary should be counted only in the first
cannam@227 448 // segment, with their full duration, then we should store them in a
cannam@227 449 // single accumulator and distribute into segments only on reduce.
cannam@227 450
cannam@227 451 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 452 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl;
cannam@227 453 #endif
cannam@227 454
cannam@227 455 // At each process step, accumulate() is called once for each
cannam@227 456 // feature on each output within that process's returned feature
cannam@227 457 // list, and with the timestamp passed in being that of the start
cannam@227 458 // of the process block.
cannam@227 459
cannam@227 460 // At the end (in getRemainingFeatures), accumulate() is called
cannam@227 461 // once for each feature on each output within the feature list
cannam@227 462 // returned by getRemainingFeatures, and with the timestamp being
cannam@227 463 // the same as the last process block and final set to true.
cannam@227 464
cannam@227 465 // (What if getRemainingFeatures doesn't return any features? We
cannam@227 466 // still need to ensure that the final duration is written. Need
cannam@227 467 // a separate function to close the durations.)
cannam@227 468
cannam@227 469 // At each call, we pull out the value for the feature and stuff
cannam@227 470 // it into the accumulator's appropriate values array; and we
cannam@227 471 // calculate the duration for the _previous_ feature, or pull it
cannam@227 472 // from the prevDurations array if the previous feature had a
cannam@227 473 // duration in its structure, and stuff that into the
cannam@227 474 // accumulator's appropriate durations array.
cannam@227 475
cannam@227 476 if (m_prevDurations.find(output) != m_prevDurations.end()) {
cannam@227 477
cannam@227 478 // Not the first time accumulate has been called for this
cannam@227 479 // output -- there has been a previous feature
cannam@227 480
cannam@227 481 RealTime prevDuration;
cannam@227 482
cannam@227 483 // Note that m_prevDurations[output] only contains the
cannam@227 484 // duration field that was contained in the previous feature.
cannam@227 485 // If it didn't have an explicit duration,
cannam@227 486 // m_prevDurations[output] should be INVALID_DURATION and we
cannam@227 487 // will have to calculate the duration from the previous and
cannam@227 488 // current timestamps.
cannam@227 489
cannam@227 490 if (m_prevDurations[output] != INVALID_DURATION) {
cannam@227 491 prevDuration = m_prevDurations[output];
cannam@227 492 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 493 std::cerr << "Previous duration from previous feature: " << prevDuration << std::endl;
cannam@227 494 #endif
cannam@227 495 } else {
cannam@227 496 prevDuration = timestamp - m_prevTimestamps[output];
cannam@227 497 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 498 std::cerr << "Previous duration from diff: " << timestamp << " - "
cannam@227 499 << m_prevTimestamps[output] << std::endl;
cannam@227 500 #endif
cannam@227 501 }
cannam@227 502
cannam@227 503 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 504 std::cerr << "output " << output << ": ";
cannam@227 505 std::cerr << "Pushing previous duration as " << prevDuration << std::endl;
cannam@227 506 #endif
cannam@227 507
cannam@227 508 m_accumulators[output].results
cannam@227 509 [m_accumulators[output].results.size() - 1]
cannam@227 510 .duration = prevDuration;
cannam@227 511 }
cannam@227 512
cannam@227 513 if (f.hasDuration) m_prevDurations[output] = f.duration;
cannam@227 514 else m_prevDurations[output] = INVALID_DURATION;
cannam@227 515
cannam@227 516 m_prevTimestamps[output] = timestamp;
cannam@227 517
cannam@227 518 if (f.hasDuration) {
cannam@227 519 RealTime et = timestamp;
cannam@227 520 et = et + f.duration;
cannam@227 521 if (et > m_endTime) m_endTime = et;
cannam@227 522 }
cannam@227 523
cannam@227 524 Result result;
cannam@227 525 result.time = timestamp;
cannam@227 526 result.duration = INVALID_DURATION;
cannam@227 527
cannam@227 528 if (f.values.size() > m_accumulators[output].bins) {
cannam@227 529 m_accumulators[output].bins = f.values.size();
cannam@227 530 }
cannam@227 531
cannam@227 532 for (int i = 0; i < int(f.values.size()); ++i) {
cannam@227 533 result.values.push_back(f.values[i]);
cannam@227 534 }
cannam@227 535
cannam@227 536 m_accumulators[output].results.push_back(result);
cannam@227 537 }
cannam@227 538
cannam@227 539 void
cannam@227 540 PluginSummarisingAdapter::Impl::accumulateFinalDurations()
cannam@227 541 {
cannam@227 542 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin();
cannam@227 543 i != m_prevTimestamps.end(); ++i) {
cannam@227 544
cannam@227 545 int output = i->first;
cannam@227 546
cannam@227 547 int acount = m_accumulators[output].results.size();
cannam@227 548
cannam@227 549 if (acount == 0) continue;
cannam@227 550
cannam@227 551 RealTime prevTimestamp = i->second;
cannam@227 552
cannam@227 553 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 554 std::cerr << "output " << output << ": ";
cannam@227 555 #endif
cannam@227 556
cannam@227 557 if (m_prevDurations.find(output) != m_prevDurations.end() &&
cannam@227 558 m_prevDurations[output] != INVALID_DURATION) {
cannam@227 559
cannam@227 560 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 561 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl;
cannam@227 562 #endif
cannam@227 563
cannam@227 564 m_accumulators[output].results[acount - 1].duration =
cannam@227 565 m_prevDurations[output];
cannam@227 566
cannam@227 567 } else {
cannam@227 568
cannam@227 569 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 570 std::cerr << "Pushing final duration from diff as " << m_endTime << " - " << m_prevTimestamps[output] << std::endl;
cannam@227 571 #endif
cannam@227 572
cannam@227 573 m_accumulators[output].results[acount - 1].duration =
cannam@227 574 m_endTime - m_prevTimestamps[output];
cannam@227 575 }
cannam@227 576
cannam@227 577 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 578 std::cerr << "so duration for result no " << acount-1 << " is "
cannam@227 579 << m_accumulators[output].results[acount-1].duration
cannam@227 580 << std::endl;
cannam@227 581 #endif
cannam@227 582 }
cannam@227 583 }
cannam@227 584
cannam@227 585 void
cannam@227 586 PluginSummarisingAdapter::Impl::findSegmentBounds(RealTime t,
cannam@227 587 RealTime &start,
cannam@227 588 RealTime &end)
cannam@227 589 {
cannam@227 590 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@227 591 std::cerr << "findSegmentBounds: t = " << t << std::endl;
cannam@227 592 #endif
cannam@227 593
cannam@227 594 SegmentBoundaries::const_iterator i = std::upper_bound
cannam@227 595 (m_boundaries.begin(), m_boundaries.end(), t);
cannam@227 596
cannam@227 597 start = RealTime::zeroTime;
cannam@227 598 end = m_endTime;
cannam@227 599
cannam@227 600 if (i != m_boundaries.end()) {
cannam@227 601 end = *i;
cannam@227 602 }
cannam@227 603
cannam@227 604 if (i != m_boundaries.begin()) {
cannam@227 605 start = *--i;
cannam@227 606 }
cannam@227 607
cannam@227 608 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@227 609 std::cerr << "findSegmentBounds: " << t << " is in segment " << start << " -> " << end << std::endl;
cannam@227 610 #endif
cannam@227 611 }
cannam@227 612
cannam@227 613 void
cannam@227 614 PluginSummarisingAdapter::Impl::segment()
cannam@227 615 {
cannam@227 616 SegmentBoundaries::iterator boundaryitr = m_boundaries.begin();
cannam@227 617 RealTime segmentStart = RealTime::zeroTime;
cannam@227 618
cannam@227 619 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
cannam@227 620 i != m_accumulators.end(); ++i) {
cannam@227 621
cannam@227 622 int output = i->first;
cannam@227 623 OutputAccumulator &source = i->second;
cannam@227 624
cannam@227 625 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@227 626 std::cerr << "segment: total results for output " << output << " = "
cannam@227 627 << source.results.size() << std::endl;
cannam@227 628 #endif
cannam@227 629
cannam@227 630 //!!! This is basically nonsense if the results have no values
cannam@227 631 //!!! (i.e. their times and counts are the only things of
cannam@227 632 //!!! interest) but perhaps it's the user's problem if they
cannam@227 633 //!!! ask for segmentation in that case
cannam@227 634
cannam@227 635 for (int n = 0; n < source.results.size(); ++n) {
cannam@227 636
cannam@227 637 // This result spans source.results[n].time to
cannam@227 638 // source.results[n].time + source.results[n].duration.
cannam@227 639 // We need to dispose it into segments appropriately
cannam@227 640
cannam@227 641 RealTime resultStart = source.results[n].time;
cannam@227 642 RealTime resultEnd = resultStart + source.results[n].duration;
cannam@227 643
cannam@227 644 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@227 645 std::cerr << "output: " << output << ", result start = " << resultStart << ", end = " << resultEnd << std::endl;
cannam@227 646 #endif
cannam@227 647
cannam@227 648 RealTime segmentStart = RealTime::zeroTime;
cannam@227 649 RealTime segmentEnd = resultEnd - RealTime(1, 0);
cannam@227 650
cannam@227 651 while (segmentEnd < resultEnd) {
cannam@227 652
cannam@227 653 findSegmentBounds(resultStart, segmentStart, segmentEnd);
cannam@227 654
cannam@227 655 RealTime chunkStart = resultStart;
cannam@227 656 if (chunkStart < segmentStart) chunkStart = segmentStart;
cannam@227 657
cannam@227 658 RealTime chunkEnd = resultEnd;
cannam@227 659 if (chunkEnd > segmentEnd) chunkEnd = segmentEnd;
cannam@227 660
cannam@227 661 m_segmentedAccumulators[output][segmentStart].bins = source.bins;
cannam@227 662
cannam@227 663 Result chunk;
cannam@227 664 chunk.time = chunkStart;
cannam@227 665 chunk.duration = chunkEnd - chunkStart;
cannam@227 666 chunk.values = source.results[n].values;
cannam@227 667
cannam@227 668 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@227 669 std::cerr << "chunk for segment " << segmentStart << ": from " << chunk.time << ", duration " << chunk.duration << std::endl;
cannam@227 670 #endif
cannam@227 671
cannam@227 672 m_segmentedAccumulators[output][segmentStart].results
cannam@227 673 .push_back(chunk);
cannam@227 674
cannam@227 675 resultStart = chunkEnd;
cannam@227 676 }
cannam@227 677 }
cannam@227 678 }
cannam@227 679 }
cannam@227 680
cannam@227 681 struct ValueDurationFloatPair
cannam@227 682 {
cannam@227 683 float value;
cannam@227 684 float duration;
cannam@227 685
cannam@227 686 ValueDurationFloatPair() : value(0), duration(0) { }
cannam@227 687 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
cannam@227 688 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
cannam@227 689 value = p.value;
cannam@227 690 duration = p.duration;
cannam@227 691 return *this;
cannam@227 692 }
cannam@227 693 bool operator<(const ValueDurationFloatPair &p) const {
cannam@227 694 return value < p.value;
cannam@227 695 }
cannam@227 696 };
cannam@227 697
cannam@227 698 static double toSec(const RealTime &r)
cannam@227 699 {
cannam@227 700 return r.sec + double(r.nsec) / 1000000000.0;
cannam@227 701 }
cannam@227 702
cannam@227 703 void
cannam@227 704 PluginSummarisingAdapter::Impl::reduce()
cannam@227 705 {
cannam@227 706 for (OutputSegmentAccumulatorMap::iterator i =
cannam@227 707 m_segmentedAccumulators.begin();
cannam@227 708 i != m_segmentedAccumulators.end(); ++i) {
cannam@227 709
cannam@227 710 int output = i->first;
cannam@227 711 SegmentAccumulatorMap &segments = i->second;
cannam@227 712
cannam@227 713 for (SegmentAccumulatorMap::iterator j = segments.begin();
cannam@227 714 j != segments.end(); ++j) {
cannam@227 715
cannam@227 716 RealTime segmentStart = j->first;
cannam@227 717 OutputAccumulator &accumulator = j->second;
cannam@227 718
cannam@227 719 int sz = accumulator.results.size();
cannam@227 720
cannam@227 721 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 722 std::cerr << "reduce: segment starting at " << segmentStart
cannam@227 723 << " on output " << output << " has " << sz << " result(s)" << std::endl;
cannam@227 724 #endif
cannam@227 725
cannam@227 726 double totalDuration = 0.0;
cannam@227 727 //!!! is this right?
cannam@227 728 if (sz > 0) {
cannam@227 729 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 730 std::cerr << "last time = " << accumulator.results[sz-1].time
cannam@227 731 << ", duration = " << accumulator.results[sz-1].duration
cannam@227 732 << " (step = " << m_stepSize << ", block = " << m_blockSize << ")"
cannam@227 733 << std::endl;
cannam@227 734 #endif
cannam@227 735 totalDuration = toSec((accumulator.results[sz-1].time +
cannam@227 736 accumulator.results[sz-1].duration) -
cannam@227 737 segmentStart);
cannam@227 738 }
cannam@227 739
cannam@227 740 for (int bin = 0; bin < accumulator.bins; ++bin) {
cannam@227 741
cannam@227 742 // work on all values over time for a single bin
cannam@227 743
cannam@227 744 OutputBinSummary summary;
cannam@227 745
cannam@227 746 summary.count = sz;
cannam@227 747
cannam@227 748 summary.minimum = 0.f;
cannam@227 749 summary.maximum = 0.f;
cannam@227 750
cannam@227 751 summary.median = 0.f;
cannam@227 752 summary.mode = 0.f;
cannam@227 753 summary.sum = 0.f;
cannam@227 754 summary.variance = 0.f;
cannam@227 755
cannam@227 756 summary.median_c = 0.f;
cannam@227 757 summary.mode_c = 0.f;
cannam@227 758 summary.mean_c = 0.f;
cannam@227 759 summary.variance_c = 0.f;
cannam@227 760
cannam@227 761 if (sz == 0) continue;
cannam@227 762
cannam@227 763 std::vector<ValueDurationFloatPair> valvec;
cannam@227 764
cannam@227 765 for (int k = 0; k < sz; ++k) {
cannam@227 766 while (accumulator.results[k].values.size() <
cannam@227 767 accumulator.bins) {
cannam@227 768 accumulator.results[k].values.push_back(0.f);
cannam@227 769 }
cannam@227 770 }
cannam@227 771
cannam@227 772 for (int k = 0; k < sz; ++k) {
cannam@227 773 float value = accumulator.results[k].values[bin];
cannam@227 774 valvec.push_back(ValueDurationFloatPair
cannam@227 775 (value,
cannam@227 776 toSec(accumulator.results[k].duration)));
cannam@227 777 }
cannam@227 778
cannam@227 779 std::sort(valvec.begin(), valvec.end());
cannam@227 780
cannam@227 781 summary.minimum = valvec[0].value;
cannam@227 782 summary.maximum = valvec[sz-1].value;
cannam@227 783
cannam@227 784 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 785 std::cerr << "total duration = " << totalDuration << std::endl;
cannam@227 786 #endif
cannam@227 787
cannam@227 788 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 789 /*
cannam@227 790 std::cerr << "value vector for medians:" << std::endl;
cannam@227 791 for (int k = 0; k < sz; ++k) {
cannam@227 792 std::cerr << "(" << valvec[k].value << "," << valvec[k].duration << ") ";
cannam@227 793 }
cannam@227 794 std::cerr << std::endl;
cannam@227 795 */
cannam@227 796 #endif
cannam@227 797
cannam@227 798 if (sz % 2 == 1) {
cannam@227 799 summary.median = valvec[sz/2].value;
cannam@227 800 } else {
cannam@227 801 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
cannam@227 802 }
cannam@227 803
cannam@227 804 double duracc = 0.0;
cannam@227 805 summary.median_c = valvec[sz-1].value;
cannam@227 806
cannam@227 807 for (int k = 0; k < sz; ++k) {
cannam@227 808 duracc += valvec[k].duration;
cannam@227 809 if (duracc > totalDuration/2) {
cannam@227 810 summary.median_c = valvec[k].value;
cannam@227 811 break;
cannam@227 812 }
cannam@227 813 }
cannam@227 814
cannam@227 815 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 816 std::cerr << "median_c = " << summary.median_c << std::endl;
cannam@227 817 std::cerr << "median = " << summary.median << std::endl;
cannam@227 818 #endif
cannam@227 819
cannam@227 820 std::map<float, int> distribution;
cannam@227 821
cannam@227 822 for (int k = 0; k < sz; ++k) {
cannam@227 823 summary.sum += accumulator.results[k].values[bin];
cannam@227 824 distribution[accumulator.results[k].values[bin]] += 1;
cannam@227 825 }
cannam@227 826
cannam@227 827 int md = 0;
cannam@227 828
cannam@227 829 for (std::map<float, int>::iterator di = distribution.begin();
cannam@227 830 di != distribution.end(); ++di) {
cannam@227 831 if (di->second > md) {
cannam@227 832 md = di->second;
cannam@227 833 summary.mode = di->first;
cannam@227 834 }
cannam@227 835 }
cannam@227 836
cannam@227 837 distribution.clear();
cannam@227 838
cannam@227 839 std::map<float, double> distribution_c;
cannam@227 840
cannam@227 841 for (int k = 0; k < sz; ++k) {
cannam@227 842 distribution_c[accumulator.results[k].values[bin]]
cannam@227 843 += toSec(accumulator.results[k].duration);
cannam@227 844 }
cannam@227 845
cannam@227 846 double mrd = 0.0;
cannam@227 847
cannam@227 848 for (std::map<float, double>::iterator di = distribution_c.begin();
cannam@227 849 di != distribution_c.end(); ++di) {
cannam@227 850 if (di->second > mrd) {
cannam@227 851 mrd = di->second;
cannam@227 852 summary.mode_c = di->first;
cannam@227 853 }
cannam@227 854 }
cannam@227 855
cannam@227 856 distribution_c.clear();
cannam@227 857
cannam@227 858 if (totalDuration > 0.0) {
cannam@227 859
cannam@227 860 double sum_c = 0.0;
cannam@227 861
cannam@227 862 for (int k = 0; k < sz; ++k) {
cannam@227 863 double value = accumulator.results[k].values[bin]
cannam@227 864 * toSec(accumulator.results[k].duration);
cannam@227 865 sum_c += value;
cannam@227 866 }
cannam@227 867
cannam@227 868 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 869 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
cannam@227 870 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl;
cannam@227 871 #endif
cannam@227 872
cannam@227 873 summary.mean_c = sum_c / totalDuration;
cannam@227 874
cannam@227 875 for (int k = 0; k < sz; ++k) {
cannam@227 876 double value = accumulator.results[k].values[bin];
cannam@227 877 // * toSec(accumulator.results[k].duration);
cannam@227 878 summary.variance_c +=
cannam@227 879 (value - summary.mean_c) * (value - summary.mean_c)
cannam@227 880 * toSec(accumulator.results[k].duration);
cannam@227 881 }
cannam@227 882
cannam@227 883 // summary.variance_c /= summary.count;
cannam@227 884 summary.variance_c /= totalDuration;
cannam@227 885 }
cannam@227 886
cannam@227 887 double mean = summary.sum / summary.count;
cannam@227 888
cannam@227 889 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@227 890 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
cannam@227 891 << summary.sum / summary.count << std::endl;
cannam@227 892 #endif
cannam@227 893
cannam@227 894 for (int k = 0; k < sz; ++k) {
cannam@227 895 float value = accumulator.results[k].values[bin];
cannam@227 896 summary.variance += (value - mean) * (value - mean);
cannam@227 897 }
cannam@227 898 summary.variance /= summary.count;
cannam@227 899
cannam@227 900 m_summaries[output][segmentStart][bin] = summary;
cannam@227 901 }
cannam@227 902 }
cannam@227 903 }
cannam@227 904
cannam@227 905 m_segmentedAccumulators.clear();
cannam@227 906 m_accumulators.clear();
cannam@227 907 }
cannam@227 908
cannam@227 909
cannam@227 910 }
cannam@227 911
cannam@227 912 }
cannam@227 913