annotate src/vamp-hostsdk/PluginSummarisingAdapter.cpp @ 287:f3b1ba71a305

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