annotate vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 211:caa9d07bb9bd

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