annotate src/vamp-hostsdk/PluginSummarisingAdapter.cpp @ 525:8c18bdaad04f c++11-mutex

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