comparison src/vamp-hostsdk/PluginSummarisingAdapter.cpp @ 233:521734d2b498 distinct-libraries

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