annotate vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 195:1e4c6f25ded6

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