annotate vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 185:701505ac170c

* Reorganise accumulators to facilitate segmentation
author cannam
date Thu, 11 Sep 2008 13:46:59 +0000
parents 26c200c3fc42
children 8311695c13f9
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@173 43 namespace Vamp {
cannam@173 44
cannam@173 45 namespace HostExt {
cannam@173 46
cannam@173 47 class PluginSummarisingAdapter::Impl
cannam@173 48 {
cannam@173 49 public:
cannam@173 50 Impl(Plugin *plugin, float inputSampleRate);
cannam@173 51 ~Impl();
cannam@173 52
cannam@173 53 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
cannam@173 54 FeatureSet getRemainingFeatures();
cannam@173 55
cannam@173 56 void setSummarySegmentBoundaries(const SegmentBoundaries &);
cannam@173 57
cannam@180 58 FeatureList getSummaryForOutput(int output,
cannam@180 59 SummaryType type,
cannam@180 60 AveragingMethod avg);
cannam@180 61
cannam@180 62 FeatureSet getSummaryForAllOutputs(SummaryType type,
cannam@180 63 AveragingMethod avg);
cannam@173 64
cannam@173 65 protected:
cannam@174 66 Plugin *m_plugin;
cannam@181 67 float m_inputSampleRate;
cannam@174 68
cannam@173 69 SegmentBoundaries m_boundaries;
cannam@185 70
cannam@174 71 typedef std::vector<float> ValueList;
cannam@185 72
cannam@185 73 struct Result { // smaller than Feature
cannam@185 74 RealTime time;
cannam@185 75 RealTime duration;
cannam@185 76 ValueList values; // bin number -> value
cannam@185 77 };
cannam@185 78
cannam@185 79 typedef std::vector<Result> ResultList;
cannam@185 80
cannam@174 81 struct OutputAccumulator {
cannam@185 82 int bins;
cannam@185 83 ResultList results;
cannam@185 84 OutputAccumulator() : bins(0) { }
cannam@174 85 };
cannam@174 86
cannam@174 87 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
cannam@180 88 OutputAccumulatorMap m_accumulators; // output number -> accumulator
cannam@180 89
cannam@185 90 typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap;
cannam@185 91 typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap;
cannam@185 92 OutputSegmentAccumulatorMap m_segmentedAccumulators;
cannam@185 93
cannam@180 94 typedef std::map<int, RealTime> OutputTimestampMap;
cannam@180 95 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
cannam@183 96 OutputTimestampMap m_prevDurations; // output number -> durations
cannam@174 97
cannam@174 98 struct OutputBinSummary {
cannam@180 99
cannam@180 100 int count;
cannam@180 101
cannam@180 102 // extents
cannam@174 103 float minimum;
cannam@174 104 float maximum;
cannam@180 105 float sum;
cannam@180 106
cannam@180 107 // sample-average results
cannam@174 108 float median;
cannam@174 109 float mode;
cannam@174 110 float variance;
cannam@180 111
cannam@180 112 // continuous-time average results
cannam@180 113 float median_c;
cannam@180 114 float mode_c;
cannam@180 115 float mean_c;
cannam@180 116 float variance_c;
cannam@174 117 };
cannam@174 118
cannam@174 119 typedef std::map<int, OutputBinSummary> OutputSummary;
cannam@174 120 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
cannam@174 121 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
cannam@174 122
cannam@174 123 OutputSummarySegmentMap m_summaries;
cannam@174 124
cannam@183 125 bool m_reduced;
cannam@174 126 RealTime m_lastTimestamp;
cannam@174 127
cannam@180 128 void accumulate(const FeatureSet &fs, RealTime, bool final);
cannam@180 129 void accumulate(int output, const Feature &f, RealTime, bool final);
cannam@184 130 void accumulateFinalDurations();
cannam@185 131 void segment();
cannam@174 132 void reduce();
cannam@173 133 };
cannam@184 134
cannam@184 135 static RealTime INVALID_DURATION(INT_MIN, INT_MIN);
cannam@173 136
cannam@173 137 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
cannam@173 138 PluginWrapper(plugin)
cannam@173 139 {
cannam@173 140 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@173 141 }
cannam@173 142
cannam@173 143 PluginSummarisingAdapter::~PluginSummarisingAdapter()
cannam@173 144 {
cannam@173 145 delete m_impl;
cannam@173 146 }
cannam@173 147
cannam@173 148 Plugin::FeatureSet
cannam@173 149 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
cannam@173 150 {
cannam@173 151 return m_impl->process(inputBuffers, timestamp);
cannam@173 152 }
cannam@173 153
cannam@174 154 Plugin::FeatureSet
cannam@174 155 PluginSummarisingAdapter::getRemainingFeatures()
cannam@174 156 {
cannam@174 157 return m_impl->getRemainingFeatures();
cannam@174 158 }
cannam@174 159
cannam@175 160 Plugin::FeatureList
cannam@180 161 PluginSummarisingAdapter::getSummaryForOutput(int output,
cannam@180 162 SummaryType type,
cannam@180 163 AveragingMethod avg)
cannam@175 164 {
cannam@180 165 return m_impl->getSummaryForOutput(output, type, avg);
cannam@176 166 }
cannam@176 167
cannam@176 168 Plugin::FeatureSet
cannam@180 169 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
cannam@180 170 AveragingMethod avg)
cannam@176 171 {
cannam@180 172 return m_impl->getSummaryForAllOutputs(type, avg);
cannam@175 173 }
cannam@173 174
cannam@173 175 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@181 176 m_plugin(plugin),
cannam@183 177 m_inputSampleRate(inputSampleRate),
cannam@183 178 m_reduced(false)
cannam@173 179 {
cannam@173 180 }
cannam@173 181
cannam@173 182 PluginSummarisingAdapter::Impl::~Impl()
cannam@173 183 {
cannam@173 184 }
cannam@173 185
cannam@174 186 Plugin::FeatureSet
cannam@174 187 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp)
cannam@174 188 {
cannam@183 189 if (m_reduced) {
cannam@183 190 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
cannam@183 191 }
cannam@174 192 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
cannam@180 193 accumulate(fs, timestamp, false);
cannam@185 194 //!!! should really be "timestamp plus step size"
cannam@174 195 m_lastTimestamp = timestamp;
cannam@174 196 return fs;
cannam@174 197 }
cannam@174 198
cannam@174 199 Plugin::FeatureSet
cannam@174 200 PluginSummarisingAdapter::Impl::getRemainingFeatures()
cannam@174 201 {
cannam@183 202 if (m_reduced) {
cannam@183 203 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
cannam@183 204 }
cannam@174 205 FeatureSet fs = m_plugin->getRemainingFeatures();
cannam@180 206 accumulate(fs, m_lastTimestamp, true);
cannam@174 207 return fs;
cannam@174 208 }
cannam@174 209
cannam@175 210 Plugin::FeatureList
cannam@180 211 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
cannam@180 212 SummaryType type,
cannam@180 213 AveragingMethod avg)
cannam@175 214 {
cannam@185 215 if (!m_reduced) {
cannam@185 216 segment();
cannam@185 217 reduce();
cannam@185 218 m_reduced = true;
cannam@185 219 }
cannam@183 220
cannam@180 221 bool continuous = (avg == ContinuousTimeAverage);
cannam@180 222
cannam@175 223 FeatureList fl;
cannam@175 224 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
cannam@175 225 i != m_summaries[output].end(); ++i) {
cannam@177 226
cannam@175 227 Feature f;
cannam@175 228 f.hasTimestamp = true;
cannam@175 229 f.timestamp = i->first;
cannam@175 230 f.hasDuration = false;
cannam@177 231
cannam@175 232 for (OutputSummary::const_iterator j = i->second.begin();
cannam@175 233 j != i->second.end(); ++j) {
cannam@175 234
cannam@175 235 // these will be ordered by bin number, and no bin numbers
cannam@175 236 // will be missing except at the end (because of the way
cannam@175 237 // the accumulators were initially filled in accumulate())
cannam@175 238
cannam@175 239 const OutputBinSummary &summary = j->second;
cannam@175 240 float result = 0.f;
cannam@175 241
cannam@175 242 switch (type) {
cannam@175 243
cannam@175 244 case Minimum:
cannam@175 245 result = summary.minimum;
cannam@175 246 break;
cannam@175 247
cannam@175 248 case Maximum:
cannam@175 249 result = summary.maximum;
cannam@175 250 break;
cannam@175 251
cannam@175 252 case Mean:
cannam@180 253 if (continuous) {
cannam@180 254 result = summary.mean_c;
cannam@180 255 } else if (summary.count) {
cannam@175 256 result = summary.sum / summary.count;
cannam@175 257 }
cannam@175 258 break;
cannam@175 259
cannam@175 260 case Median:
cannam@180 261 if (continuous) result = summary.median_c;
cannam@180 262 else result = summary.median;
cannam@175 263 break;
cannam@175 264
cannam@175 265 case Mode:
cannam@180 266 if (continuous) result = summary.mode_c;
cannam@180 267 else result = summary.mode;
cannam@175 268 break;
cannam@175 269
cannam@175 270 case Sum:
cannam@175 271 result = summary.sum;
cannam@175 272 break;
cannam@175 273
cannam@175 274 case Variance:
cannam@180 275 if (continuous) result = summary.variance_c;
cannam@180 276 else result = summary.variance;
cannam@175 277 break;
cannam@175 278
cannam@175 279 case StandardDeviation:
cannam@180 280 if (continuous) result = sqrtf(summary.variance_c);
cannam@180 281 else result = sqrtf(summary.variance);
cannam@175 282 break;
cannam@175 283
cannam@175 284 case Count:
cannam@175 285 result = summary.count;
cannam@175 286 break;
cannam@180 287
cannam@180 288 default:
cannam@180 289 break;
cannam@175 290 }
cannam@177 291
cannam@177 292 f.values.push_back(result);
cannam@175 293 }
cannam@175 294
cannam@175 295 fl.push_back(f);
cannam@175 296 }
cannam@175 297 return fl;
cannam@175 298 }
cannam@175 299
cannam@176 300 Plugin::FeatureSet
cannam@180 301 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
cannam@180 302 AveragingMethod avg)
cannam@176 303 {
cannam@185 304 if (!m_reduced) {
cannam@185 305 segment();
cannam@185 306 reduce();
cannam@185 307 m_reduced = true;
cannam@185 308 }
cannam@183 309
cannam@176 310 FeatureSet fs;
cannam@176 311 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
cannam@176 312 i != m_summaries.end(); ++i) {
cannam@180 313 fs[i->first] = getSummaryForOutput(i->first, type, avg);
cannam@176 314 }
cannam@176 315 return fs;
cannam@176 316 }
cannam@176 317
cannam@174 318 void
cannam@174 319 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
cannam@180 320 RealTime timestamp,
cannam@180 321 bool final)
cannam@174 322 {
cannam@174 323 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
cannam@174 324 for (FeatureList::const_iterator j = i->second.begin();
cannam@174 325 j != i->second.end(); ++j) {
cannam@182 326 if (j->hasTimestamp) {
cannam@182 327 accumulate(i->first, *j, j->timestamp, final);
cannam@182 328 } else {
cannam@182 329 //!!! is this correct?
cannam@182 330 accumulate(i->first, *j, timestamp, final);
cannam@182 331 }
cannam@174 332 }
cannam@174 333 }
cannam@174 334 }
cannam@174 335
cannam@174 336 void
cannam@174 337 PluginSummarisingAdapter::Impl::accumulate(int output,
cannam@174 338 const Feature &f,
cannam@180 339 RealTime timestamp,
cannam@180 340 bool final)
cannam@174 341 {
cannam@180 342 //!!! to do: use timestamp to determine which segment we're on
cannam@185 343
cannam@185 344 //!!! What should happen if a feature's duration spans a segment
cannam@185 345 // boundary? I think we probably want to chop it, and pretend that it
cannam@185 346 // appears in both -- don't we? do we? A very long feature (e.g. key,
cannam@185 347 // if the whole audio is in a single key) might span many or all
cannam@185 348 // segments, and we want that to be reflected in the results (e.g. it
cannam@185 349 // is the modal key in all of those segments, not just the first).
cannam@185 350 // That is actually quite complicated to do!
cannam@180 351
cannam@185 352 //!!! This affects how we record things. If features spanning a
cannam@185 353 // boundary should be chopped, then we need to have per-segment
cannam@185 354 // accumulators (and the feature value goes into both -- perhaps we
cannam@185 355 // need a separate phase to split the accumulator up into segments).
cannam@185 356 // If features spanning a boundary should be counted only in the first
cannam@185 357 // segment, with their full duration, then we should store them in a
cannam@185 358 // single accumulator and distribute into segments only on reduce.
cannam@180 359
cannam@184 360 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl;
cannam@182 361
cannam@184 362 // At each process step, accumulate() is called once for each
cannam@184 363 // feature on each output within that process's returned feature
cannam@184 364 // list, and with the timestamp passed in being that of the start
cannam@184 365 // of the process block.
cannam@182 366
cannam@184 367 // At the end (in getRemainingFeatures), accumulate() is called
cannam@184 368 // once for each feature on each output within the feature list
cannam@184 369 // returned by getRemainingFeatures, and with the timestamp being
cannam@184 370 // the same as the last process block and final set to true.
cannam@184 371
cannam@184 372 // (What if getRemainingFeatures doesn't return any features? We
cannam@184 373 // still need to ensure that the final duration is written. Need
cannam@184 374 // a separate function to close the durations.)
cannam@184 375
cannam@184 376 // At each call, we pull out the value for the feature and stuff
cannam@184 377 // it into the accumulator's appropriate values array; and we
cannam@184 378 // calculate the duration for the _previous_ feature, or pull it
cannam@184 379 // from the prevDurations array if the previous feature had a
cannam@184 380 // duration in its structure, and stuff that into the
cannam@184 381 // accumulator's appropriate durations array.
cannam@184 382
cannam@184 383 if (m_prevDurations.find(output) != m_prevDurations.end()) {
cannam@184 384
cannam@184 385 // Not the first time accumulate has been called for this
cannam@184 386 // output -- there has been a previous feature
cannam@184 387
cannam@184 388 RealTime prevDuration;
cannam@184 389
cannam@184 390 // Note that m_prevDurations[output] only contains the
cannam@184 391 // duration field that was contained in the previous feature.
cannam@184 392 // If it didn't have an explicit duration,
cannam@184 393 // m_prevDurations[output] should be INVALID_DURATION and we
cannam@184 394 // will have to calculate the duration from the previous and
cannam@184 395 // current timestamps.
cannam@184 396
cannam@184 397 if (m_prevDurations[output] != INVALID_DURATION) {
cannam@184 398 prevDuration = m_prevDurations[output];
cannam@184 399 std::cerr << "Previous duration from previous feature: " << prevDuration << std::endl;
cannam@184 400 } else {
cannam@184 401 prevDuration = timestamp - m_prevTimestamps[output];
cannam@184 402 std::cerr << "Previous duration from diff: " << timestamp << " - "
cannam@184 403 << m_prevTimestamps[output] << std::endl;
cannam@180 404 }
cannam@184 405
cannam@184 406 std::cerr << "output " << output << ": ";
cannam@184 407
cannam@184 408 std::cerr << "Pushing previous duration as " << prevDuration << std::endl;
cannam@185 409
cannam@185 410 m_accumulators[output].results
cannam@185 411 [m_accumulators[output].results.size() - 1]
cannam@185 412 .duration = prevDuration;
cannam@180 413 }
cannam@180 414
cannam@184 415 if (f.hasDuration) m_prevDurations[output] = f.duration;
cannam@184 416 else m_prevDurations[output] = INVALID_DURATION;
cannam@184 417
cannam@180 418 m_prevTimestamps[output] = timestamp;
cannam@185 419
cannam@185 420 //!!! should really be "timestamp plus duration" or "timestamp plus output resolution"
cannam@184 421 if (timestamp > m_lastTimestamp) m_lastTimestamp = timestamp;
cannam@180 422
cannam@185 423 Result result;
cannam@185 424 result.time = timestamp;
cannam@185 425 result.duration = INVALID_DURATION;
cannam@185 426
cannam@185 427 if (f.values.size() > m_accumulators[output].bins) {
cannam@185 428 m_accumulators[output].bins = f.values.size();
cannam@185 429 }
cannam@185 430
cannam@174 431 for (int i = 0; i < int(f.values.size()); ++i) {
cannam@185 432 result.values.push_back(f.values[i]);
cannam@174 433 }
cannam@185 434
cannam@185 435 m_accumulators[output].results.push_back(result);
cannam@184 436 }
cannam@180 437
cannam@184 438 void
cannam@184 439 PluginSummarisingAdapter::Impl::accumulateFinalDurations()
cannam@184 440 {
cannam@184 441 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin();
cannam@184 442 i != m_prevTimestamps.end(); ++i) {
cannam@184 443
cannam@184 444 int output = i->first;
cannam@185 445
cannam@185 446 int acount = m_accumulators[output].results.size();
cannam@185 447
cannam@185 448 if (acount == 0) continue;
cannam@185 449
cannam@184 450 RealTime prevTimestamp = i->second;
cannam@184 451
cannam@184 452 std::cerr << "output " << output << ": ";
cannam@184 453
cannam@184 454 if (m_prevDurations.find(output) != m_prevDurations.end() &&
cannam@184 455 m_prevDurations[output] != INVALID_DURATION) {
cannam@184 456
cannam@184 457 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl;
cannam@184 458
cannam@185 459 m_accumulators[output].results[acount - 1].duration =
cannam@185 460 m_prevDurations[output];
cannam@184 461
cannam@184 462 } else {
cannam@184 463
cannam@184 464 std::cerr << "Pushing final duration from diff as " << m_lastTimestamp << " - " << m_prevTimestamps[output] << std::endl;
cannam@184 465
cannam@185 466 m_accumulators[output].results[acount - 1].duration =
cannam@185 467 m_lastTimestamp - m_prevTimestamps[output];
cannam@184 468 }
cannam@180 469 }
cannam@174 470 }
cannam@174 471
cannam@185 472 void
cannam@185 473 PluginSummarisingAdapter::Impl::segment()
cannam@185 474 {
cannam@185 475 /*
cannam@185 476 SegmentBoundaries::iterator boundaryitr = m_boundaries.begin();
cannam@185 477 RealTime segmentStart = RealTime::zeroTime;
cannam@185 478
cannam@185 479 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
cannam@185 480 i != m_accumulators.end(); ++i) {
cannam@185 481
cannam@185 482 int output = i->first;
cannam@185 483 OutputAccumulator &source = i->second;
cannam@185 484 RealTime accumulatedTime = RealTime::zeroTime;
cannam@185 485
cannam@185 486 for (int n = 0; n < source.durations.size(); ++n) {
cannam@185 487 */
cannam@185 488
cannam@185 489
cannam@185 490 /*
cannam@185 491 if (boundaryitr == m_boundaries.end()) {
cannam@185 492 m_segmentedAccumulators[output][segmentStart] = source;
cannam@185 493 source.clear();
cannam@185 494 continue;
cannam@185 495 }
cannam@185 496 */
cannam@185 497
cannam@185 498
cannam@185 499
cannam@185 500
cannam@185 501 }
cannam@185 502
cannam@181 503 struct ValueDurationFloatPair
cannam@181 504 {
cannam@181 505 float value;
cannam@181 506 float duration;
cannam@181 507
cannam@181 508 ValueDurationFloatPair() : value(0), duration(0) { }
cannam@181 509 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
cannam@181 510 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
cannam@181 511 value = p.value;
cannam@181 512 duration = p.duration;
cannam@181 513 return *this;
cannam@181 514 }
cannam@181 515 bool operator<(const ValueDurationFloatPair &p) const {
cannam@181 516 return value < p.value;
cannam@181 517 }
cannam@181 518 };
cannam@181 519
cannam@181 520 static double toSec(const RealTime &r)
cannam@181 521 {
cannam@181 522 return r.sec + double(r.nsec) / 1000000000.0;
cannam@181 523 }
cannam@181 524
cannam@174 525 void
cannam@174 526 PluginSummarisingAdapter::Impl::reduce()
cannam@174 527 {
cannam@184 528 accumulateFinalDurations();
cannam@184 529
cannam@174 530 RealTime segmentStart = RealTime::zeroTime; //!!!
cannam@174 531
cannam@174 532 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
cannam@174 533 i != m_accumulators.end(); ++i) {
cannam@174 534
cannam@174 535 int output = i->first;
cannam@174 536 OutputAccumulator &accumulator = i->second;
cannam@174 537
cannam@185 538 int sz = accumulator.results.size();
cannam@185 539
cannam@182 540 double totalDuration = 0.0;
cannam@185 541 //!!! is this right?
cannam@185 542 if (sz > 0) {
cannam@185 543 totalDuration = toSec(accumulator.results[sz-1].time +
cannam@185 544 accumulator.results[sz-1].duration);
cannam@180 545 }
cannam@180 546
cannam@185 547 for (int bin = 0; bin < accumulator.bins; ++bin) {
cannam@174 548
cannam@180 549 // work on all values over time for a single bin
cannam@180 550
cannam@174 551 OutputBinSummary summary;
cannam@180 552
cannam@185 553 summary.count = sz;
cannam@180 554
cannam@174 555 summary.minimum = 0.f;
cannam@174 556 summary.maximum = 0.f;
cannam@180 557
cannam@174 558 summary.median = 0.f;
cannam@174 559 summary.mode = 0.f;
cannam@174 560 summary.sum = 0.f;
cannam@174 561 summary.variance = 0.f;
cannam@180 562
cannam@180 563 summary.median_c = 0.f;
cannam@180 564 summary.mode_c = 0.f;
cannam@180 565 summary.mean_c = 0.f;
cannam@180 566 summary.variance_c = 0.f;
cannam@180 567
cannam@185 568 if (sz == 0) continue;
cannam@180 569
cannam@181 570 std::vector<ValueDurationFloatPair> valvec;
cannam@181 571
cannam@181 572 for (int k = 0; k < sz; ++k) {
cannam@185 573 while (accumulator.results[k].values.size() <
cannam@185 574 accumulator.bins) {
cannam@185 575 accumulator.results[k].values.push_back(0.f);
cannam@185 576 }
cannam@185 577 }
cannam@185 578
cannam@185 579 for (int k = 0; k < sz; ++k) {
cannam@185 580 float value = accumulator.results[k].values[bin];
cannam@185 581 valvec.push_back(ValueDurationFloatPair
cannam@185 582 (value,
cannam@185 583 toSec(accumulator.results[k].duration)));
cannam@181 584 }
cannam@181 585
cannam@181 586 std::sort(valvec.begin(), valvec.end());
cannam@181 587
cannam@181 588 summary.minimum = valvec[0].value;
cannam@181 589 summary.maximum = valvec[sz-1].value;
cannam@174 590
cannam@174 591 if (sz % 2 == 1) {
cannam@181 592 summary.median = valvec[sz/2].value;
cannam@174 593 } else {
cannam@181 594 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
cannam@174 595 }
cannam@181 596
cannam@181 597 double duracc = 0.0;
cannam@181 598 summary.median_c = valvec[sz-1].value;
cannam@174 599
cannam@181 600 for (int k = 0; k < sz; ++k) {
cannam@181 601 duracc += valvec[k].duration;
cannam@181 602 if (duracc > totalDuration/2) {
cannam@181 603 summary.median_c = valvec[k].value;
cannam@181 604 break;
cannam@181 605 }
cannam@181 606 }
cannam@185 607
cannam@185 608 std::cerr << "median_c = " << summary.median_c << std::endl;
cannam@185 609 std::cerr << "median = " << summary.median << std::endl;
cannam@181 610
cannam@174 611 std::map<float, int> distribution;
cannam@174 612
cannam@174 613 for (int k = 0; k < sz; ++k) {
cannam@185 614 summary.sum += accumulator.results[k].values[bin];
cannam@185 615 distribution[accumulator.results[k].values[bin]] += 1;
cannam@174 616 }
cannam@174 617
cannam@174 618 int md = 0;
cannam@174 619
cannam@174 620 for (std::map<float, int>::iterator di = distribution.begin();
cannam@174 621 di != distribution.end(); ++di) {
cannam@174 622 if (di->second > md) {
cannam@174 623 md = di->second;
cannam@174 624 summary.mode = di->first;
cannam@174 625 }
cannam@174 626 }
cannam@174 627
cannam@174 628 distribution.clear();
cannam@174 629
cannam@181 630 std::map<float, double> distribution_c;
cannam@180 631
cannam@180 632 for (int k = 0; k < sz; ++k) {
cannam@185 633 distribution_c[accumulator.results[k].values[bin]]
cannam@185 634 += toSec(accumulator.results[k].duration);
cannam@180 635 }
cannam@180 636
cannam@181 637 double mrd = 0.0;
cannam@180 638
cannam@181 639 for (std::map<float, double>::iterator di = distribution_c.begin();
cannam@180 640 di != distribution_c.end(); ++di) {
cannam@180 641 if (di->second > mrd) {
cannam@180 642 mrd = di->second;
cannam@180 643 summary.mode_c = di->first;
cannam@180 644 }
cannam@180 645 }
cannam@180 646
cannam@180 647 distribution_c.clear();
cannam@180 648
cannam@181 649 if (totalDuration > 0.0) {
cannam@181 650
cannam@181 651 double sum_c = 0.0;
cannam@181 652
cannam@181 653 for (int k = 0; k < sz; ++k) {
cannam@185 654 double value = accumulator.results[k].values[bin]
cannam@185 655 * toSec(accumulator.results[k].duration);
cannam@181 656 sum_c += value;
cannam@181 657 }
cannam@182 658
cannam@182 659 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
cannam@184 660 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl;
cannam@181 661
cannam@181 662 summary.mean_c = sum_c / totalDuration;
cannam@181 663
cannam@181 664 for (int k = 0; k < sz; ++k) {
cannam@185 665 double value = accumulator.results[k].values[bin]
cannam@185 666 * toSec(accumulator.results[k].duration);
cannam@181 667 summary.variance_c +=
cannam@181 668 (value - summary.mean_c) * (value - summary.mean_c);
cannam@181 669 }
cannam@181 670
cannam@181 671 summary.variance_c /= summary.count;
cannam@181 672 }
cannam@181 673
cannam@174 674 float mean = summary.sum / summary.count;
cannam@174 675
cannam@182 676 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
cannam@182 677 << summary.sum / summary.count << std::endl;
cannam@182 678
cannam@174 679 for (int k = 0; k < sz; ++k) {
cannam@185 680 float value = accumulator.results[k].values[bin];
cannam@185 681 summary.variance += (value - mean) * (value - mean);
cannam@174 682 }
cannam@174 683 summary.variance /= summary.count;
cannam@174 684
cannam@174 685 m_summaries[output][segmentStart][bin] = summary;
cannam@174 686 }
cannam@174 687 }
cannam@175 688
cannam@175 689 m_accumulators.clear();
cannam@174 690 }
cannam@174 691
cannam@174 692
cannam@174 693 }
cannam@174 694
cannam@174 695 }
cannam@174 696