Mercurial > hg > vamp-plugin-sdk
comparison vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 185:701505ac170c
* Reorganise accumulators to facilitate segmentation
author | cannam |
---|---|
date | Thu, 11 Sep 2008 13:46:59 +0000 |
parents | 26c200c3fc42 |
children | 8311695c13f9 |
comparison
equal
deleted
inserted
replaced
184:26c200c3fc42 | 185:701505ac170c |
---|---|
65 protected: | 65 protected: |
66 Plugin *m_plugin; | 66 Plugin *m_plugin; |
67 float m_inputSampleRate; | 67 float m_inputSampleRate; |
68 | 68 |
69 SegmentBoundaries m_boundaries; | 69 SegmentBoundaries m_boundaries; |
70 | 70 |
71 typedef std::vector<float> ValueList; | 71 typedef std::vector<float> ValueList; |
72 typedef std::map<int, ValueList> BinValueMap; | 72 |
73 typedef std::vector<RealTime> DurationList; | 73 struct Result { // smaller than Feature |
74 | 74 RealTime time; |
75 RealTime duration; | |
76 ValueList values; // bin number -> value | |
77 }; | |
78 | |
79 typedef std::vector<Result> ResultList; | |
80 | |
75 struct OutputAccumulator { | 81 struct OutputAccumulator { |
76 int count; | 82 int bins; |
77 BinValueMap values; // bin number -> values ordered by time | 83 ResultList results; |
78 DurationList durations; | 84 OutputAccumulator() : bins(0) { } |
79 OutputAccumulator() : count(0), values(), durations() { } | |
80 }; | 85 }; |
81 | 86 |
82 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap; | 87 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap; |
83 OutputAccumulatorMap m_accumulators; // output number -> accumulator | 88 OutputAccumulatorMap m_accumulators; // output number -> accumulator |
89 | |
90 typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap; | |
91 typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap; | |
92 OutputSegmentAccumulatorMap m_segmentedAccumulators; | |
84 | 93 |
85 typedef std::map<int, RealTime> OutputTimestampMap; | 94 typedef std::map<int, RealTime> OutputTimestampMap; |
86 OutputTimestampMap m_prevTimestamps; // output number -> timestamp | 95 OutputTimestampMap m_prevTimestamps; // output number -> timestamp |
87 OutputTimestampMap m_prevDurations; // output number -> durations | 96 OutputTimestampMap m_prevDurations; // output number -> durations |
88 | 97 |
117 RealTime m_lastTimestamp; | 126 RealTime m_lastTimestamp; |
118 | 127 |
119 void accumulate(const FeatureSet &fs, RealTime, bool final); | 128 void accumulate(const FeatureSet &fs, RealTime, bool final); |
120 void accumulate(int output, const Feature &f, RealTime, bool final); | 129 void accumulate(int output, const Feature &f, RealTime, bool final); |
121 void accumulateFinalDurations(); | 130 void accumulateFinalDurations(); |
131 void segment(); | |
122 void reduce(); | 132 void reduce(); |
123 }; | 133 }; |
124 | 134 |
125 static RealTime INVALID_DURATION(INT_MIN, INT_MIN); | 135 static RealTime INVALID_DURATION(INT_MIN, INT_MIN); |
126 | 136 |
179 if (m_reduced) { | 189 if (m_reduced) { |
180 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl; | 190 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl; |
181 } | 191 } |
182 FeatureSet fs = m_plugin->process(inputBuffers, timestamp); | 192 FeatureSet fs = m_plugin->process(inputBuffers, timestamp); |
183 accumulate(fs, timestamp, false); | 193 accumulate(fs, timestamp, false); |
194 //!!! should really be "timestamp plus step size" | |
184 m_lastTimestamp = timestamp; | 195 m_lastTimestamp = timestamp; |
185 return fs; | 196 return fs; |
186 } | 197 } |
187 | 198 |
188 Plugin::FeatureSet | 199 Plugin::FeatureSet |
199 Plugin::FeatureList | 210 Plugin::FeatureList |
200 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output, | 211 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output, |
201 SummaryType type, | 212 SummaryType type, |
202 AveragingMethod avg) | 213 AveragingMethod avg) |
203 { | 214 { |
204 if (!m_reduced) reduce(); | 215 if (!m_reduced) { |
216 segment(); | |
217 reduce(); | |
218 m_reduced = true; | |
219 } | |
205 | 220 |
206 bool continuous = (avg == ContinuousTimeAverage); | 221 bool continuous = (avg == ContinuousTimeAverage); |
207 | 222 |
208 FeatureList fl; | 223 FeatureList fl; |
209 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin(); | 224 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin(); |
284 | 299 |
285 Plugin::FeatureSet | 300 Plugin::FeatureSet |
286 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type, | 301 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type, |
287 AveragingMethod avg) | 302 AveragingMethod avg) |
288 { | 303 { |
289 if (!m_reduced) reduce(); | 304 if (!m_reduced) { |
305 segment(); | |
306 reduce(); | |
307 m_reduced = true; | |
308 } | |
290 | 309 |
291 FeatureSet fs; | 310 FeatureSet fs; |
292 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin(); | 311 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin(); |
293 i != m_summaries.end(); ++i) { | 312 i != m_summaries.end(); ++i) { |
294 fs[i->first] = getSummaryForOutput(i->first, type, avg); | 313 fs[i->first] = getSummaryForOutput(i->first, type, avg); |
319 const Feature &f, | 338 const Feature &f, |
320 RealTime timestamp, | 339 RealTime timestamp, |
321 bool final) | 340 bool final) |
322 { | 341 { |
323 //!!! to do: use timestamp to determine which segment we're on | 342 //!!! to do: use timestamp to determine which segment we're on |
324 | 343 |
325 m_accumulators[output].count++; | 344 //!!! What should happen if a feature's duration spans a segment |
345 // boundary? I think we probably want to chop it, and pretend that it | |
346 // appears in both -- don't we? do we? A very long feature (e.g. key, | |
347 // if the whole audio is in a single key) might span many or all | |
348 // segments, and we want that to be reflected in the results (e.g. it | |
349 // is the modal key in all of those segments, not just the first). | |
350 // That is actually quite complicated to do! | |
351 | |
352 //!!! This affects how we record things. If features spanning a | |
353 // boundary should be chopped, then we need to have per-segment | |
354 // accumulators (and the feature value goes into both -- perhaps we | |
355 // need a separate phase to split the accumulator up into segments). | |
356 // If features spanning a boundary should be counted only in the first | |
357 // segment, with their full duration, then we should store them in a | |
358 // single accumulator and distribute into segments only on reduce. | |
326 | 359 |
327 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl; | 360 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl; |
328 | 361 |
329 // At each process step, accumulate() is called once for each | 362 // At each process step, accumulate() is called once for each |
330 // feature on each output within that process's returned feature | 363 // feature on each output within that process's returned feature |
371 } | 404 } |
372 | 405 |
373 std::cerr << "output " << output << ": "; | 406 std::cerr << "output " << output << ": "; |
374 | 407 |
375 std::cerr << "Pushing previous duration as " << prevDuration << std::endl; | 408 std::cerr << "Pushing previous duration as " << prevDuration << std::endl; |
376 m_accumulators[output].durations.push_back(prevDuration); | 409 |
410 m_accumulators[output].results | |
411 [m_accumulators[output].results.size() - 1] | |
412 .duration = prevDuration; | |
377 } | 413 } |
378 | 414 |
379 if (f.hasDuration) m_prevDurations[output] = f.duration; | 415 if (f.hasDuration) m_prevDurations[output] = f.duration; |
380 else m_prevDurations[output] = INVALID_DURATION; | 416 else m_prevDurations[output] = INVALID_DURATION; |
381 | 417 |
382 m_prevTimestamps[output] = timestamp; | 418 m_prevTimestamps[output] = timestamp; |
419 | |
420 //!!! should really be "timestamp plus duration" or "timestamp plus output resolution" | |
383 if (timestamp > m_lastTimestamp) m_lastTimestamp = timestamp; | 421 if (timestamp > m_lastTimestamp) m_lastTimestamp = timestamp; |
384 | 422 |
423 Result result; | |
424 result.time = timestamp; | |
425 result.duration = INVALID_DURATION; | |
426 | |
427 if (f.values.size() > m_accumulators[output].bins) { | |
428 m_accumulators[output].bins = f.values.size(); | |
429 } | |
430 | |
385 for (int i = 0; i < int(f.values.size()); ++i) { | 431 for (int i = 0; i < int(f.values.size()); ++i) { |
386 m_accumulators[output].values[i].push_back(f.values[i]); | 432 result.values.push_back(f.values[i]); |
387 } | 433 } |
434 | |
435 m_accumulators[output].results.push_back(result); | |
388 } | 436 } |
389 | 437 |
390 void | 438 void |
391 PluginSummarisingAdapter::Impl::accumulateFinalDurations() | 439 PluginSummarisingAdapter::Impl::accumulateFinalDurations() |
392 { | 440 { |
393 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin(); | 441 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin(); |
394 i != m_prevTimestamps.end(); ++i) { | 442 i != m_prevTimestamps.end(); ++i) { |
395 | 443 |
396 int output = i->first; | 444 int output = i->first; |
445 | |
446 int acount = m_accumulators[output].results.size(); | |
447 | |
448 if (acount == 0) continue; | |
449 | |
397 RealTime prevTimestamp = i->second; | 450 RealTime prevTimestamp = i->second; |
398 | 451 |
399 std::cerr << "output " << output << ": "; | 452 std::cerr << "output " << output << ": "; |
400 | 453 |
401 if (m_prevDurations.find(output) != m_prevDurations.end() && | 454 if (m_prevDurations.find(output) != m_prevDurations.end() && |
402 m_prevDurations[output] != INVALID_DURATION) { | 455 m_prevDurations[output] != INVALID_DURATION) { |
403 | 456 |
404 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl; | 457 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl; |
405 | 458 |
406 m_accumulators[output].durations.push_back(m_prevDurations[output]); | 459 m_accumulators[output].results[acount - 1].duration = |
460 m_prevDurations[output]; | |
407 | 461 |
408 } else { | 462 } else { |
409 | 463 |
410 std::cerr << "Pushing final duration from diff as " << m_lastTimestamp << " - " << m_prevTimestamps[output] << std::endl; | 464 std::cerr << "Pushing final duration from diff as " << m_lastTimestamp << " - " << m_prevTimestamps[output] << std::endl; |
411 | 465 |
412 m_accumulators[output].durations.push_back | 466 m_accumulators[output].results[acount - 1].duration = |
413 (m_lastTimestamp - m_prevTimestamps[output]); | 467 m_lastTimestamp - m_prevTimestamps[output]; |
414 } | 468 } |
415 } | 469 } |
470 } | |
471 | |
472 void | |
473 PluginSummarisingAdapter::Impl::segment() | |
474 { | |
475 /* | |
476 SegmentBoundaries::iterator boundaryitr = m_boundaries.begin(); | |
477 RealTime segmentStart = RealTime::zeroTime; | |
478 | |
479 for (OutputAccumulatorMap::iterator i = m_accumulators.begin(); | |
480 i != m_accumulators.end(); ++i) { | |
481 | |
482 int output = i->first; | |
483 OutputAccumulator &source = i->second; | |
484 RealTime accumulatedTime = RealTime::zeroTime; | |
485 | |
486 for (int n = 0; n < source.durations.size(); ++n) { | |
487 */ | |
488 | |
489 | |
490 /* | |
491 if (boundaryitr == m_boundaries.end()) { | |
492 m_segmentedAccumulators[output][segmentStart] = source; | |
493 source.clear(); | |
494 continue; | |
495 } | |
496 */ | |
497 | |
498 | |
499 | |
500 | |
416 } | 501 } |
417 | 502 |
418 struct ValueDurationFloatPair | 503 struct ValueDurationFloatPair |
419 { | 504 { |
420 float value; | 505 float value; |
448 i != m_accumulators.end(); ++i) { | 533 i != m_accumulators.end(); ++i) { |
449 | 534 |
450 int output = i->first; | 535 int output = i->first; |
451 OutputAccumulator &accumulator = i->second; | 536 OutputAccumulator &accumulator = i->second; |
452 | 537 |
538 int sz = accumulator.results.size(); | |
539 | |
453 double totalDuration = 0.0; | 540 double totalDuration = 0.0; |
454 for (int k = 0; k < accumulator.durations.size(); ++k) { | 541 //!!! is this right? |
455 totalDuration += toSec(accumulator.durations[k]); | 542 if (sz > 0) { |
543 totalDuration = toSec(accumulator.results[sz-1].time + | |
544 accumulator.results[sz-1].duration); | |
456 } | 545 } |
457 | 546 |
458 for (BinValueMap::iterator j = accumulator.values.begin(); | 547 for (int bin = 0; bin < accumulator.bins; ++bin) { |
459 j != accumulator.values.end(); ++j) { | |
460 | 548 |
461 // work on all values over time for a single bin | 549 // work on all values over time for a single bin |
462 | 550 |
463 int bin = j->first; | |
464 const ValueList &values = j->second; | |
465 const DurationList &durations = accumulator.durations; | |
466 | |
467 OutputBinSummary summary; | 551 OutputBinSummary summary; |
468 | 552 |
469 summary.count = accumulator.count; | 553 summary.count = sz; |
470 | 554 |
471 summary.minimum = 0.f; | 555 summary.minimum = 0.f; |
472 summary.maximum = 0.f; | 556 summary.maximum = 0.f; |
473 | 557 |
474 summary.median = 0.f; | 558 summary.median = 0.f; |
479 summary.median_c = 0.f; | 563 summary.median_c = 0.f; |
480 summary.mode_c = 0.f; | 564 summary.mode_c = 0.f; |
481 summary.mean_c = 0.f; | 565 summary.mean_c = 0.f; |
482 summary.variance_c = 0.f; | 566 summary.variance_c = 0.f; |
483 | 567 |
484 if (summary.count == 0 || values.empty()) continue; | 568 if (sz == 0) continue; |
485 | |
486 int sz = values.size(); | |
487 | |
488 if (sz != durations.size()) { | |
489 std::cerr << "WARNING: sz " << sz << " != durations.size() " | |
490 << durations.size() << std::endl; | |
491 // while (durations.size() < sz) { | |
492 // durations.push_back(RealTime::zeroTime); | |
493 // } | |
494 //!!! then what? | |
495 } | |
496 | 569 |
497 std::vector<ValueDurationFloatPair> valvec; | 570 std::vector<ValueDurationFloatPair> valvec; |
498 | 571 |
499 for (int k = 0; k < sz; ++k) { | 572 for (int k = 0; k < sz; ++k) { |
500 valvec.push_back(ValueDurationFloatPair(values[k], | 573 while (accumulator.results[k].values.size() < |
501 toSec(durations[k]))); | 574 accumulator.bins) { |
575 accumulator.results[k].values.push_back(0.f); | |
576 } | |
577 } | |
578 | |
579 for (int k = 0; k < sz; ++k) { | |
580 float value = accumulator.results[k].values[bin]; | |
581 valvec.push_back(ValueDurationFloatPair | |
582 (value, | |
583 toSec(accumulator.results[k].duration))); | |
502 } | 584 } |
503 | 585 |
504 std::sort(valvec.begin(), valvec.end()); | 586 std::sort(valvec.begin(), valvec.end()); |
505 | 587 |
506 summary.minimum = valvec[0].value; | 588 summary.minimum = valvec[0].value; |
520 if (duracc > totalDuration/2) { | 602 if (duracc > totalDuration/2) { |
521 summary.median_c = valvec[k].value; | 603 summary.median_c = valvec[k].value; |
522 break; | 604 break; |
523 } | 605 } |
524 } | 606 } |
607 | |
608 std::cerr << "median_c = " << summary.median_c << std::endl; | |
609 std::cerr << "median = " << summary.median << std::endl; | |
525 | 610 |
526 std::map<float, int> distribution; | 611 std::map<float, int> distribution; |
527 | 612 |
528 for (int k = 0; k < sz; ++k) { | 613 for (int k = 0; k < sz; ++k) { |
529 summary.sum += values[k]; | 614 summary.sum += accumulator.results[k].values[bin]; |
530 distribution[values[k]] += 1; | 615 distribution[accumulator.results[k].values[bin]] += 1; |
531 } | 616 } |
532 | 617 |
533 int md = 0; | 618 int md = 0; |
534 | 619 |
535 for (std::map<float, int>::iterator di = distribution.begin(); | 620 for (std::map<float, int>::iterator di = distribution.begin(); |
540 } | 625 } |
541 } | 626 } |
542 | 627 |
543 distribution.clear(); | 628 distribution.clear(); |
544 | 629 |
545 //!!! we want to omit this bit if the features all have | |
546 //!!! equal duration (and set mode_c equal to mode instead) | |
547 | |
548 std::map<float, double> distribution_c; | 630 std::map<float, double> distribution_c; |
549 | 631 |
550 for (int k = 0; k < sz; ++k) { | 632 for (int k = 0; k < sz; ++k) { |
551 distribution_c[values[k]] += toSec(durations[k]); | 633 distribution_c[accumulator.results[k].values[bin]] |
634 += toSec(accumulator.results[k].duration); | |
552 } | 635 } |
553 | 636 |
554 double mrd = 0.0; | 637 double mrd = 0.0; |
555 | 638 |
556 for (std::map<float, double>::iterator di = distribution_c.begin(); | 639 for (std::map<float, double>::iterator di = distribution_c.begin(); |
566 if (totalDuration > 0.0) { | 649 if (totalDuration > 0.0) { |
567 | 650 |
568 double sum_c = 0.0; | 651 double sum_c = 0.0; |
569 | 652 |
570 for (int k = 0; k < sz; ++k) { | 653 for (int k = 0; k < sz; ++k) { |
571 double value = values[k] * toSec(durations[k]); | 654 double value = accumulator.results[k].values[bin] |
655 * toSec(accumulator.results[k].duration); | |
572 sum_c += value; | 656 sum_c += value; |
573 } | 657 } |
574 | 658 |
575 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = " | 659 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = " |
576 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl; | 660 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl; |
577 | 661 |
578 summary.mean_c = sum_c / totalDuration; | 662 summary.mean_c = sum_c / totalDuration; |
579 | 663 |
580 for (int k = 0; k < sz; ++k) { | 664 for (int k = 0; k < sz; ++k) { |
581 double value = values[k] * toSec(durations[k]); | 665 double value = accumulator.results[k].values[bin] |
666 * toSec(accumulator.results[k].duration); | |
582 summary.variance_c += | 667 summary.variance_c += |
583 (value - summary.mean_c) * (value - summary.mean_c); | 668 (value - summary.mean_c) * (value - summary.mean_c); |
584 } | 669 } |
585 | 670 |
586 summary.variance_c /= summary.count; | 671 summary.variance_c /= summary.count; |
587 } | 672 } |
588 | 673 |
589 //!!! still to handle: median_c | |
590 | |
591 float mean = summary.sum / summary.count; | 674 float mean = summary.sum / summary.count; |
592 | 675 |
593 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = " | 676 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = " |
594 << summary.sum / summary.count << std::endl; | 677 << summary.sum / summary.count << std::endl; |
595 | 678 |
596 for (int k = 0; k < sz; ++k) { | 679 for (int k = 0; k < sz; ++k) { |
597 summary.variance += (values[k] - mean) * (values[k] - mean); | 680 float value = accumulator.results[k].values[bin]; |
681 summary.variance += (value - mean) * (value - mean); | |
598 } | 682 } |
599 summary.variance /= summary.count; | 683 summary.variance /= summary.count; |
600 | 684 |
601 m_summaries[output][segmentStart][bin] = summary; | 685 m_summaries[output][segmentStart][bin] = summary; |
602 } | 686 } |
603 } | 687 } |
604 | 688 |
605 m_accumulators.clear(); | 689 m_accumulators.clear(); |
606 m_reduced = true; | 690 } |
607 } | 691 |
608 | 692 |
609 | 693 } |
610 } | 694 |
611 | 695 } |
612 } | 696 |
613 |