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