cannam@173
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@173
|
2
|
cannam@173
|
3 /*
|
cannam@173
|
4 Vamp
|
cannam@173
|
5
|
cannam@173
|
6 An API for audio analysis and feature extraction plugins.
|
cannam@173
|
7
|
cannam@173
|
8 Centre for Digital Music, Queen Mary, University of London.
|
cannam@173
|
9 Copyright 2006-2008 Chris Cannam and QMUL.
|
cannam@173
|
10
|
cannam@173
|
11 Permission is hereby granted, free of charge, to any person
|
cannam@173
|
12 obtaining a copy of this software and associated documentation
|
cannam@173
|
13 files (the "Software"), to deal in the Software without
|
cannam@173
|
14 restriction, including without limitation the rights to use, copy,
|
cannam@173
|
15 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@173
|
16 of the Software, and to permit persons to whom the Software is
|
cannam@173
|
17 furnished to do so, subject to the following conditions:
|
cannam@173
|
18
|
cannam@173
|
19 The above copyright notice and this permission notice shall be
|
cannam@173
|
20 included in all copies or substantial portions of the Software.
|
cannam@173
|
21
|
cannam@173
|
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@173
|
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@173
|
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@173
|
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@173
|
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@173
|
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@173
|
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@173
|
29
|
cannam@173
|
30 Except as contained in this notice, the names of the Centre for
|
cannam@173
|
31 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@173
|
32 shall not be used in advertising or otherwise to promote the sale,
|
cannam@173
|
33 use or other dealings in this Software without prior written
|
cannam@173
|
34 authorization.
|
cannam@173
|
35 */
|
cannam@173
|
36
|
cannam@173
|
37 #include "PluginSummarisingAdapter.h"
|
cannam@173
|
38
|
cannam@174
|
39 #include <map>
|
cannam@175
|
40 #include <cmath>
|
cannam@174
|
41
|
cannam@173
|
42 namespace Vamp {
|
cannam@173
|
43
|
cannam@173
|
44 namespace HostExt {
|
cannam@173
|
45
|
cannam@173
|
46 class PluginSummarisingAdapter::Impl
|
cannam@173
|
47 {
|
cannam@173
|
48 public:
|
cannam@173
|
49 Impl(Plugin *plugin, float inputSampleRate);
|
cannam@173
|
50 ~Impl();
|
cannam@173
|
51
|
cannam@173
|
52 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
|
cannam@173
|
53 FeatureSet getRemainingFeatures();
|
cannam@173
|
54
|
cannam@173
|
55 void setSummarySegmentBoundaries(const SegmentBoundaries &);
|
cannam@173
|
56
|
cannam@180
|
57 FeatureList getSummaryForOutput(int output,
|
cannam@180
|
58 SummaryType type,
|
cannam@180
|
59 AveragingMethod avg);
|
cannam@180
|
60
|
cannam@180
|
61 FeatureSet getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
62 AveragingMethod avg);
|
cannam@173
|
63
|
cannam@173
|
64 protected:
|
cannam@174
|
65 Plugin *m_plugin;
|
cannam@174
|
66
|
cannam@173
|
67 SegmentBoundaries m_boundaries;
|
cannam@174
|
68
|
cannam@174
|
69 typedef std::vector<float> ValueList;
|
cannam@174
|
70 typedef std::map<int, ValueList> BinValueMap;
|
cannam@180
|
71 typedef std::vector<RealTime> DurationList;
|
cannam@174
|
72
|
cannam@174
|
73 struct OutputAccumulator {
|
cannam@174
|
74 int count;
|
cannam@180
|
75 BinValueMap values; // bin number -> values ordered by time
|
cannam@180
|
76 DurationList durations;
|
cannam@180
|
77 OutputAccumulator() : count(0), values(), durations() { }
|
cannam@174
|
78 };
|
cannam@174
|
79
|
cannam@174
|
80 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
|
cannam@180
|
81 OutputAccumulatorMap m_accumulators; // output number -> accumulator
|
cannam@180
|
82
|
cannam@180
|
83 typedef std::map<int, RealTime> OutputTimestampMap;
|
cannam@180
|
84 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
|
cannam@174
|
85
|
cannam@174
|
86 struct OutputBinSummary {
|
cannam@180
|
87
|
cannam@180
|
88 int count;
|
cannam@180
|
89
|
cannam@180
|
90 // extents
|
cannam@174
|
91 float minimum;
|
cannam@174
|
92 float maximum;
|
cannam@180
|
93 float sum;
|
cannam@180
|
94
|
cannam@180
|
95 // sample-average results
|
cannam@174
|
96 float median;
|
cannam@174
|
97 float mode;
|
cannam@174
|
98 float variance;
|
cannam@180
|
99
|
cannam@180
|
100 // continuous-time average results
|
cannam@180
|
101 float median_c;
|
cannam@180
|
102 float mode_c;
|
cannam@180
|
103 float mean_c;
|
cannam@180
|
104 float variance_c;
|
cannam@174
|
105 };
|
cannam@174
|
106
|
cannam@174
|
107 typedef std::map<int, OutputBinSummary> OutputSummary;
|
cannam@174
|
108 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
|
cannam@174
|
109 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
|
cannam@174
|
110
|
cannam@174
|
111 OutputSummarySegmentMap m_summaries;
|
cannam@174
|
112
|
cannam@174
|
113 RealTime m_lastTimestamp;
|
cannam@180
|
114 RealTime m_prevDuration;
|
cannam@174
|
115
|
cannam@180
|
116 void accumulate(const FeatureSet &fs, RealTime, bool final);
|
cannam@180
|
117 void accumulate(int output, const Feature &f, RealTime, bool final);
|
cannam@174
|
118 void reduce();
|
cannam@173
|
119 };
|
cannam@173
|
120
|
cannam@173
|
121 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
|
cannam@173
|
122 PluginWrapper(plugin)
|
cannam@173
|
123 {
|
cannam@173
|
124 m_impl = new Impl(plugin, m_inputSampleRate);
|
cannam@173
|
125 }
|
cannam@173
|
126
|
cannam@173
|
127 PluginSummarisingAdapter::~PluginSummarisingAdapter()
|
cannam@173
|
128 {
|
cannam@173
|
129 delete m_impl;
|
cannam@173
|
130 }
|
cannam@173
|
131
|
cannam@173
|
132 Plugin::FeatureSet
|
cannam@173
|
133 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@173
|
134 {
|
cannam@173
|
135 return m_impl->process(inputBuffers, timestamp);
|
cannam@173
|
136 }
|
cannam@173
|
137
|
cannam@174
|
138 Plugin::FeatureSet
|
cannam@174
|
139 PluginSummarisingAdapter::getRemainingFeatures()
|
cannam@174
|
140 {
|
cannam@174
|
141 return m_impl->getRemainingFeatures();
|
cannam@174
|
142 }
|
cannam@174
|
143
|
cannam@175
|
144 Plugin::FeatureList
|
cannam@180
|
145 PluginSummarisingAdapter::getSummaryForOutput(int output,
|
cannam@180
|
146 SummaryType type,
|
cannam@180
|
147 AveragingMethod avg)
|
cannam@175
|
148 {
|
cannam@180
|
149 return m_impl->getSummaryForOutput(output, type, avg);
|
cannam@176
|
150 }
|
cannam@176
|
151
|
cannam@176
|
152 Plugin::FeatureSet
|
cannam@180
|
153 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
154 AveragingMethod avg)
|
cannam@176
|
155 {
|
cannam@180
|
156 return m_impl->getSummaryForAllOutputs(type, avg);
|
cannam@175
|
157 }
|
cannam@173
|
158
|
cannam@173
|
159 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
|
cannam@174
|
160 m_plugin(plugin)
|
cannam@173
|
161 {
|
cannam@173
|
162 }
|
cannam@173
|
163
|
cannam@173
|
164 PluginSummarisingAdapter::Impl::~Impl()
|
cannam@173
|
165 {
|
cannam@173
|
166 }
|
cannam@173
|
167
|
cannam@174
|
168 Plugin::FeatureSet
|
cannam@174
|
169 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@174
|
170 {
|
cannam@174
|
171 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
|
cannam@180
|
172 accumulate(fs, timestamp, false);
|
cannam@174
|
173 m_lastTimestamp = timestamp;
|
cannam@174
|
174 return fs;
|
cannam@174
|
175 }
|
cannam@174
|
176
|
cannam@174
|
177 Plugin::FeatureSet
|
cannam@174
|
178 PluginSummarisingAdapter::Impl::getRemainingFeatures()
|
cannam@174
|
179 {
|
cannam@174
|
180 FeatureSet fs = m_plugin->getRemainingFeatures();
|
cannam@180
|
181 accumulate(fs, m_lastTimestamp, true);
|
cannam@174
|
182 reduce();
|
cannam@174
|
183 return fs;
|
cannam@174
|
184 }
|
cannam@174
|
185
|
cannam@175
|
186 Plugin::FeatureList
|
cannam@180
|
187 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
|
cannam@180
|
188 SummaryType type,
|
cannam@180
|
189 AveragingMethod avg)
|
cannam@175
|
190 {
|
cannam@180
|
191 bool continuous = (avg == ContinuousTimeAverage);
|
cannam@180
|
192
|
cannam@175
|
193 //!!! need to ensure that this is only called after processing is
|
cannam@175
|
194 //!!! complete (at the moment processing is "completed" in the
|
cannam@175
|
195 //!!! call to getRemainingFeatures, but we don't want to require
|
cannam@175
|
196 //!!! the host to call getRemainingFeatures at all unless it
|
cannam@175
|
197 //!!! actually wants the raw features too -- calling getSummary
|
cannam@175
|
198 //!!! should be enough -- we do need to ensure that all data has
|
cannam@175
|
199 //!!! been processed though!)
|
cannam@175
|
200 FeatureList fl;
|
cannam@175
|
201 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
|
cannam@175
|
202 i != m_summaries[output].end(); ++i) {
|
cannam@177
|
203
|
cannam@175
|
204 Feature f;
|
cannam@175
|
205 f.hasTimestamp = true;
|
cannam@175
|
206 f.timestamp = i->first;
|
cannam@175
|
207 f.hasDuration = false;
|
cannam@177
|
208
|
cannam@175
|
209 for (OutputSummary::const_iterator j = i->second.begin();
|
cannam@175
|
210 j != i->second.end(); ++j) {
|
cannam@175
|
211
|
cannam@175
|
212 // these will be ordered by bin number, and no bin numbers
|
cannam@175
|
213 // will be missing except at the end (because of the way
|
cannam@175
|
214 // the accumulators were initially filled in accumulate())
|
cannam@175
|
215
|
cannam@175
|
216 const OutputBinSummary &summary = j->second;
|
cannam@175
|
217 float result = 0.f;
|
cannam@175
|
218
|
cannam@175
|
219 switch (type) {
|
cannam@175
|
220
|
cannam@175
|
221 case Minimum:
|
cannam@175
|
222 result = summary.minimum;
|
cannam@175
|
223 break;
|
cannam@175
|
224
|
cannam@175
|
225 case Maximum:
|
cannam@175
|
226 result = summary.maximum;
|
cannam@175
|
227 break;
|
cannam@175
|
228
|
cannam@175
|
229 case Mean:
|
cannam@180
|
230 if (continuous) {
|
cannam@180
|
231 result = summary.mean_c;
|
cannam@180
|
232 } else if (summary.count) {
|
cannam@175
|
233 result = summary.sum / summary.count;
|
cannam@175
|
234 }
|
cannam@175
|
235 break;
|
cannam@175
|
236
|
cannam@175
|
237 case Median:
|
cannam@180
|
238 if (continuous) result = summary.median_c;
|
cannam@180
|
239 else result = summary.median;
|
cannam@175
|
240 break;
|
cannam@175
|
241
|
cannam@175
|
242 case Mode:
|
cannam@180
|
243 if (continuous) result = summary.mode_c;
|
cannam@180
|
244 else result = summary.mode;
|
cannam@175
|
245 break;
|
cannam@175
|
246
|
cannam@175
|
247 case Sum:
|
cannam@175
|
248 result = summary.sum;
|
cannam@175
|
249 break;
|
cannam@175
|
250
|
cannam@175
|
251 case Variance:
|
cannam@180
|
252 if (continuous) result = summary.variance_c;
|
cannam@180
|
253 else result = summary.variance;
|
cannam@175
|
254 break;
|
cannam@175
|
255
|
cannam@175
|
256 case StandardDeviation:
|
cannam@180
|
257 if (continuous) result = sqrtf(summary.variance_c);
|
cannam@180
|
258 else result = sqrtf(summary.variance);
|
cannam@175
|
259 break;
|
cannam@175
|
260
|
cannam@175
|
261 case Count:
|
cannam@175
|
262 result = summary.count;
|
cannam@175
|
263 break;
|
cannam@180
|
264
|
cannam@180
|
265 default:
|
cannam@180
|
266 break;
|
cannam@175
|
267 }
|
cannam@177
|
268
|
cannam@177
|
269 f.values.push_back(result);
|
cannam@175
|
270 }
|
cannam@175
|
271
|
cannam@175
|
272 fl.push_back(f);
|
cannam@175
|
273 }
|
cannam@175
|
274 return fl;
|
cannam@175
|
275 }
|
cannam@175
|
276
|
cannam@176
|
277 Plugin::FeatureSet
|
cannam@180
|
278 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
279 AveragingMethod avg)
|
cannam@176
|
280 {
|
cannam@176
|
281 FeatureSet fs;
|
cannam@176
|
282 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
|
cannam@176
|
283 i != m_summaries.end(); ++i) {
|
cannam@180
|
284 fs[i->first] = getSummaryForOutput(i->first, type, avg);
|
cannam@176
|
285 }
|
cannam@176
|
286 return fs;
|
cannam@176
|
287 }
|
cannam@176
|
288
|
cannam@174
|
289 void
|
cannam@174
|
290 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
|
cannam@180
|
291 RealTime timestamp,
|
cannam@180
|
292 bool final)
|
cannam@174
|
293 {
|
cannam@174
|
294 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
|
cannam@174
|
295 for (FeatureList::const_iterator j = i->second.begin();
|
cannam@174
|
296 j != i->second.end(); ++j) {
|
cannam@180
|
297 accumulate(i->first, *j, timestamp, final);
|
cannam@174
|
298 }
|
cannam@174
|
299 }
|
cannam@174
|
300 }
|
cannam@174
|
301
|
cannam@174
|
302 void
|
cannam@174
|
303 PluginSummarisingAdapter::Impl::accumulate(int output,
|
cannam@174
|
304 const Feature &f,
|
cannam@180
|
305 RealTime timestamp,
|
cannam@180
|
306 bool final)
|
cannam@174
|
307 {
|
cannam@180
|
308 //!!! to do: use timestamp to determine which segment we're on
|
cannam@180
|
309
|
cannam@174
|
310 m_accumulators[output].count++;
|
cannam@180
|
311
|
cannam@180
|
312 if (m_prevDuration == RealTime::zeroTime) {
|
cannam@180
|
313 if (m_prevTimestamps.find(output) != m_prevTimestamps.end()) {
|
cannam@180
|
314 m_prevDuration = timestamp - m_prevTimestamps[output];
|
cannam@180
|
315 }
|
cannam@180
|
316 }
|
cannam@180
|
317 if (m_prevDuration != RealTime::zeroTime ||
|
cannam@180
|
318 !m_accumulators[output].durations.empty()) {
|
cannam@180
|
319 // ... i.e. if not first result. We don't push a duration
|
cannam@180
|
320 // when we process the first result; then the duration we push
|
cannam@180
|
321 // each time is that for the result before the one we're
|
cannam@180
|
322 // processing, and we push an extra one at the end. This
|
cannam@180
|
323 // permits handling the case where the feature itself doesn't
|
cannam@180
|
324 // have a duration field, and we have to calculate it from the
|
cannam@180
|
325 // time to the following feature. The net effect is simply
|
cannam@180
|
326 // that values[n] and durations[n] refer to the same result.
|
cannam@180
|
327 m_accumulators[output].durations.push_back(m_prevDuration);
|
cannam@180
|
328 }
|
cannam@180
|
329
|
cannam@180
|
330 m_prevTimestamps[output] = timestamp;
|
cannam@180
|
331
|
cannam@174
|
332 for (int i = 0; i < int(f.values.size()); ++i) {
|
cannam@174
|
333 m_accumulators[output].values[i].push_back(f.values[i]);
|
cannam@174
|
334 }
|
cannam@180
|
335
|
cannam@180
|
336 if (final) {
|
cannam@180
|
337 RealTime finalDuration;
|
cannam@180
|
338 if (f.hasDuration) finalDuration = f.duration;
|
cannam@180
|
339 m_accumulators[output].durations.push_back(finalDuration);
|
cannam@180
|
340 }
|
cannam@180
|
341
|
cannam@180
|
342 if (f.hasDuration) m_prevDuration = f.duration;
|
cannam@180
|
343 else m_prevDuration = RealTime::zeroTime;
|
cannam@174
|
344 }
|
cannam@174
|
345
|
cannam@174
|
346 void
|
cannam@174
|
347 PluginSummarisingAdapter::Impl::reduce()
|
cannam@174
|
348 {
|
cannam@174
|
349 RealTime segmentStart = RealTime::zeroTime; //!!!
|
cannam@174
|
350
|
cannam@174
|
351 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
|
cannam@174
|
352 i != m_accumulators.end(); ++i) {
|
cannam@174
|
353
|
cannam@174
|
354 int output = i->first;
|
cannam@174
|
355 OutputAccumulator &accumulator = i->second;
|
cannam@174
|
356
|
cannam@180
|
357 RealTime totalDuration;
|
cannam@180
|
358 size_t dindex = 0;
|
cannam@180
|
359
|
cannam@180
|
360 while (dindex < accumulator.durations.size()) {
|
cannam@180
|
361 totalDuration = totalDuration + accumulator.durations[dindex++];
|
cannam@180
|
362 }
|
cannam@180
|
363
|
cannam@180
|
364 dindex = 0;
|
cannam@180
|
365
|
cannam@174
|
366 for (BinValueMap::iterator j = accumulator.values.begin();
|
cannam@174
|
367 j != accumulator.values.end(); ++j) {
|
cannam@174
|
368
|
cannam@180
|
369 // work on all values over time for a single bin
|
cannam@180
|
370
|
cannam@174
|
371 int bin = j->first;
|
cannam@174
|
372 ValueList &values = j->second;
|
cannam@180
|
373 const DurationList &durations = accumulator.durations;
|
cannam@174
|
374
|
cannam@174
|
375 OutputBinSummary summary;
|
cannam@180
|
376
|
cannam@180
|
377 summary.count = accumulator.count;
|
cannam@180
|
378
|
cannam@174
|
379 summary.minimum = 0.f;
|
cannam@174
|
380 summary.maximum = 0.f;
|
cannam@180
|
381
|
cannam@174
|
382 summary.median = 0.f;
|
cannam@174
|
383 summary.mode = 0.f;
|
cannam@174
|
384 summary.sum = 0.f;
|
cannam@174
|
385 summary.variance = 0.f;
|
cannam@180
|
386
|
cannam@180
|
387 summary.median_c = 0.f;
|
cannam@180
|
388 summary.mode_c = 0.f;
|
cannam@180
|
389 summary.mean_c = 0.f;
|
cannam@180
|
390 summary.variance_c = 0.f;
|
cannam@180
|
391
|
cannam@174
|
392 if (summary.count == 0 || values.empty()) continue;
|
cannam@174
|
393
|
cannam@174
|
394 std::sort(values.begin(), values.end());
|
cannam@174
|
395 int sz = values.size();
|
cannam@174
|
396
|
cannam@180
|
397 if (sz != durations.size()) {
|
cannam@180
|
398 //!!! is this reasonable?
|
cannam@180
|
399 std::cerr << "WARNING: sz " << sz << " != durations.size() "
|
cannam@180
|
400 << durations.size() << std::endl;
|
cannam@180
|
401 }
|
cannam@180
|
402
|
cannam@174
|
403 summary.minimum = values[0];
|
cannam@174
|
404 summary.maximum = values[sz-1];
|
cannam@174
|
405
|
cannam@174
|
406 if (sz % 2 == 1) {
|
cannam@174
|
407 summary.median = values[sz/2];
|
cannam@174
|
408 } else {
|
cannam@174
|
409 summary.median = (values[sz/2] + values[sz/2 + 1]) / 2;
|
cannam@174
|
410 }
|
cannam@174
|
411
|
cannam@174
|
412 std::map<float, int> distribution;
|
cannam@174
|
413
|
cannam@174
|
414 for (int k = 0; k < sz; ++k) {
|
cannam@174
|
415 summary.sum += values[k];
|
cannam@180
|
416 distribution[values[k]] += 1;
|
cannam@174
|
417 }
|
cannam@174
|
418
|
cannam@174
|
419 int md = 0;
|
cannam@174
|
420
|
cannam@174
|
421 for (std::map<float, int>::iterator di = distribution.begin();
|
cannam@174
|
422 di != distribution.end(); ++di) {
|
cannam@174
|
423 if (di->second > md) {
|
cannam@174
|
424 md = di->second;
|
cannam@174
|
425 summary.mode = di->first;
|
cannam@174
|
426 }
|
cannam@174
|
427 }
|
cannam@174
|
428
|
cannam@174
|
429 distribution.clear();
|
cannam@174
|
430
|
cannam@180
|
431 //!!! we want to omit this bit if the features all have
|
cannam@180
|
432 //!!! equal duration (and set mode_c equal to mode instead)
|
cannam@180
|
433
|
cannam@180
|
434 std::map<float, RealTime> distribution_c;
|
cannam@180
|
435
|
cannam@180
|
436 for (int k = 0; k < sz; ++k) {
|
cannam@180
|
437 distribution_c[values[k]] =
|
cannam@180
|
438 distribution_c[values[k]] + durations[k];
|
cannam@180
|
439 }
|
cannam@180
|
440
|
cannam@180
|
441 RealTime mrd = RealTime::zeroTime;
|
cannam@180
|
442
|
cannam@180
|
443 for (std::map<float, RealTime>::iterator di = distribution_c.begin();
|
cannam@180
|
444 di != distribution_c.end(); ++di) {
|
cannam@180
|
445 if (di->second > mrd) {
|
cannam@180
|
446 mrd = di->second;
|
cannam@180
|
447 summary.mode_c = di->first;
|
cannam@180
|
448 }
|
cannam@180
|
449 }
|
cannam@180
|
450
|
cannam@180
|
451 distribution_c.clear();
|
cannam@180
|
452
|
cannam@180
|
453 //!!! handle mean_c, median_c, variance_c
|
cannam@180
|
454
|
cannam@174
|
455 float mean = summary.sum / summary.count;
|
cannam@174
|
456
|
cannam@174
|
457 for (int k = 0; k < sz; ++k) {
|
cannam@174
|
458 summary.variance += (values[k] - mean) * (values[k] - mean);
|
cannam@174
|
459 }
|
cannam@174
|
460 summary.variance /= summary.count;
|
cannam@174
|
461
|
cannam@174
|
462 m_summaries[output][segmentStart][bin] = summary;
|
cannam@174
|
463 }
|
cannam@174
|
464 }
|
cannam@175
|
465
|
cannam@175
|
466 m_accumulators.clear();
|
cannam@174
|
467 }
|
cannam@174
|
468
|
cannam@174
|
469
|
cannam@174
|
470 }
|
cannam@174
|
471
|
cannam@174
|
472 }
|
cannam@174
|
473
|