annotate src/vamp-hostsdk/PluginSummarisingAdapter.cpp @ 318:2ed36547e1bf

Patch from Dan Stowell to permit vamp-simple-host to read data of unknown length (for streaming input)
author Chris Cannam
date Thu, 18 Aug 2011 12:21:30 +0100
parents c97e70ed5abc
children d5c5a52e6c9f
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@290 9 Copyright 2006-2009 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