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