annotate src/vamp-hostsdk/PluginSummarisingAdapter.cpp @ 354:e85513153c71

Initialise rate to 0. Otherwise there's a danger plugins will change the SampleType (e.g. to VariableSampleRate) but not set the rate because they don't think they need it (when in fact it needs to be set to 0)
author Chris Cannam
date Thu, 28 Mar 2013 15:49:17 +0000
parents d5c5a52e6c9f
children 632d90c185ec
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 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 570 std::cerr << "output " << output << ": ";
cannam@233 571 #endif
cannam@233 572
cannam@233 573 if (m_prevDurations.find(output) != m_prevDurations.end() &&
cannam@233 574 m_prevDurations[output] != INVALID_DURATION) {
cannam@233 575
cannam@233 576 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 577 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl;
cannam@233 578 #endif
cannam@233 579
cannam@233 580 m_accumulators[output].results[acount - 1].duration =
cannam@233 581 m_prevDurations[output];
cannam@233 582
cannam@233 583 } else {
cannam@233 584
cannam@233 585 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 586 std::cerr << "Pushing final duration from diff as " << m_endTime << " - " << m_prevTimestamps[output] << std::endl;
cannam@233 587 #endif
cannam@233 588
cannam@233 589 m_accumulators[output].results[acount - 1].duration =
cannam@233 590 m_endTime - m_prevTimestamps[output];
cannam@233 591 }
cannam@233 592
cannam@233 593 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 594 std::cerr << "so duration for result no " << acount-1 << " is "
cannam@233 595 << m_accumulators[output].results[acount-1].duration
cannam@233 596 << std::endl;
cannam@233 597 #endif
cannam@233 598 }
cannam@233 599 }
cannam@233 600
cannam@233 601 void
cannam@233 602 PluginSummarisingAdapter::Impl::findSegmentBounds(RealTime t,
cannam@233 603 RealTime &start,
cannam@233 604 RealTime &end)
cannam@233 605 {
cannam@233 606 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@233 607 std::cerr << "findSegmentBounds: t = " << t << std::endl;
cannam@233 608 #endif
cannam@233 609
cannam@233 610 SegmentBoundaries::const_iterator i = std::upper_bound
cannam@233 611 (m_boundaries.begin(), m_boundaries.end(), t);
cannam@233 612
cannam@233 613 start = RealTime::zeroTime;
cannam@233 614 end = m_endTime;
cannam@233 615
cannam@233 616 if (i != m_boundaries.end()) {
cannam@233 617 end = *i;
cannam@233 618 }
cannam@233 619
cannam@233 620 if (i != m_boundaries.begin()) {
cannam@233 621 start = *--i;
cannam@233 622 }
cannam@233 623
cannam@233 624 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@233 625 std::cerr << "findSegmentBounds: " << t << " is in segment " << start << " -> " << end << std::endl;
cannam@233 626 #endif
cannam@233 627 }
cannam@233 628
cannam@233 629 void
cannam@233 630 PluginSummarisingAdapter::Impl::segment()
cannam@233 631 {
cannam@233 632 SegmentBoundaries::iterator boundaryitr = m_boundaries.begin();
cannam@233 633
cannam@274 634 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@274 635 std::cerr << "segment: starting" << std::endl;
cannam@274 636 #endif
cannam@274 637
cannam@233 638 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
cannam@233 639 i != m_accumulators.end(); ++i) {
cannam@233 640
cannam@233 641 int output = i->first;
cannam@233 642 OutputAccumulator &source = i->second;
cannam@233 643
cannam@233 644 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@233 645 std::cerr << "segment: total results for output " << output << " = "
cannam@233 646 << source.results.size() << std::endl;
cannam@233 647 #endif
cannam@233 648
cannam@248 649 // This is basically nonsense if the results have no values
cannam@248 650 // (i.e. their times and counts are the only things of
cannam@248 651 // interest)... but perhaps it's the user's problem if they
cannam@248 652 // ask for segmentation (or any summary at all) in that case
cannam@233 653
cannam@265 654 for (int n = 0; n < int(source.results.size()); ++n) {
cannam@233 655
cannam@233 656 // This result spans source.results[n].time to
cannam@233 657 // source.results[n].time + source.results[n].duration.
cannam@233 658 // We need to dispose it into segments appropriately
cannam@233 659
cannam@233 660 RealTime resultStart = source.results[n].time;
cannam@233 661 RealTime resultEnd = resultStart + source.results[n].duration;
cannam@233 662
cannam@233 663 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@233 664 std::cerr << "output: " << output << ", result start = " << resultStart << ", end = " << resultEnd << std::endl;
cannam@233 665 #endif
cannam@233 666
cannam@233 667 RealTime segmentStart = RealTime::zeroTime;
cannam@233 668 RealTime segmentEnd = resultEnd - RealTime(1, 0);
cannam@233 669
cannam@274 670 RealTime prevSegmentStart = segmentStart - RealTime(1, 0);
cannam@274 671
cannam@233 672 while (segmentEnd < resultEnd) {
cannam@233 673
cannam@274 674 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@274 675 std::cerr << "segment end " << segmentEnd << " < result end "
cannam@274 676 << resultEnd << " (with result start " << resultStart << ")" << std::endl;
cannam@274 677 #endif
cannam@274 678
cannam@233 679 findSegmentBounds(resultStart, segmentStart, segmentEnd);
cannam@274 680
cannam@274 681 if (segmentStart == prevSegmentStart) {
cannam@274 682 // This can happen when we reach the end of the
cannam@274 683 // input, if a feature's end time overruns the
cannam@274 684 // input audio end time
cannam@274 685 break;
cannam@274 686 }
cannam@274 687 prevSegmentStart = segmentStart;
cannam@233 688
cannam@233 689 RealTime chunkStart = resultStart;
cannam@233 690 if (chunkStart < segmentStart) chunkStart = segmentStart;
cannam@233 691
cannam@233 692 RealTime chunkEnd = resultEnd;
cannam@233 693 if (chunkEnd > segmentEnd) chunkEnd = segmentEnd;
cannam@233 694
cannam@233 695 m_segmentedAccumulators[output][segmentStart].bins = source.bins;
cannam@233 696
cannam@233 697 Result chunk;
cannam@233 698 chunk.time = chunkStart;
cannam@233 699 chunk.duration = chunkEnd - chunkStart;
cannam@233 700 chunk.values = source.results[n].values;
cannam@233 701
cannam@233 702 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER_SEGMENT
cannam@233 703 std::cerr << "chunk for segment " << segmentStart << ": from " << chunk.time << ", duration " << chunk.duration << std::endl;
cannam@233 704 #endif
cannam@233 705
cannam@233 706 m_segmentedAccumulators[output][segmentStart].results
cannam@233 707 .push_back(chunk);
cannam@233 708
cannam@233 709 resultStart = chunkEnd;
cannam@233 710 }
cannam@233 711 }
cannam@233 712 }
cannam@233 713 }
cannam@233 714
cannam@233 715 struct ValueDurationFloatPair
cannam@233 716 {
cannam@233 717 float value;
cannam@233 718 float duration;
cannam@233 719
cannam@233 720 ValueDurationFloatPair() : value(0), duration(0) { }
cannam@233 721 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
cannam@233 722 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
cannam@233 723 value = p.value;
cannam@233 724 duration = p.duration;
cannam@233 725 return *this;
cannam@233 726 }
cannam@233 727 bool operator<(const ValueDurationFloatPair &p) const {
cannam@233 728 return value < p.value;
cannam@233 729 }
cannam@233 730 };
cannam@233 731
cannam@233 732 static double toSec(const RealTime &r)
cannam@233 733 {
cannam@233 734 return r.sec + double(r.nsec) / 1000000000.0;
cannam@233 735 }
cannam@233 736
cannam@233 737 void
cannam@233 738 PluginSummarisingAdapter::Impl::reduce()
cannam@233 739 {
cannam@233 740 for (OutputSegmentAccumulatorMap::iterator i =
cannam@233 741 m_segmentedAccumulators.begin();
cannam@233 742 i != m_segmentedAccumulators.end(); ++i) {
cannam@233 743
cannam@233 744 int output = i->first;
cannam@233 745 SegmentAccumulatorMap &segments = i->second;
cannam@233 746
cannam@233 747 for (SegmentAccumulatorMap::iterator j = segments.begin();
cannam@233 748 j != segments.end(); ++j) {
cannam@233 749
cannam@233 750 RealTime segmentStart = j->first;
cannam@233 751 OutputAccumulator &accumulator = j->second;
cannam@233 752
cannam@233 753 int sz = accumulator.results.size();
cannam@233 754
cannam@233 755 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 756 std::cerr << "reduce: segment starting at " << segmentStart
cannam@233 757 << " on output " << output << " has " << sz << " result(s)" << std::endl;
cannam@233 758 #endif
cannam@233 759
cannam@233 760 double totalDuration = 0.0;
cannam@233 761 //!!! is this right?
cannam@233 762 if (sz > 0) {
cannam@233 763 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 764 std::cerr << "last time = " << accumulator.results[sz-1].time
cannam@233 765 << ", duration = " << accumulator.results[sz-1].duration
cannam@233 766 << " (step = " << m_stepSize << ", block = " << m_blockSize << ")"
cannam@233 767 << std::endl;
cannam@233 768 #endif
cannam@233 769 totalDuration = toSec((accumulator.results[sz-1].time +
cannam@233 770 accumulator.results[sz-1].duration) -
cannam@233 771 segmentStart);
cannam@233 772 }
cannam@233 773
cannam@233 774 for (int bin = 0; bin < accumulator.bins; ++bin) {
cannam@233 775
cannam@233 776 // work on all values over time for a single bin
cannam@233 777
cannam@233 778 OutputBinSummary summary;
cannam@233 779
cannam@233 780 summary.count = sz;
cannam@233 781
cannam@233 782 summary.minimum = 0.f;
cannam@233 783 summary.maximum = 0.f;
cannam@233 784
cannam@233 785 summary.median = 0.f;
cannam@233 786 summary.mode = 0.f;
cannam@233 787 summary.sum = 0.f;
cannam@233 788 summary.variance = 0.f;
cannam@233 789
cannam@233 790 summary.median_c = 0.f;
cannam@233 791 summary.mode_c = 0.f;
cannam@233 792 summary.mean_c = 0.f;
cannam@233 793 summary.variance_c = 0.f;
cannam@233 794
cannam@233 795 if (sz == 0) continue;
cannam@233 796
cannam@233 797 std::vector<ValueDurationFloatPair> valvec;
cannam@233 798
cannam@233 799 for (int k = 0; k < sz; ++k) {
cannam@265 800 while (int(accumulator.results[k].values.size()) <
cannam@233 801 accumulator.bins) {
cannam@233 802 accumulator.results[k].values.push_back(0.f);
cannam@233 803 }
cannam@233 804 }
cannam@233 805
cannam@233 806 for (int k = 0; k < sz; ++k) {
cannam@233 807 float value = accumulator.results[k].values[bin];
cannam@233 808 valvec.push_back(ValueDurationFloatPair
cannam@233 809 (value,
cannam@233 810 toSec(accumulator.results[k].duration)));
cannam@233 811 }
cannam@233 812
cannam@233 813 std::sort(valvec.begin(), valvec.end());
cannam@233 814
cannam@233 815 summary.minimum = valvec[0].value;
cannam@233 816 summary.maximum = valvec[sz-1].value;
cannam@233 817
cannam@233 818 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 819 std::cerr << "total duration = " << totalDuration << std::endl;
cannam@233 820 #endif
cannam@233 821
cannam@233 822 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 823 /*
cannam@233 824 std::cerr << "value vector for medians:" << std::endl;
cannam@233 825 for (int k = 0; k < sz; ++k) {
cannam@233 826 std::cerr << "(" << valvec[k].value << "," << valvec[k].duration << ") ";
cannam@233 827 }
cannam@233 828 std::cerr << std::endl;
cannam@233 829 */
cannam@233 830 #endif
cannam@233 831
cannam@233 832 if (sz % 2 == 1) {
cannam@233 833 summary.median = valvec[sz/2].value;
cannam@233 834 } else {
cannam@233 835 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
cannam@233 836 }
cannam@233 837
cannam@233 838 double duracc = 0.0;
cannam@233 839 summary.median_c = valvec[sz-1].value;
cannam@233 840
cannam@233 841 for (int k = 0; k < sz; ++k) {
cannam@233 842 duracc += valvec[k].duration;
cannam@233 843 if (duracc > totalDuration/2) {
cannam@233 844 summary.median_c = valvec[k].value;
cannam@233 845 break;
cannam@233 846 }
cannam@233 847 }
cannam@233 848
cannam@233 849 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 850 std::cerr << "median_c = " << summary.median_c << std::endl;
cannam@233 851 std::cerr << "median = " << summary.median << std::endl;
cannam@233 852 #endif
cannam@233 853
cannam@233 854 std::map<float, int> distribution;
cannam@233 855
cannam@233 856 for (int k = 0; k < sz; ++k) {
cannam@233 857 summary.sum += accumulator.results[k].values[bin];
cannam@233 858 distribution[accumulator.results[k].values[bin]] += 1;
cannam@233 859 }
cannam@233 860
cannam@233 861 int md = 0;
cannam@233 862
cannam@233 863 for (std::map<float, int>::iterator di = distribution.begin();
cannam@233 864 di != distribution.end(); ++di) {
cannam@233 865 if (di->second > md) {
cannam@233 866 md = di->second;
cannam@233 867 summary.mode = di->first;
cannam@233 868 }
cannam@233 869 }
cannam@233 870
cannam@233 871 distribution.clear();
cannam@233 872
cannam@233 873 std::map<float, double> distribution_c;
cannam@233 874
cannam@233 875 for (int k = 0; k < sz; ++k) {
cannam@233 876 distribution_c[accumulator.results[k].values[bin]]
cannam@233 877 += toSec(accumulator.results[k].duration);
cannam@233 878 }
cannam@233 879
cannam@233 880 double mrd = 0.0;
cannam@233 881
cannam@233 882 for (std::map<float, double>::iterator di = distribution_c.begin();
cannam@233 883 di != distribution_c.end(); ++di) {
cannam@233 884 if (di->second > mrd) {
cannam@233 885 mrd = di->second;
cannam@233 886 summary.mode_c = di->first;
cannam@233 887 }
cannam@233 888 }
cannam@233 889
cannam@233 890 distribution_c.clear();
cannam@233 891
cannam@233 892 if (totalDuration > 0.0) {
cannam@233 893
cannam@233 894 double sum_c = 0.0;
cannam@233 895
cannam@233 896 for (int k = 0; k < sz; ++k) {
cannam@233 897 double value = accumulator.results[k].values[bin]
cannam@233 898 * toSec(accumulator.results[k].duration);
cannam@233 899 sum_c += value;
cannam@233 900 }
cannam@233 901
cannam@233 902 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 903 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
cannam@233 904 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl;
cannam@233 905 #endif
cannam@233 906
cannam@233 907 summary.mean_c = sum_c / totalDuration;
cannam@233 908
cannam@233 909 for (int k = 0; k < sz; ++k) {
cannam@233 910 double value = accumulator.results[k].values[bin];
cannam@233 911 // * toSec(accumulator.results[k].duration);
cannam@233 912 summary.variance_c +=
cannam@233 913 (value - summary.mean_c) * (value - summary.mean_c)
cannam@233 914 * toSec(accumulator.results[k].duration);
cannam@233 915 }
cannam@233 916
cannam@233 917 // summary.variance_c /= summary.count;
cannam@233 918 summary.variance_c /= totalDuration;
cannam@233 919 }
cannam@233 920
cannam@233 921 double mean = summary.sum / summary.count;
cannam@233 922
cannam@233 923 #ifdef DEBUG_PLUGIN_SUMMARISING_ADAPTER
cannam@233 924 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
cannam@233 925 << summary.sum / summary.count << std::endl;
cannam@233 926 #endif
cannam@233 927
cannam@233 928 for (int k = 0; k < sz; ++k) {
cannam@233 929 float value = accumulator.results[k].values[bin];
cannam@233 930 summary.variance += (value - mean) * (value - mean);
cannam@233 931 }
cannam@233 932 summary.variance /= summary.count;
cannam@233 933
cannam@233 934 m_summaries[output][segmentStart][bin] = summary;
cannam@233 935 }
cannam@233 936 }
cannam@233 937 }
cannam@233 938
cannam@233 939 m_segmentedAccumulators.clear();
cannam@233 940 m_accumulators.clear();
cannam@233 941 }
cannam@233 942
cannam@233 943
cannam@233 944 }
cannam@233 945
cannam@233 946 }
cannam@233 947
cannam@263 948 _VAMP_SDK_HOSTSPACE_END(PluginSummarisingAdapter.cpp)
cannam@263 949