annotate src/vamp-hostsdk/PluginSummarisingAdapter.cpp @ 263:4454843ff384

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