annotate vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 194:27cfae2a4155

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