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@181
|
66 float m_inputSampleRate;
|
cannam@174
|
67
|
cannam@173
|
68 SegmentBoundaries m_boundaries;
|
cannam@174
|
69
|
cannam@174
|
70 typedef std::vector<float> ValueList;
|
cannam@174
|
71 typedef std::map<int, ValueList> BinValueMap;
|
cannam@180
|
72 typedef std::vector<RealTime> DurationList;
|
cannam@174
|
73
|
cannam@174
|
74 struct OutputAccumulator {
|
cannam@174
|
75 int count;
|
cannam@180
|
76 BinValueMap values; // bin number -> values ordered by time
|
cannam@180
|
77 DurationList durations;
|
cannam@180
|
78 OutputAccumulator() : count(0), values(), durations() { }
|
cannam@174
|
79 };
|
cannam@174
|
80
|
cannam@174
|
81 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
|
cannam@180
|
82 OutputAccumulatorMap m_accumulators; // output number -> accumulator
|
cannam@180
|
83
|
cannam@180
|
84 typedef std::map<int, RealTime> OutputTimestampMap;
|
cannam@180
|
85 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
|
cannam@183
|
86 OutputTimestampMap m_prevDurations; // output number -> durations
|
cannam@174
|
87
|
cannam@174
|
88 struct OutputBinSummary {
|
cannam@180
|
89
|
cannam@180
|
90 int count;
|
cannam@180
|
91
|
cannam@180
|
92 // extents
|
cannam@174
|
93 float minimum;
|
cannam@174
|
94 float maximum;
|
cannam@180
|
95 float sum;
|
cannam@180
|
96
|
cannam@180
|
97 // sample-average results
|
cannam@174
|
98 float median;
|
cannam@174
|
99 float mode;
|
cannam@174
|
100 float variance;
|
cannam@180
|
101
|
cannam@180
|
102 // continuous-time average results
|
cannam@180
|
103 float median_c;
|
cannam@180
|
104 float mode_c;
|
cannam@180
|
105 float mean_c;
|
cannam@180
|
106 float variance_c;
|
cannam@174
|
107 };
|
cannam@174
|
108
|
cannam@174
|
109 typedef std::map<int, OutputBinSummary> OutputSummary;
|
cannam@174
|
110 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
|
cannam@174
|
111 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
|
cannam@174
|
112
|
cannam@174
|
113 OutputSummarySegmentMap m_summaries;
|
cannam@174
|
114
|
cannam@183
|
115 bool m_reduced;
|
cannam@174
|
116 RealTime m_lastTimestamp;
|
cannam@174
|
117
|
cannam@180
|
118 void accumulate(const FeatureSet &fs, RealTime, bool final);
|
cannam@180
|
119 void accumulate(int output, const Feature &f, RealTime, bool final);
|
cannam@174
|
120 void reduce();
|
cannam@173
|
121 };
|
cannam@173
|
122
|
cannam@173
|
123 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
|
cannam@173
|
124 PluginWrapper(plugin)
|
cannam@173
|
125 {
|
cannam@173
|
126 m_impl = new Impl(plugin, m_inputSampleRate);
|
cannam@173
|
127 }
|
cannam@173
|
128
|
cannam@173
|
129 PluginSummarisingAdapter::~PluginSummarisingAdapter()
|
cannam@173
|
130 {
|
cannam@173
|
131 delete m_impl;
|
cannam@173
|
132 }
|
cannam@173
|
133
|
cannam@173
|
134 Plugin::FeatureSet
|
cannam@173
|
135 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@173
|
136 {
|
cannam@173
|
137 return m_impl->process(inputBuffers, timestamp);
|
cannam@173
|
138 }
|
cannam@173
|
139
|
cannam@174
|
140 Plugin::FeatureSet
|
cannam@174
|
141 PluginSummarisingAdapter::getRemainingFeatures()
|
cannam@174
|
142 {
|
cannam@174
|
143 return m_impl->getRemainingFeatures();
|
cannam@174
|
144 }
|
cannam@174
|
145
|
cannam@175
|
146 Plugin::FeatureList
|
cannam@180
|
147 PluginSummarisingAdapter::getSummaryForOutput(int output,
|
cannam@180
|
148 SummaryType type,
|
cannam@180
|
149 AveragingMethod avg)
|
cannam@175
|
150 {
|
cannam@180
|
151 return m_impl->getSummaryForOutput(output, type, avg);
|
cannam@176
|
152 }
|
cannam@176
|
153
|
cannam@176
|
154 Plugin::FeatureSet
|
cannam@180
|
155 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
156 AveragingMethod avg)
|
cannam@176
|
157 {
|
cannam@180
|
158 return m_impl->getSummaryForAllOutputs(type, avg);
|
cannam@175
|
159 }
|
cannam@173
|
160
|
cannam@173
|
161 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
|
cannam@181
|
162 m_plugin(plugin),
|
cannam@183
|
163 m_inputSampleRate(inputSampleRate),
|
cannam@183
|
164 m_reduced(false)
|
cannam@173
|
165 {
|
cannam@173
|
166 }
|
cannam@173
|
167
|
cannam@173
|
168 PluginSummarisingAdapter::Impl::~Impl()
|
cannam@173
|
169 {
|
cannam@173
|
170 }
|
cannam@173
|
171
|
cannam@174
|
172 Plugin::FeatureSet
|
cannam@174
|
173 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@174
|
174 {
|
cannam@183
|
175 if (m_reduced) {
|
cannam@183
|
176 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
|
cannam@183
|
177 }
|
cannam@174
|
178 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
|
cannam@180
|
179 accumulate(fs, timestamp, false);
|
cannam@174
|
180 m_lastTimestamp = timestamp;
|
cannam@174
|
181 return fs;
|
cannam@174
|
182 }
|
cannam@174
|
183
|
cannam@174
|
184 Plugin::FeatureSet
|
cannam@174
|
185 PluginSummarisingAdapter::Impl::getRemainingFeatures()
|
cannam@174
|
186 {
|
cannam@183
|
187 if (m_reduced) {
|
cannam@183
|
188 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
|
cannam@183
|
189 }
|
cannam@174
|
190 FeatureSet fs = m_plugin->getRemainingFeatures();
|
cannam@180
|
191 accumulate(fs, m_lastTimestamp, true);
|
cannam@174
|
192 return fs;
|
cannam@174
|
193 }
|
cannam@174
|
194
|
cannam@175
|
195 Plugin::FeatureList
|
cannam@180
|
196 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
|
cannam@180
|
197 SummaryType type,
|
cannam@180
|
198 AveragingMethod avg)
|
cannam@175
|
199 {
|
cannam@183
|
200 if (!m_reduced) reduce();
|
cannam@183
|
201
|
cannam@180
|
202 bool continuous = (avg == ContinuousTimeAverage);
|
cannam@180
|
203
|
cannam@175
|
204 FeatureList fl;
|
cannam@175
|
205 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
|
cannam@175
|
206 i != m_summaries[output].end(); ++i) {
|
cannam@177
|
207
|
cannam@175
|
208 Feature f;
|
cannam@175
|
209 f.hasTimestamp = true;
|
cannam@175
|
210 f.timestamp = i->first;
|
cannam@175
|
211 f.hasDuration = false;
|
cannam@177
|
212
|
cannam@175
|
213 for (OutputSummary::const_iterator j = i->second.begin();
|
cannam@175
|
214 j != i->second.end(); ++j) {
|
cannam@175
|
215
|
cannam@175
|
216 // these will be ordered by bin number, and no bin numbers
|
cannam@175
|
217 // will be missing except at the end (because of the way
|
cannam@175
|
218 // the accumulators were initially filled in accumulate())
|
cannam@175
|
219
|
cannam@175
|
220 const OutputBinSummary &summary = j->second;
|
cannam@175
|
221 float result = 0.f;
|
cannam@175
|
222
|
cannam@175
|
223 switch (type) {
|
cannam@175
|
224
|
cannam@175
|
225 case Minimum:
|
cannam@175
|
226 result = summary.minimum;
|
cannam@175
|
227 break;
|
cannam@175
|
228
|
cannam@175
|
229 case Maximum:
|
cannam@175
|
230 result = summary.maximum;
|
cannam@175
|
231 break;
|
cannam@175
|
232
|
cannam@175
|
233 case Mean:
|
cannam@180
|
234 if (continuous) {
|
cannam@180
|
235 result = summary.mean_c;
|
cannam@180
|
236 } else if (summary.count) {
|
cannam@175
|
237 result = summary.sum / summary.count;
|
cannam@175
|
238 }
|
cannam@175
|
239 break;
|
cannam@175
|
240
|
cannam@175
|
241 case Median:
|
cannam@180
|
242 if (continuous) result = summary.median_c;
|
cannam@180
|
243 else result = summary.median;
|
cannam@175
|
244 break;
|
cannam@175
|
245
|
cannam@175
|
246 case Mode:
|
cannam@180
|
247 if (continuous) result = summary.mode_c;
|
cannam@180
|
248 else result = summary.mode;
|
cannam@175
|
249 break;
|
cannam@175
|
250
|
cannam@175
|
251 case Sum:
|
cannam@175
|
252 result = summary.sum;
|
cannam@175
|
253 break;
|
cannam@175
|
254
|
cannam@175
|
255 case Variance:
|
cannam@180
|
256 if (continuous) result = summary.variance_c;
|
cannam@180
|
257 else result = summary.variance;
|
cannam@175
|
258 break;
|
cannam@175
|
259
|
cannam@175
|
260 case StandardDeviation:
|
cannam@180
|
261 if (continuous) result = sqrtf(summary.variance_c);
|
cannam@180
|
262 else result = sqrtf(summary.variance);
|
cannam@175
|
263 break;
|
cannam@175
|
264
|
cannam@175
|
265 case Count:
|
cannam@175
|
266 result = summary.count;
|
cannam@175
|
267 break;
|
cannam@180
|
268
|
cannam@180
|
269 default:
|
cannam@180
|
270 break;
|
cannam@175
|
271 }
|
cannam@177
|
272
|
cannam@177
|
273 f.values.push_back(result);
|
cannam@175
|
274 }
|
cannam@175
|
275
|
cannam@175
|
276 fl.push_back(f);
|
cannam@175
|
277 }
|
cannam@175
|
278 return fl;
|
cannam@175
|
279 }
|
cannam@175
|
280
|
cannam@176
|
281 Plugin::FeatureSet
|
cannam@180
|
282 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
283 AveragingMethod avg)
|
cannam@176
|
284 {
|
cannam@183
|
285 if (!m_reduced) reduce();
|
cannam@183
|
286
|
cannam@176
|
287 FeatureSet fs;
|
cannam@176
|
288 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
|
cannam@176
|
289 i != m_summaries.end(); ++i) {
|
cannam@180
|
290 fs[i->first] = getSummaryForOutput(i->first, type, avg);
|
cannam@176
|
291 }
|
cannam@176
|
292 return fs;
|
cannam@176
|
293 }
|
cannam@176
|
294
|
cannam@174
|
295 void
|
cannam@174
|
296 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
|
cannam@180
|
297 RealTime timestamp,
|
cannam@180
|
298 bool final)
|
cannam@174
|
299 {
|
cannam@174
|
300 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
|
cannam@174
|
301 for (FeatureList::const_iterator j = i->second.begin();
|
cannam@174
|
302 j != i->second.end(); ++j) {
|
cannam@182
|
303 if (j->hasTimestamp) {
|
cannam@182
|
304 accumulate(i->first, *j, j->timestamp, final);
|
cannam@182
|
305 } else {
|
cannam@182
|
306 //!!! is this correct?
|
cannam@182
|
307 accumulate(i->first, *j, timestamp, final);
|
cannam@182
|
308 }
|
cannam@174
|
309 }
|
cannam@174
|
310 }
|
cannam@174
|
311 }
|
cannam@174
|
312
|
cannam@174
|
313 void
|
cannam@174
|
314 PluginSummarisingAdapter::Impl::accumulate(int output,
|
cannam@174
|
315 const Feature &f,
|
cannam@180
|
316 RealTime timestamp,
|
cannam@180
|
317 bool final)
|
cannam@174
|
318 {
|
cannam@180
|
319 //!!! to do: use timestamp to determine which segment we're on
|
cannam@180
|
320
|
cannam@174
|
321 m_accumulators[output].count++;
|
cannam@180
|
322
|
cannam@182
|
323 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << std::endl;
|
cannam@182
|
324
|
cannam@182
|
325 //!!! also, this will not work if we are called repeatedly with
|
cannam@182
|
326 //!!! the same timestamp -- no values will be registered until a
|
cannam@182
|
327 //!!! new timestamp is seen -- we need a better test for "not
|
cannam@182
|
328 //!!! first result" below
|
cannam@182
|
329
|
cannam@183
|
330 if (m_prevDurations[output] == RealTime::zeroTime) {
|
cannam@180
|
331 if (m_prevTimestamps.find(output) != m_prevTimestamps.end()) {
|
cannam@183
|
332 m_prevDurations[output] = timestamp - m_prevTimestamps[output];
|
cannam@180
|
333 }
|
cannam@180
|
334 }
|
cannam@183
|
335 if (m_prevDurations[output] != RealTime::zeroTime ||
|
cannam@180
|
336 !m_accumulators[output].durations.empty()) {
|
cannam@180
|
337 // ... i.e. if not first result. We don't push a duration
|
cannam@180
|
338 // when we process the first result; then the duration we push
|
cannam@180
|
339 // each time is that for the result before the one we're
|
cannam@180
|
340 // processing, and we push an extra one at the end. This
|
cannam@180
|
341 // permits handling the case where the feature itself doesn't
|
cannam@180
|
342 // have a duration field, and we have to calculate it from the
|
cannam@180
|
343 // time to the following feature. The net effect is simply
|
cannam@180
|
344 // that values[n] and durations[n] refer to the same result.
|
cannam@183
|
345 m_accumulators[output].durations.push_back(m_prevDurations[output]);
|
cannam@180
|
346 }
|
cannam@180
|
347
|
cannam@180
|
348 m_prevTimestamps[output] = timestamp;
|
cannam@180
|
349
|
cannam@174
|
350 for (int i = 0; i < int(f.values.size()); ++i) {
|
cannam@174
|
351 m_accumulators[output].values[i].push_back(f.values[i]);
|
cannam@174
|
352 }
|
cannam@180
|
353
|
cannam@180
|
354 if (final) {
|
cannam@180
|
355 RealTime finalDuration;
|
cannam@180
|
356 if (f.hasDuration) finalDuration = f.duration;
|
cannam@180
|
357 m_accumulators[output].durations.push_back(finalDuration);
|
cannam@180
|
358 }
|
cannam@180
|
359
|
cannam@183
|
360 if (f.hasDuration) m_prevDurations[output] = f.duration;
|
cannam@183
|
361 else m_prevDurations[output] = RealTime::zeroTime;
|
cannam@174
|
362 }
|
cannam@174
|
363
|
cannam@181
|
364 struct ValueDurationFloatPair
|
cannam@181
|
365 {
|
cannam@181
|
366 float value;
|
cannam@181
|
367 float duration;
|
cannam@181
|
368
|
cannam@181
|
369 ValueDurationFloatPair() : value(0), duration(0) { }
|
cannam@181
|
370 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
|
cannam@181
|
371 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
|
cannam@181
|
372 value = p.value;
|
cannam@181
|
373 duration = p.duration;
|
cannam@181
|
374 return *this;
|
cannam@181
|
375 }
|
cannam@181
|
376 bool operator<(const ValueDurationFloatPair &p) const {
|
cannam@181
|
377 return value < p.value;
|
cannam@181
|
378 }
|
cannam@181
|
379 };
|
cannam@181
|
380
|
cannam@181
|
381 static double toSec(const RealTime &r)
|
cannam@181
|
382 {
|
cannam@181
|
383 return r.sec + double(r.nsec) / 1000000000.0;
|
cannam@181
|
384 }
|
cannam@181
|
385
|
cannam@174
|
386 void
|
cannam@174
|
387 PluginSummarisingAdapter::Impl::reduce()
|
cannam@174
|
388 {
|
cannam@174
|
389 RealTime segmentStart = RealTime::zeroTime; //!!!
|
cannam@174
|
390
|
cannam@174
|
391 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
|
cannam@174
|
392 i != m_accumulators.end(); ++i) {
|
cannam@174
|
393
|
cannam@174
|
394 int output = i->first;
|
cannam@174
|
395 OutputAccumulator &accumulator = i->second;
|
cannam@174
|
396
|
cannam@182
|
397 double totalDuration = 0.0;
|
cannam@181
|
398 for (int k = 0; k < accumulator.durations.size(); ++k) {
|
cannam@181
|
399 totalDuration += toSec(accumulator.durations[k]);
|
cannam@180
|
400 }
|
cannam@180
|
401
|
cannam@174
|
402 for (BinValueMap::iterator j = accumulator.values.begin();
|
cannam@174
|
403 j != accumulator.values.end(); ++j) {
|
cannam@174
|
404
|
cannam@180
|
405 // work on all values over time for a single bin
|
cannam@180
|
406
|
cannam@174
|
407 int bin = j->first;
|
cannam@181
|
408 const ValueList &values = j->second;
|
cannam@180
|
409 const DurationList &durations = accumulator.durations;
|
cannam@174
|
410
|
cannam@174
|
411 OutputBinSummary summary;
|
cannam@180
|
412
|
cannam@180
|
413 summary.count = accumulator.count;
|
cannam@180
|
414
|
cannam@174
|
415 summary.minimum = 0.f;
|
cannam@174
|
416 summary.maximum = 0.f;
|
cannam@180
|
417
|
cannam@174
|
418 summary.median = 0.f;
|
cannam@174
|
419 summary.mode = 0.f;
|
cannam@174
|
420 summary.sum = 0.f;
|
cannam@174
|
421 summary.variance = 0.f;
|
cannam@180
|
422
|
cannam@180
|
423 summary.median_c = 0.f;
|
cannam@180
|
424 summary.mode_c = 0.f;
|
cannam@180
|
425 summary.mean_c = 0.f;
|
cannam@180
|
426 summary.variance_c = 0.f;
|
cannam@180
|
427
|
cannam@174
|
428 if (summary.count == 0 || values.empty()) continue;
|
cannam@174
|
429
|
cannam@174
|
430 int sz = values.size();
|
cannam@174
|
431
|
cannam@180
|
432 if (sz != durations.size()) {
|
cannam@180
|
433 std::cerr << "WARNING: sz " << sz << " != durations.size() "
|
cannam@180
|
434 << durations.size() << std::endl;
|
cannam@181
|
435 // while (durations.size() < sz) {
|
cannam@181
|
436 // durations.push_back(RealTime::zeroTime);
|
cannam@181
|
437 // }
|
cannam@181
|
438 //!!! then what?
|
cannam@180
|
439 }
|
cannam@180
|
440
|
cannam@181
|
441 std::vector<ValueDurationFloatPair> valvec;
|
cannam@181
|
442
|
cannam@181
|
443 for (int k = 0; k < sz; ++k) {
|
cannam@181
|
444 valvec.push_back(ValueDurationFloatPair(values[k],
|
cannam@181
|
445 toSec(durations[k])));
|
cannam@181
|
446 }
|
cannam@181
|
447
|
cannam@181
|
448 std::sort(valvec.begin(), valvec.end());
|
cannam@181
|
449
|
cannam@181
|
450 summary.minimum = valvec[0].value;
|
cannam@181
|
451 summary.maximum = valvec[sz-1].value;
|
cannam@174
|
452
|
cannam@174
|
453 if (sz % 2 == 1) {
|
cannam@181
|
454 summary.median = valvec[sz/2].value;
|
cannam@174
|
455 } else {
|
cannam@181
|
456 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
|
cannam@174
|
457 }
|
cannam@181
|
458
|
cannam@181
|
459 double duracc = 0.0;
|
cannam@181
|
460 summary.median_c = valvec[sz-1].value;
|
cannam@174
|
461
|
cannam@181
|
462 for (int k = 0; k < sz; ++k) {
|
cannam@181
|
463 duracc += valvec[k].duration;
|
cannam@181
|
464 if (duracc > totalDuration/2) {
|
cannam@181
|
465 summary.median_c = valvec[k].value;
|
cannam@181
|
466 break;
|
cannam@181
|
467 }
|
cannam@181
|
468 }
|
cannam@181
|
469
|
cannam@174
|
470 std::map<float, int> distribution;
|
cannam@174
|
471
|
cannam@174
|
472 for (int k = 0; k < sz; ++k) {
|
cannam@174
|
473 summary.sum += values[k];
|
cannam@180
|
474 distribution[values[k]] += 1;
|
cannam@174
|
475 }
|
cannam@174
|
476
|
cannam@174
|
477 int md = 0;
|
cannam@174
|
478
|
cannam@174
|
479 for (std::map<float, int>::iterator di = distribution.begin();
|
cannam@174
|
480 di != distribution.end(); ++di) {
|
cannam@174
|
481 if (di->second > md) {
|
cannam@174
|
482 md = di->second;
|
cannam@174
|
483 summary.mode = di->first;
|
cannam@174
|
484 }
|
cannam@174
|
485 }
|
cannam@174
|
486
|
cannam@174
|
487 distribution.clear();
|
cannam@174
|
488
|
cannam@180
|
489 //!!! we want to omit this bit if the features all have
|
cannam@180
|
490 //!!! equal duration (and set mode_c equal to mode instead)
|
cannam@180
|
491
|
cannam@181
|
492 std::map<float, double> distribution_c;
|
cannam@180
|
493
|
cannam@180
|
494 for (int k = 0; k < sz; ++k) {
|
cannam@181
|
495 distribution_c[values[k]] += toSec(durations[k]);
|
cannam@180
|
496 }
|
cannam@180
|
497
|
cannam@181
|
498 double mrd = 0.0;
|
cannam@180
|
499
|
cannam@181
|
500 for (std::map<float, double>::iterator di = distribution_c.begin();
|
cannam@180
|
501 di != distribution_c.end(); ++di) {
|
cannam@180
|
502 if (di->second > mrd) {
|
cannam@180
|
503 mrd = di->second;
|
cannam@180
|
504 summary.mode_c = di->first;
|
cannam@180
|
505 }
|
cannam@180
|
506 }
|
cannam@180
|
507
|
cannam@180
|
508 distribution_c.clear();
|
cannam@180
|
509
|
cannam@181
|
510 if (totalDuration > 0.0) {
|
cannam@181
|
511
|
cannam@181
|
512 double sum_c = 0.0;
|
cannam@181
|
513
|
cannam@181
|
514 for (int k = 0; k < sz; ++k) {
|
cannam@181
|
515 double value = values[k] * toSec(durations[k]);
|
cannam@181
|
516 sum_c += value;
|
cannam@181
|
517 }
|
cannam@182
|
518
|
cannam@182
|
519 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
|
cannam@182
|
520 << sum_c / totalDuration << std::endl;
|
cannam@181
|
521
|
cannam@181
|
522 summary.mean_c = sum_c / totalDuration;
|
cannam@181
|
523
|
cannam@181
|
524 for (int k = 0; k < sz; ++k) {
|
cannam@181
|
525 double value = values[k] * toSec(durations[k]);
|
cannam@181
|
526 summary.variance_c +=
|
cannam@181
|
527 (value - summary.mean_c) * (value - summary.mean_c);
|
cannam@181
|
528 }
|
cannam@181
|
529
|
cannam@181
|
530 summary.variance_c /= summary.count;
|
cannam@181
|
531 }
|
cannam@181
|
532
|
cannam@181
|
533 //!!! still to handle: median_c
|
cannam@180
|
534
|
cannam@174
|
535 float mean = summary.sum / summary.count;
|
cannam@174
|
536
|
cannam@182
|
537 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
|
cannam@182
|
538 << summary.sum / summary.count << std::endl;
|
cannam@182
|
539
|
cannam@174
|
540 for (int k = 0; k < sz; ++k) {
|
cannam@174
|
541 summary.variance += (values[k] - mean) * (values[k] - mean);
|
cannam@174
|
542 }
|
cannam@174
|
543 summary.variance /= summary.count;
|
cannam@174
|
544
|
cannam@174
|
545 m_summaries[output][segmentStart][bin] = summary;
|
cannam@174
|
546 }
|
cannam@174
|
547 }
|
cannam@175
|
548
|
cannam@175
|
549 m_accumulators.clear();
|
cannam@183
|
550 m_reduced = true;
|
cannam@174
|
551 }
|
cannam@174
|
552
|
cannam@174
|
553
|
cannam@174
|
554 }
|
cannam@174
|
555
|
cannam@174
|
556 }
|
cannam@174
|
557
|