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@184
|
41 #include <climits>
|
cannam@174
|
42
|
cannam@173
|
43 namespace Vamp {
|
cannam@173
|
44
|
cannam@173
|
45 namespace HostExt {
|
cannam@173
|
46
|
cannam@173
|
47 class PluginSummarisingAdapter::Impl
|
cannam@173
|
48 {
|
cannam@173
|
49 public:
|
cannam@173
|
50 Impl(Plugin *plugin, float inputSampleRate);
|
cannam@173
|
51 ~Impl();
|
cannam@173
|
52
|
cannam@173
|
53 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
|
cannam@173
|
54 FeatureSet getRemainingFeatures();
|
cannam@173
|
55
|
cannam@173
|
56 void setSummarySegmentBoundaries(const SegmentBoundaries &);
|
cannam@173
|
57
|
cannam@180
|
58 FeatureList getSummaryForOutput(int output,
|
cannam@180
|
59 SummaryType type,
|
cannam@180
|
60 AveragingMethod avg);
|
cannam@180
|
61
|
cannam@180
|
62 FeatureSet getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
63 AveragingMethod avg);
|
cannam@173
|
64
|
cannam@173
|
65 protected:
|
cannam@174
|
66 Plugin *m_plugin;
|
cannam@181
|
67 float m_inputSampleRate;
|
cannam@174
|
68
|
cannam@173
|
69 SegmentBoundaries m_boundaries;
|
cannam@185
|
70
|
cannam@174
|
71 typedef std::vector<float> ValueList;
|
cannam@185
|
72
|
cannam@185
|
73 struct Result { // smaller than Feature
|
cannam@185
|
74 RealTime time;
|
cannam@185
|
75 RealTime duration;
|
cannam@185
|
76 ValueList values; // bin number -> value
|
cannam@185
|
77 };
|
cannam@185
|
78
|
cannam@185
|
79 typedef std::vector<Result> ResultList;
|
cannam@185
|
80
|
cannam@174
|
81 struct OutputAccumulator {
|
cannam@185
|
82 int bins;
|
cannam@185
|
83 ResultList results;
|
cannam@185
|
84 OutputAccumulator() : bins(0) { }
|
cannam@174
|
85 };
|
cannam@174
|
86
|
cannam@174
|
87 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
|
cannam@180
|
88 OutputAccumulatorMap m_accumulators; // output number -> accumulator
|
cannam@180
|
89
|
cannam@185
|
90 typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap;
|
cannam@185
|
91 typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap;
|
cannam@185
|
92 OutputSegmentAccumulatorMap m_segmentedAccumulators;
|
cannam@185
|
93
|
cannam@180
|
94 typedef std::map<int, RealTime> OutputTimestampMap;
|
cannam@180
|
95 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
|
cannam@183
|
96 OutputTimestampMap m_prevDurations; // output number -> durations
|
cannam@174
|
97
|
cannam@174
|
98 struct OutputBinSummary {
|
cannam@180
|
99
|
cannam@180
|
100 int count;
|
cannam@180
|
101
|
cannam@180
|
102 // extents
|
cannam@174
|
103 float minimum;
|
cannam@174
|
104 float maximum;
|
cannam@180
|
105 float sum;
|
cannam@180
|
106
|
cannam@180
|
107 // sample-average results
|
cannam@174
|
108 float median;
|
cannam@174
|
109 float mode;
|
cannam@174
|
110 float variance;
|
cannam@180
|
111
|
cannam@180
|
112 // continuous-time average results
|
cannam@180
|
113 float median_c;
|
cannam@180
|
114 float mode_c;
|
cannam@180
|
115 float mean_c;
|
cannam@180
|
116 float variance_c;
|
cannam@174
|
117 };
|
cannam@174
|
118
|
cannam@174
|
119 typedef std::map<int, OutputBinSummary> OutputSummary;
|
cannam@174
|
120 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
|
cannam@174
|
121 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
|
cannam@174
|
122
|
cannam@174
|
123 OutputSummarySegmentMap m_summaries;
|
cannam@174
|
124
|
cannam@183
|
125 bool m_reduced;
|
cannam@174
|
126 RealTime m_lastTimestamp;
|
cannam@174
|
127
|
cannam@180
|
128 void accumulate(const FeatureSet &fs, RealTime, bool final);
|
cannam@180
|
129 void accumulate(int output, const Feature &f, RealTime, bool final);
|
cannam@184
|
130 void accumulateFinalDurations();
|
cannam@185
|
131 void segment();
|
cannam@174
|
132 void reduce();
|
cannam@173
|
133 };
|
cannam@184
|
134
|
cannam@184
|
135 static RealTime INVALID_DURATION(INT_MIN, INT_MIN);
|
cannam@173
|
136
|
cannam@173
|
137 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
|
cannam@173
|
138 PluginWrapper(plugin)
|
cannam@173
|
139 {
|
cannam@173
|
140 m_impl = new Impl(plugin, m_inputSampleRate);
|
cannam@173
|
141 }
|
cannam@173
|
142
|
cannam@173
|
143 PluginSummarisingAdapter::~PluginSummarisingAdapter()
|
cannam@173
|
144 {
|
cannam@173
|
145 delete m_impl;
|
cannam@173
|
146 }
|
cannam@173
|
147
|
cannam@173
|
148 Plugin::FeatureSet
|
cannam@173
|
149 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@173
|
150 {
|
cannam@173
|
151 return m_impl->process(inputBuffers, timestamp);
|
cannam@173
|
152 }
|
cannam@173
|
153
|
cannam@174
|
154 Plugin::FeatureSet
|
cannam@174
|
155 PluginSummarisingAdapter::getRemainingFeatures()
|
cannam@174
|
156 {
|
cannam@174
|
157 return m_impl->getRemainingFeatures();
|
cannam@174
|
158 }
|
cannam@174
|
159
|
cannam@175
|
160 Plugin::FeatureList
|
cannam@180
|
161 PluginSummarisingAdapter::getSummaryForOutput(int output,
|
cannam@180
|
162 SummaryType type,
|
cannam@180
|
163 AveragingMethod avg)
|
cannam@175
|
164 {
|
cannam@180
|
165 return m_impl->getSummaryForOutput(output, type, avg);
|
cannam@176
|
166 }
|
cannam@176
|
167
|
cannam@176
|
168 Plugin::FeatureSet
|
cannam@180
|
169 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
170 AveragingMethod avg)
|
cannam@176
|
171 {
|
cannam@180
|
172 return m_impl->getSummaryForAllOutputs(type, avg);
|
cannam@175
|
173 }
|
cannam@173
|
174
|
cannam@173
|
175 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
|
cannam@181
|
176 m_plugin(plugin),
|
cannam@183
|
177 m_inputSampleRate(inputSampleRate),
|
cannam@183
|
178 m_reduced(false)
|
cannam@173
|
179 {
|
cannam@173
|
180 }
|
cannam@173
|
181
|
cannam@173
|
182 PluginSummarisingAdapter::Impl::~Impl()
|
cannam@173
|
183 {
|
cannam@173
|
184 }
|
cannam@173
|
185
|
cannam@174
|
186 Plugin::FeatureSet
|
cannam@174
|
187 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp)
|
cannam@174
|
188 {
|
cannam@183
|
189 if (m_reduced) {
|
cannam@183
|
190 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
|
cannam@183
|
191 }
|
cannam@174
|
192 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
|
cannam@180
|
193 accumulate(fs, timestamp, false);
|
cannam@185
|
194 //!!! should really be "timestamp plus step size"
|
cannam@174
|
195 m_lastTimestamp = timestamp;
|
cannam@174
|
196 return fs;
|
cannam@174
|
197 }
|
cannam@174
|
198
|
cannam@174
|
199 Plugin::FeatureSet
|
cannam@174
|
200 PluginSummarisingAdapter::Impl::getRemainingFeatures()
|
cannam@174
|
201 {
|
cannam@183
|
202 if (m_reduced) {
|
cannam@183
|
203 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
|
cannam@183
|
204 }
|
cannam@174
|
205 FeatureSet fs = m_plugin->getRemainingFeatures();
|
cannam@180
|
206 accumulate(fs, m_lastTimestamp, true);
|
cannam@174
|
207 return fs;
|
cannam@174
|
208 }
|
cannam@174
|
209
|
cannam@175
|
210 Plugin::FeatureList
|
cannam@180
|
211 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
|
cannam@180
|
212 SummaryType type,
|
cannam@180
|
213 AveragingMethod avg)
|
cannam@175
|
214 {
|
cannam@185
|
215 if (!m_reduced) {
|
cannam@185
|
216 segment();
|
cannam@185
|
217 reduce();
|
cannam@185
|
218 m_reduced = true;
|
cannam@185
|
219 }
|
cannam@183
|
220
|
cannam@180
|
221 bool continuous = (avg == ContinuousTimeAverage);
|
cannam@180
|
222
|
cannam@175
|
223 FeatureList fl;
|
cannam@175
|
224 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
|
cannam@175
|
225 i != m_summaries[output].end(); ++i) {
|
cannam@177
|
226
|
cannam@175
|
227 Feature f;
|
cannam@175
|
228 f.hasTimestamp = true;
|
cannam@175
|
229 f.timestamp = i->first;
|
cannam@175
|
230 f.hasDuration = false;
|
cannam@177
|
231
|
cannam@175
|
232 for (OutputSummary::const_iterator j = i->second.begin();
|
cannam@175
|
233 j != i->second.end(); ++j) {
|
cannam@175
|
234
|
cannam@175
|
235 // these will be ordered by bin number, and no bin numbers
|
cannam@175
|
236 // will be missing except at the end (because of the way
|
cannam@175
|
237 // the accumulators were initially filled in accumulate())
|
cannam@175
|
238
|
cannam@175
|
239 const OutputBinSummary &summary = j->second;
|
cannam@175
|
240 float result = 0.f;
|
cannam@175
|
241
|
cannam@175
|
242 switch (type) {
|
cannam@175
|
243
|
cannam@175
|
244 case Minimum:
|
cannam@175
|
245 result = summary.minimum;
|
cannam@175
|
246 break;
|
cannam@175
|
247
|
cannam@175
|
248 case Maximum:
|
cannam@175
|
249 result = summary.maximum;
|
cannam@175
|
250 break;
|
cannam@175
|
251
|
cannam@175
|
252 case Mean:
|
cannam@180
|
253 if (continuous) {
|
cannam@180
|
254 result = summary.mean_c;
|
cannam@180
|
255 } else if (summary.count) {
|
cannam@175
|
256 result = summary.sum / summary.count;
|
cannam@175
|
257 }
|
cannam@175
|
258 break;
|
cannam@175
|
259
|
cannam@175
|
260 case Median:
|
cannam@180
|
261 if (continuous) result = summary.median_c;
|
cannam@180
|
262 else result = summary.median;
|
cannam@175
|
263 break;
|
cannam@175
|
264
|
cannam@175
|
265 case Mode:
|
cannam@180
|
266 if (continuous) result = summary.mode_c;
|
cannam@180
|
267 else result = summary.mode;
|
cannam@175
|
268 break;
|
cannam@175
|
269
|
cannam@175
|
270 case Sum:
|
cannam@175
|
271 result = summary.sum;
|
cannam@175
|
272 break;
|
cannam@175
|
273
|
cannam@175
|
274 case Variance:
|
cannam@180
|
275 if (continuous) result = summary.variance_c;
|
cannam@180
|
276 else result = summary.variance;
|
cannam@175
|
277 break;
|
cannam@175
|
278
|
cannam@175
|
279 case StandardDeviation:
|
cannam@180
|
280 if (continuous) result = sqrtf(summary.variance_c);
|
cannam@180
|
281 else result = sqrtf(summary.variance);
|
cannam@175
|
282 break;
|
cannam@175
|
283
|
cannam@175
|
284 case Count:
|
cannam@175
|
285 result = summary.count;
|
cannam@175
|
286 break;
|
cannam@180
|
287
|
cannam@180
|
288 default:
|
cannam@180
|
289 break;
|
cannam@175
|
290 }
|
cannam@177
|
291
|
cannam@177
|
292 f.values.push_back(result);
|
cannam@175
|
293 }
|
cannam@175
|
294
|
cannam@175
|
295 fl.push_back(f);
|
cannam@175
|
296 }
|
cannam@175
|
297 return fl;
|
cannam@175
|
298 }
|
cannam@175
|
299
|
cannam@176
|
300 Plugin::FeatureSet
|
cannam@180
|
301 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
|
cannam@180
|
302 AveragingMethod avg)
|
cannam@176
|
303 {
|
cannam@185
|
304 if (!m_reduced) {
|
cannam@185
|
305 segment();
|
cannam@185
|
306 reduce();
|
cannam@185
|
307 m_reduced = true;
|
cannam@185
|
308 }
|
cannam@183
|
309
|
cannam@176
|
310 FeatureSet fs;
|
cannam@176
|
311 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
|
cannam@176
|
312 i != m_summaries.end(); ++i) {
|
cannam@180
|
313 fs[i->first] = getSummaryForOutput(i->first, type, avg);
|
cannam@176
|
314 }
|
cannam@176
|
315 return fs;
|
cannam@176
|
316 }
|
cannam@176
|
317
|
cannam@174
|
318 void
|
cannam@174
|
319 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
|
cannam@180
|
320 RealTime timestamp,
|
cannam@180
|
321 bool final)
|
cannam@174
|
322 {
|
cannam@174
|
323 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
|
cannam@174
|
324 for (FeatureList::const_iterator j = i->second.begin();
|
cannam@174
|
325 j != i->second.end(); ++j) {
|
cannam@182
|
326 if (j->hasTimestamp) {
|
cannam@182
|
327 accumulate(i->first, *j, j->timestamp, final);
|
cannam@182
|
328 } else {
|
cannam@182
|
329 //!!! is this correct?
|
cannam@182
|
330 accumulate(i->first, *j, timestamp, final);
|
cannam@182
|
331 }
|
cannam@174
|
332 }
|
cannam@174
|
333 }
|
cannam@174
|
334 }
|
cannam@174
|
335
|
cannam@174
|
336 void
|
cannam@174
|
337 PluginSummarisingAdapter::Impl::accumulate(int output,
|
cannam@174
|
338 const Feature &f,
|
cannam@180
|
339 RealTime timestamp,
|
cannam@180
|
340 bool final)
|
cannam@174
|
341 {
|
cannam@180
|
342 //!!! to do: use timestamp to determine which segment we're on
|
cannam@185
|
343
|
cannam@185
|
344 //!!! What should happen if a feature's duration spans a segment
|
cannam@185
|
345 // boundary? I think we probably want to chop it, and pretend that it
|
cannam@185
|
346 // appears in both -- don't we? do we? A very long feature (e.g. key,
|
cannam@185
|
347 // if the whole audio is in a single key) might span many or all
|
cannam@185
|
348 // segments, and we want that to be reflected in the results (e.g. it
|
cannam@185
|
349 // is the modal key in all of those segments, not just the first).
|
cannam@185
|
350 // That is actually quite complicated to do!
|
cannam@180
|
351
|
cannam@185
|
352 //!!! This affects how we record things. If features spanning a
|
cannam@185
|
353 // boundary should be chopped, then we need to have per-segment
|
cannam@185
|
354 // accumulators (and the feature value goes into both -- perhaps we
|
cannam@185
|
355 // need a separate phase to split the accumulator up into segments).
|
cannam@185
|
356 // If features spanning a boundary should be counted only in the first
|
cannam@185
|
357 // segment, with their full duration, then we should store them in a
|
cannam@185
|
358 // single accumulator and distribute into segments only on reduce.
|
cannam@180
|
359
|
cannam@184
|
360 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl;
|
cannam@182
|
361
|
cannam@184
|
362 // At each process step, accumulate() is called once for each
|
cannam@184
|
363 // feature on each output within that process's returned feature
|
cannam@184
|
364 // list, and with the timestamp passed in being that of the start
|
cannam@184
|
365 // of the process block.
|
cannam@182
|
366
|
cannam@184
|
367 // At the end (in getRemainingFeatures), accumulate() is called
|
cannam@184
|
368 // once for each feature on each output within the feature list
|
cannam@184
|
369 // returned by getRemainingFeatures, and with the timestamp being
|
cannam@184
|
370 // the same as the last process block and final set to true.
|
cannam@184
|
371
|
cannam@184
|
372 // (What if getRemainingFeatures doesn't return any features? We
|
cannam@184
|
373 // still need to ensure that the final duration is written. Need
|
cannam@184
|
374 // a separate function to close the durations.)
|
cannam@184
|
375
|
cannam@184
|
376 // At each call, we pull out the value for the feature and stuff
|
cannam@184
|
377 // it into the accumulator's appropriate values array; and we
|
cannam@184
|
378 // calculate the duration for the _previous_ feature, or pull it
|
cannam@184
|
379 // from the prevDurations array if the previous feature had a
|
cannam@184
|
380 // duration in its structure, and stuff that into the
|
cannam@184
|
381 // accumulator's appropriate durations array.
|
cannam@184
|
382
|
cannam@184
|
383 if (m_prevDurations.find(output) != m_prevDurations.end()) {
|
cannam@184
|
384
|
cannam@184
|
385 // Not the first time accumulate has been called for this
|
cannam@184
|
386 // output -- there has been a previous feature
|
cannam@184
|
387
|
cannam@184
|
388 RealTime prevDuration;
|
cannam@184
|
389
|
cannam@184
|
390 // Note that m_prevDurations[output] only contains the
|
cannam@184
|
391 // duration field that was contained in the previous feature.
|
cannam@184
|
392 // If it didn't have an explicit duration,
|
cannam@184
|
393 // m_prevDurations[output] should be INVALID_DURATION and we
|
cannam@184
|
394 // will have to calculate the duration from the previous and
|
cannam@184
|
395 // current timestamps.
|
cannam@184
|
396
|
cannam@184
|
397 if (m_prevDurations[output] != INVALID_DURATION) {
|
cannam@184
|
398 prevDuration = m_prevDurations[output];
|
cannam@184
|
399 std::cerr << "Previous duration from previous feature: " << prevDuration << std::endl;
|
cannam@184
|
400 } else {
|
cannam@184
|
401 prevDuration = timestamp - m_prevTimestamps[output];
|
cannam@184
|
402 std::cerr << "Previous duration from diff: " << timestamp << " - "
|
cannam@184
|
403 << m_prevTimestamps[output] << std::endl;
|
cannam@180
|
404 }
|
cannam@184
|
405
|
cannam@184
|
406 std::cerr << "output " << output << ": ";
|
cannam@184
|
407
|
cannam@184
|
408 std::cerr << "Pushing previous duration as " << prevDuration << std::endl;
|
cannam@185
|
409
|
cannam@185
|
410 m_accumulators[output].results
|
cannam@185
|
411 [m_accumulators[output].results.size() - 1]
|
cannam@185
|
412 .duration = prevDuration;
|
cannam@180
|
413 }
|
cannam@180
|
414
|
cannam@184
|
415 if (f.hasDuration) m_prevDurations[output] = f.duration;
|
cannam@184
|
416 else m_prevDurations[output] = INVALID_DURATION;
|
cannam@184
|
417
|
cannam@180
|
418 m_prevTimestamps[output] = timestamp;
|
cannam@185
|
419
|
cannam@185
|
420 //!!! should really be "timestamp plus duration" or "timestamp plus output resolution"
|
cannam@184
|
421 if (timestamp > m_lastTimestamp) m_lastTimestamp = timestamp;
|
cannam@180
|
422
|
cannam@185
|
423 Result result;
|
cannam@185
|
424 result.time = timestamp;
|
cannam@185
|
425 result.duration = INVALID_DURATION;
|
cannam@185
|
426
|
cannam@185
|
427 if (f.values.size() > m_accumulators[output].bins) {
|
cannam@185
|
428 m_accumulators[output].bins = f.values.size();
|
cannam@185
|
429 }
|
cannam@185
|
430
|
cannam@174
|
431 for (int i = 0; i < int(f.values.size()); ++i) {
|
cannam@185
|
432 result.values.push_back(f.values[i]);
|
cannam@174
|
433 }
|
cannam@185
|
434
|
cannam@185
|
435 m_accumulators[output].results.push_back(result);
|
cannam@184
|
436 }
|
cannam@180
|
437
|
cannam@184
|
438 void
|
cannam@184
|
439 PluginSummarisingAdapter::Impl::accumulateFinalDurations()
|
cannam@184
|
440 {
|
cannam@184
|
441 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin();
|
cannam@184
|
442 i != m_prevTimestamps.end(); ++i) {
|
cannam@184
|
443
|
cannam@184
|
444 int output = i->first;
|
cannam@185
|
445
|
cannam@185
|
446 int acount = m_accumulators[output].results.size();
|
cannam@185
|
447
|
cannam@185
|
448 if (acount == 0) continue;
|
cannam@185
|
449
|
cannam@184
|
450 RealTime prevTimestamp = i->second;
|
cannam@184
|
451
|
cannam@184
|
452 std::cerr << "output " << output << ": ";
|
cannam@184
|
453
|
cannam@184
|
454 if (m_prevDurations.find(output) != m_prevDurations.end() &&
|
cannam@184
|
455 m_prevDurations[output] != INVALID_DURATION) {
|
cannam@184
|
456
|
cannam@184
|
457 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl;
|
cannam@184
|
458
|
cannam@185
|
459 m_accumulators[output].results[acount - 1].duration =
|
cannam@185
|
460 m_prevDurations[output];
|
cannam@184
|
461
|
cannam@184
|
462 } else {
|
cannam@184
|
463
|
cannam@184
|
464 std::cerr << "Pushing final duration from diff as " << m_lastTimestamp << " - " << m_prevTimestamps[output] << std::endl;
|
cannam@184
|
465
|
cannam@185
|
466 m_accumulators[output].results[acount - 1].duration =
|
cannam@185
|
467 m_lastTimestamp - m_prevTimestamps[output];
|
cannam@184
|
468 }
|
cannam@180
|
469 }
|
cannam@174
|
470 }
|
cannam@174
|
471
|
cannam@185
|
472 void
|
cannam@185
|
473 PluginSummarisingAdapter::Impl::segment()
|
cannam@185
|
474 {
|
cannam@185
|
475 /*
|
cannam@185
|
476 SegmentBoundaries::iterator boundaryitr = m_boundaries.begin();
|
cannam@185
|
477 RealTime segmentStart = RealTime::zeroTime;
|
cannam@185
|
478
|
cannam@185
|
479 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
|
cannam@185
|
480 i != m_accumulators.end(); ++i) {
|
cannam@185
|
481
|
cannam@185
|
482 int output = i->first;
|
cannam@185
|
483 OutputAccumulator &source = i->second;
|
cannam@185
|
484 RealTime accumulatedTime = RealTime::zeroTime;
|
cannam@185
|
485
|
cannam@185
|
486 for (int n = 0; n < source.durations.size(); ++n) {
|
cannam@185
|
487 */
|
cannam@185
|
488
|
cannam@185
|
489
|
cannam@185
|
490 /*
|
cannam@185
|
491 if (boundaryitr == m_boundaries.end()) {
|
cannam@185
|
492 m_segmentedAccumulators[output][segmentStart] = source;
|
cannam@185
|
493 source.clear();
|
cannam@185
|
494 continue;
|
cannam@185
|
495 }
|
cannam@185
|
496 */
|
cannam@185
|
497
|
cannam@185
|
498
|
cannam@185
|
499
|
cannam@185
|
500
|
cannam@185
|
501 }
|
cannam@185
|
502
|
cannam@181
|
503 struct ValueDurationFloatPair
|
cannam@181
|
504 {
|
cannam@181
|
505 float value;
|
cannam@181
|
506 float duration;
|
cannam@181
|
507
|
cannam@181
|
508 ValueDurationFloatPair() : value(0), duration(0) { }
|
cannam@181
|
509 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
|
cannam@181
|
510 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
|
cannam@181
|
511 value = p.value;
|
cannam@181
|
512 duration = p.duration;
|
cannam@181
|
513 return *this;
|
cannam@181
|
514 }
|
cannam@181
|
515 bool operator<(const ValueDurationFloatPair &p) const {
|
cannam@181
|
516 return value < p.value;
|
cannam@181
|
517 }
|
cannam@181
|
518 };
|
cannam@181
|
519
|
cannam@181
|
520 static double toSec(const RealTime &r)
|
cannam@181
|
521 {
|
cannam@181
|
522 return r.sec + double(r.nsec) / 1000000000.0;
|
cannam@181
|
523 }
|
cannam@181
|
524
|
cannam@174
|
525 void
|
cannam@174
|
526 PluginSummarisingAdapter::Impl::reduce()
|
cannam@174
|
527 {
|
cannam@184
|
528 accumulateFinalDurations();
|
cannam@184
|
529
|
cannam@174
|
530 RealTime segmentStart = RealTime::zeroTime; //!!!
|
cannam@174
|
531
|
cannam@174
|
532 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
|
cannam@174
|
533 i != m_accumulators.end(); ++i) {
|
cannam@174
|
534
|
cannam@174
|
535 int output = i->first;
|
cannam@174
|
536 OutputAccumulator &accumulator = i->second;
|
cannam@174
|
537
|
cannam@185
|
538 int sz = accumulator.results.size();
|
cannam@185
|
539
|
cannam@182
|
540 double totalDuration = 0.0;
|
cannam@185
|
541 //!!! is this right?
|
cannam@185
|
542 if (sz > 0) {
|
cannam@185
|
543 totalDuration = toSec(accumulator.results[sz-1].time +
|
cannam@185
|
544 accumulator.results[sz-1].duration);
|
cannam@180
|
545 }
|
cannam@180
|
546
|
cannam@185
|
547 for (int bin = 0; bin < accumulator.bins; ++bin) {
|
cannam@174
|
548
|
cannam@180
|
549 // work on all values over time for a single bin
|
cannam@180
|
550
|
cannam@174
|
551 OutputBinSummary summary;
|
cannam@180
|
552
|
cannam@185
|
553 summary.count = sz;
|
cannam@180
|
554
|
cannam@174
|
555 summary.minimum = 0.f;
|
cannam@174
|
556 summary.maximum = 0.f;
|
cannam@180
|
557
|
cannam@174
|
558 summary.median = 0.f;
|
cannam@174
|
559 summary.mode = 0.f;
|
cannam@174
|
560 summary.sum = 0.f;
|
cannam@174
|
561 summary.variance = 0.f;
|
cannam@180
|
562
|
cannam@180
|
563 summary.median_c = 0.f;
|
cannam@180
|
564 summary.mode_c = 0.f;
|
cannam@180
|
565 summary.mean_c = 0.f;
|
cannam@180
|
566 summary.variance_c = 0.f;
|
cannam@180
|
567
|
cannam@185
|
568 if (sz == 0) continue;
|
cannam@180
|
569
|
cannam@181
|
570 std::vector<ValueDurationFloatPair> valvec;
|
cannam@181
|
571
|
cannam@181
|
572 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
573 while (accumulator.results[k].values.size() <
|
cannam@185
|
574 accumulator.bins) {
|
cannam@185
|
575 accumulator.results[k].values.push_back(0.f);
|
cannam@185
|
576 }
|
cannam@185
|
577 }
|
cannam@185
|
578
|
cannam@185
|
579 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
580 float value = accumulator.results[k].values[bin];
|
cannam@185
|
581 valvec.push_back(ValueDurationFloatPair
|
cannam@185
|
582 (value,
|
cannam@185
|
583 toSec(accumulator.results[k].duration)));
|
cannam@181
|
584 }
|
cannam@181
|
585
|
cannam@181
|
586 std::sort(valvec.begin(), valvec.end());
|
cannam@181
|
587
|
cannam@181
|
588 summary.minimum = valvec[0].value;
|
cannam@181
|
589 summary.maximum = valvec[sz-1].value;
|
cannam@174
|
590
|
cannam@174
|
591 if (sz % 2 == 1) {
|
cannam@181
|
592 summary.median = valvec[sz/2].value;
|
cannam@174
|
593 } else {
|
cannam@181
|
594 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
|
cannam@174
|
595 }
|
cannam@181
|
596
|
cannam@181
|
597 double duracc = 0.0;
|
cannam@181
|
598 summary.median_c = valvec[sz-1].value;
|
cannam@174
|
599
|
cannam@181
|
600 for (int k = 0; k < sz; ++k) {
|
cannam@181
|
601 duracc += valvec[k].duration;
|
cannam@181
|
602 if (duracc > totalDuration/2) {
|
cannam@181
|
603 summary.median_c = valvec[k].value;
|
cannam@181
|
604 break;
|
cannam@181
|
605 }
|
cannam@181
|
606 }
|
cannam@185
|
607
|
cannam@185
|
608 std::cerr << "median_c = " << summary.median_c << std::endl;
|
cannam@185
|
609 std::cerr << "median = " << summary.median << std::endl;
|
cannam@181
|
610
|
cannam@174
|
611 std::map<float, int> distribution;
|
cannam@174
|
612
|
cannam@174
|
613 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
614 summary.sum += accumulator.results[k].values[bin];
|
cannam@185
|
615 distribution[accumulator.results[k].values[bin]] += 1;
|
cannam@174
|
616 }
|
cannam@174
|
617
|
cannam@174
|
618 int md = 0;
|
cannam@174
|
619
|
cannam@174
|
620 for (std::map<float, int>::iterator di = distribution.begin();
|
cannam@174
|
621 di != distribution.end(); ++di) {
|
cannam@174
|
622 if (di->second > md) {
|
cannam@174
|
623 md = di->second;
|
cannam@174
|
624 summary.mode = di->first;
|
cannam@174
|
625 }
|
cannam@174
|
626 }
|
cannam@174
|
627
|
cannam@174
|
628 distribution.clear();
|
cannam@174
|
629
|
cannam@181
|
630 std::map<float, double> distribution_c;
|
cannam@180
|
631
|
cannam@180
|
632 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
633 distribution_c[accumulator.results[k].values[bin]]
|
cannam@185
|
634 += toSec(accumulator.results[k].duration);
|
cannam@180
|
635 }
|
cannam@180
|
636
|
cannam@181
|
637 double mrd = 0.0;
|
cannam@180
|
638
|
cannam@181
|
639 for (std::map<float, double>::iterator di = distribution_c.begin();
|
cannam@180
|
640 di != distribution_c.end(); ++di) {
|
cannam@180
|
641 if (di->second > mrd) {
|
cannam@180
|
642 mrd = di->second;
|
cannam@180
|
643 summary.mode_c = di->first;
|
cannam@180
|
644 }
|
cannam@180
|
645 }
|
cannam@180
|
646
|
cannam@180
|
647 distribution_c.clear();
|
cannam@180
|
648
|
cannam@181
|
649 if (totalDuration > 0.0) {
|
cannam@181
|
650
|
cannam@181
|
651 double sum_c = 0.0;
|
cannam@181
|
652
|
cannam@181
|
653 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
654 double value = accumulator.results[k].values[bin]
|
cannam@185
|
655 * toSec(accumulator.results[k].duration);
|
cannam@181
|
656 sum_c += value;
|
cannam@181
|
657 }
|
cannam@182
|
658
|
cannam@182
|
659 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
|
cannam@184
|
660 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl;
|
cannam@181
|
661
|
cannam@181
|
662 summary.mean_c = sum_c / totalDuration;
|
cannam@181
|
663
|
cannam@181
|
664 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
665 double value = accumulator.results[k].values[bin]
|
cannam@185
|
666 * toSec(accumulator.results[k].duration);
|
cannam@181
|
667 summary.variance_c +=
|
cannam@181
|
668 (value - summary.mean_c) * (value - summary.mean_c);
|
cannam@181
|
669 }
|
cannam@181
|
670
|
cannam@181
|
671 summary.variance_c /= summary.count;
|
cannam@181
|
672 }
|
cannam@181
|
673
|
cannam@174
|
674 float mean = summary.sum / summary.count;
|
cannam@174
|
675
|
cannam@182
|
676 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
|
cannam@182
|
677 << summary.sum / summary.count << std::endl;
|
cannam@182
|
678
|
cannam@174
|
679 for (int k = 0; k < sz; ++k) {
|
cannam@185
|
680 float value = accumulator.results[k].values[bin];
|
cannam@185
|
681 summary.variance += (value - mean) * (value - mean);
|
cannam@174
|
682 }
|
cannam@174
|
683 summary.variance /= summary.count;
|
cannam@174
|
684
|
cannam@174
|
685 m_summaries[output][segmentStart][bin] = summary;
|
cannam@174
|
686 }
|
cannam@174
|
687 }
|
cannam@175
|
688
|
cannam@175
|
689 m_accumulators.clear();
|
cannam@174
|
690 }
|
cannam@174
|
691
|
cannam@174
|
692
|
cannam@174
|
693 }
|
cannam@174
|
694
|
cannam@174
|
695 }
|
cannam@174
|
696
|