mathieu@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
mathieu@0
|
2
|
mathieu@0
|
3 /*
|
mathieu@0
|
4 Calcium Signal Analyser Vamp Plugin
|
mathieu@0
|
5
|
mathieu@2
|
6 Transient detection and characterisation for calcium signals.
|
mathieu@0
|
7
|
mathieu@2
|
8 Centre for Digital Music, Queen Mary University of London.
|
mathieu@2
|
9 This file copyright 2010-2011 Mathieu Barthet and QMUL.
|
mathieu@0
|
10
|
mathieu@2
|
11 Based on the QM Vamp note onset detector plugin by Christian
|
mathieu@2
|
12 Landone, Chris Duxbury, and Juan Pablo Bello.
|
mathieu@0
|
13
|
mathieu@2
|
14 This program is free software; you can redistribute it and/or
|
mathieu@2
|
15 modify it under the terms of the GNU General Public License as
|
mathieu@2
|
16 published by the Free Software Foundation; either version 2 of the
|
mathieu@2
|
17 License, or (at your option) any later version. See the file
|
mathieu@2
|
18 COPYING included with this distribution for more information.
|
mathieu@2
|
19
|
mathieu@2
|
20 Version: 2
|
mathieu@2
|
21 */
|
mathieu@0
|
22
|
mathieu@0
|
23 #include "CalciumSignalAnalyser.h"
|
mathieu@0
|
24
|
mathieu@0
|
25 #include <dsp/onsets/PeakPicking.h>
|
mathieu@0
|
26 #include <dsp/tempotracking/TempoTrack.h>
|
mathieu@0
|
27
|
mathieu@0
|
28 using std::string;
|
mathieu@0
|
29 using std::vector;
|
mathieu@0
|
30 using std::cerr;
|
mathieu@0
|
31 using std::endl;
|
mathieu@0
|
32
|
mathieu@0
|
33 #define MAX_PEAKFREQ 10; //maximum peak frequency in Hz
|
mathieu@0
|
34
|
mathieu@0
|
35 //#define VERBOSE //remove comment to have output messages for data display or debugging information
|
mathieu@0
|
36
|
mathieu@0
|
37 CalciumSignalAnalyser::CalciumSignalAnalyser(float inputSampleRate) :
|
mathieu@0
|
38 Vamp::Plugin(inputSampleRate),
|
mathieu@0
|
39 m_inputSampleRate(inputSampleRate),
|
mathieu@0
|
40 m_blockSize(0),
|
mathieu@0
|
41 m_stepSize(0),
|
mathieu@0
|
42 data(0),
|
mathieu@0
|
43 time(0),
|
mathieu@0
|
44 m_sensitivity(50),
|
mathieu@0
|
45 m_delta(0.2),
|
mathieu@2
|
46 m_mfwindur(10),
|
mathieu@0
|
47 processcounter(0)
|
mathieu@0
|
48 {
|
mathieu@0
|
49 #ifdef VERBOSE
|
mathieu@0
|
50 cout << "Sample rate: " << inputSampleRate << endl;
|
mathieu@0
|
51 #endif
|
mathieu@0
|
52 }
|
mathieu@0
|
53
|
mathieu@0
|
54 CalciumSignalAnalyser::~CalciumSignalAnalyser()
|
mathieu@0
|
55 {
|
mathieu@0
|
56 #ifdef VERBOSE
|
mathieu@0
|
57 cout << "Destructor" << endl;
|
mathieu@0
|
58 #endif
|
mathieu@0
|
59
|
mathieu@0
|
60 if (!data.empty()){
|
mathieu@0
|
61 #ifdef VERBOSE
|
mathieu@0
|
62 cout << "Data vector reset" << endl;
|
mathieu@0
|
63 #endif
|
mathieu@0
|
64 data.clear(); //clears the vector's elements
|
mathieu@0
|
65 }
|
mathieu@0
|
66
|
mathieu@0
|
67 if (!time.empty()){
|
mathieu@0
|
68 #ifdef VERBOSE
|
mathieu@0
|
69 cout << "Time vector reset" << endl;
|
mathieu@0
|
70 #endif
|
mathieu@0
|
71 time.clear(); //clears the vector's elements
|
mathieu@0
|
72 }
|
mathieu@0
|
73 }
|
mathieu@0
|
74
|
mathieu@0
|
75 string
|
mathieu@0
|
76 CalciumSignalAnalyser::getIdentifier() const
|
mathieu@0
|
77 {
|
mathieu@0
|
78 return "peakdetection";
|
mathieu@0
|
79 }
|
mathieu@0
|
80
|
mathieu@0
|
81 string
|
mathieu@0
|
82 CalciumSignalAnalyser::getName() const
|
mathieu@0
|
83 {
|
mathieu@0
|
84 return "Calcium Signal Analyser";
|
mathieu@0
|
85 }
|
mathieu@0
|
86
|
mathieu@0
|
87 string
|
mathieu@0
|
88 CalciumSignalAnalyser::getDescription() const
|
mathieu@0
|
89 {
|
mathieu@0
|
90 return "Automatic detection and characterisation of the signal's transients";
|
mathieu@0
|
91 }
|
mathieu@0
|
92
|
mathieu@0
|
93 string
|
mathieu@0
|
94 CalciumSignalAnalyser::getMaker() const
|
mathieu@0
|
95 {
|
mathieu@0
|
96 return "Mathieu Barthet";
|
mathieu@0
|
97 }
|
mathieu@0
|
98
|
mathieu@0
|
99 int
|
mathieu@0
|
100 CalciumSignalAnalyser::getPluginVersion() const
|
mathieu@0
|
101 {
|
mathieu@2
|
102 return 2;
|
mathieu@0
|
103 }
|
mathieu@0
|
104
|
mathieu@0
|
105 string
|
mathieu@0
|
106 CalciumSignalAnalyser::getCopyright() const
|
mathieu@0
|
107 {
|
mathieu@0
|
108 return "Plugin by Mathieu Barthet. Based on the note onset detector plugin by Christian Landone, Chris Duxbury and Juan Pablo Bello. Copyright (c) 2010 QMUL - All Rights Reserved";
|
mathieu@0
|
109 }
|
mathieu@0
|
110
|
mathieu@0
|
111 CalciumSignalAnalyser::ParameterList
|
mathieu@0
|
112 CalciumSignalAnalyser::getParameterDescriptors() const
|
mathieu@0
|
113 {
|
mathieu@0
|
114 ParameterList list;
|
mathieu@0
|
115 ParameterDescriptor desc;
|
mathieu@0
|
116
|
mathieu@0
|
117 desc.identifier = "sensitivity";
|
mathieu@0
|
118 desc.name = "Peak-picking sensitivity";
|
mathieu@0
|
119 desc.description = "Sensitivity of the peak detection (criterion based on quadratic interpolation)";
|
mathieu@0
|
120 desc.minValue = 0;
|
mathieu@0
|
121 desc.maxValue = 100;
|
mathieu@0
|
122 desc.defaultValue = 50;
|
mathieu@0
|
123 desc.isQuantized = true;
|
mathieu@0
|
124 desc.quantizeStep = 1;
|
mathieu@0
|
125 desc.unit = "%";
|
mathieu@0
|
126 list.push_back(desc);
|
mathieu@0
|
127
|
mathieu@0
|
128 desc.identifier = "deltathreshold";
|
mathieu@0
|
129 desc.name = "Peak-picking offset threshold (delta)";
|
mathieu@0
|
130 desc.description = "Offset threshold aiming at improving the peak detection by discarding the noise";
|
mathieu@0
|
131 desc.minValue = 0;
|
mathieu@0
|
132 desc.maxValue = 1;
|
mathieu@0
|
133 desc.defaultValue = 0.2;
|
mathieu@0
|
134 desc.isQuantized = true;
|
mathieu@0
|
135 desc.quantizeStep = 0.05;
|
mathieu@0
|
136 desc.unit = "";
|
mathieu@0
|
137 list.push_back(desc);
|
mathieu@0
|
138
|
mathieu@2
|
139 desc.identifier = "mfwindowduration";
|
mathieu@2
|
140 desc.name = "Duration of the median filtering window";
|
mathieu@2
|
141 desc.description = "Sets the duration of the median filtering window";
|
mathieu@2
|
142 desc.minValue = 2;
|
mathieu@2
|
143 desc.maxValue = 100;
|
mathieu@2
|
144 desc.defaultValue = 10;
|
mathieu@2
|
145 desc.isQuantized = true;
|
mathieu@2
|
146 desc.quantizeStep = 0.1;
|
mathieu@2
|
147 desc.unit = "s";
|
mathieu@2
|
148 list.push_back(desc);
|
mathieu@2
|
149
|
mathieu@0
|
150 return list;
|
mathieu@0
|
151 }
|
mathieu@0
|
152
|
mathieu@0
|
153 float
|
mathieu@0
|
154 CalciumSignalAnalyser::getParameter(std::string name) const
|
mathieu@0
|
155 {
|
mathieu@0
|
156 if (name == "sensitivity"){
|
mathieu@0
|
157 return m_sensitivity;
|
mathieu@0
|
158 }
|
mathieu@0
|
159 else if (name == "deltathreshold") {
|
mathieu@0
|
160 return m_delta;
|
mathieu@2
|
161 }
|
mathieu@2
|
162 else if (name == "mfwindowduration") {
|
mathieu@2
|
163 return m_mfwindur;
|
mathieu@0
|
164 }
|
mathieu@0
|
165
|
mathieu@0
|
166 return 0.0;
|
mathieu@0
|
167 }
|
mathieu@0
|
168
|
mathieu@0
|
169 void
|
mathieu@0
|
170 CalciumSignalAnalyser::setParameter(std::string name, float value)
|
mathieu@0
|
171 {
|
mathieu@0
|
172 if (name == "sensitivity") {
|
mathieu@0
|
173 if (m_sensitivity == value) return;
|
mathieu@0
|
174 m_sensitivity = value;
|
mathieu@0
|
175
|
mathieu@0
|
176 }
|
mathieu@0
|
177 else if (name == "deltathreshold") {
|
mathieu@0
|
178 if (m_delta == value) return;
|
mathieu@0
|
179 m_delta = value;
|
mathieu@0
|
180 }
|
mathieu@2
|
181 else if (name == "mfwindowduration") {
|
mathieu@2
|
182 if (m_mfwindur == value) return;
|
mathieu@2
|
183 m_mfwindur = value;
|
mathieu@2
|
184 }
|
mathieu@0
|
185 }
|
mathieu@0
|
186
|
mathieu@0
|
187
|
mathieu@0
|
188 CalciumSignalAnalyser::ProgramList
|
mathieu@0
|
189 CalciumSignalAnalyser::getPrograms() const
|
mathieu@0
|
190 {
|
mathieu@0
|
191 ProgramList programs;
|
mathieu@0
|
192
|
mathieu@0
|
193 return programs;
|
mathieu@0
|
194 }
|
mathieu@0
|
195
|
mathieu@0
|
196 std::string
|
mathieu@0
|
197 CalciumSignalAnalyser::getCurrentProgram() const
|
mathieu@0
|
198 {
|
mathieu@0
|
199 return "";
|
mathieu@0
|
200 }
|
mathieu@0
|
201
|
mathieu@0
|
202 void
|
mathieu@0
|
203 CalciumSignalAnalyser::selectProgram(std::string program)
|
mathieu@0
|
204 {
|
mathieu@0
|
205 }
|
mathieu@0
|
206
|
mathieu@0
|
207 bool
|
mathieu@0
|
208 CalciumSignalAnalyser::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
mathieu@0
|
209 {
|
mathieu@0
|
210
|
mathieu@0
|
211 #ifdef VERBOSE
|
mathieu@0
|
212 cout << "Initialise" << endl;
|
mathieu@0
|
213 #endif
|
mathieu@0
|
214
|
mathieu@0
|
215 m_blockSize = 1;
|
mathieu@0
|
216 m_stepSize = 1;
|
mathieu@0
|
217
|
mathieu@0
|
218 #ifdef VERBOSE
|
mathieu@0
|
219 cout << m_blockSize << " " << m_stepSize << endl;
|
mathieu@0
|
220 #endif
|
mathieu@0
|
221
|
mathieu@0
|
222 if (channels < getMinChannelCount() ||
|
mathieu@0
|
223 channels > getMaxChannelCount()) {
|
mathieu@0
|
224 std::cerr << "CalciumSignalAnalyser::initialise: Unsupported channel count: "
|
mathieu@0
|
225 << channels << std::endl;
|
mathieu@0
|
226 return false;
|
mathieu@0
|
227 }
|
mathieu@0
|
228
|
mathieu@0
|
229 if (stepSize != getPreferredStepSize()) {
|
mathieu@0
|
230
|
mathieu@0
|
231 std::cerr << "ERROR: CalciumSignalAnalyser::initialise: the step size has to be 1." << std::endl;
|
mathieu@0
|
232 return false;
|
mathieu@0
|
233 }
|
mathieu@0
|
234
|
mathieu@0
|
235 if (blockSize != getPreferredBlockSize()) {
|
mathieu@0
|
236
|
mathieu@0
|
237 std::cerr << "ERROR: CalciumSignalAnalyser::initialise: the block size has to be 1." << std::endl;
|
mathieu@0
|
238 return false;
|
mathieu@0
|
239 }
|
mathieu@0
|
240
|
mathieu@0
|
241 return true;
|
mathieu@0
|
242 }
|
mathieu@0
|
243
|
mathieu@0
|
244 void
|
mathieu@0
|
245 CalciumSignalAnalyser::reset()
|
mathieu@0
|
246 {
|
mathieu@0
|
247 #ifdef VERBOSE
|
mathieu@0
|
248 cout << "Reset" << endl;
|
mathieu@0
|
249 #endif
|
mathieu@0
|
250
|
mathieu@0
|
251 if (!data.empty()){
|
mathieu@0
|
252 #ifdef VERBOSE
|
mathieu@0
|
253 cout << "Data vector reset" << endl;
|
mathieu@0
|
254 #endif
|
mathieu@0
|
255 data.clear(); //clears the vector's elements
|
mathieu@0
|
256 }
|
mathieu@0
|
257
|
mathieu@0
|
258 if (!time.empty()){
|
mathieu@0
|
259 #ifdef VERBOSE
|
mathieu@0
|
260 cout << "Time vector reset" << endl;
|
mathieu@0
|
261 #endif
|
mathieu@0
|
262 time.clear(); //clears the vector's elements
|
mathieu@0
|
263 }
|
mathieu@0
|
264 }
|
mathieu@0
|
265
|
mathieu@0
|
266 size_t
|
mathieu@0
|
267 CalciumSignalAnalyser::getPreferredStepSize() const
|
mathieu@0
|
268 {
|
mathieu@0
|
269 return getPreferredBlockSize();
|
mathieu@0
|
270 }
|
mathieu@0
|
271
|
mathieu@0
|
272 size_t
|
mathieu@0
|
273 CalciumSignalAnalyser::getPreferredBlockSize() const
|
mathieu@0
|
274 {
|
mathieu@0
|
275 return 1;
|
mathieu@0
|
276 }
|
mathieu@0
|
277
|
mathieu@0
|
278 size_t
|
mathieu@0
|
279 CalciumSignalAnalyser::getMinChannelCount() const
|
mathieu@0
|
280 {
|
mathieu@0
|
281 return 1;
|
mathieu@0
|
282 }
|
mathieu@0
|
283
|
mathieu@0
|
284 size_t
|
mathieu@0
|
285 CalciumSignalAnalyser::getMaxChannelCount() const
|
mathieu@0
|
286 {
|
mathieu@0
|
287 return 1;
|
mathieu@0
|
288 }
|
mathieu@0
|
289
|
mathieu@0
|
290
|
mathieu@0
|
291 CalciumSignalAnalyser::OutputList
|
mathieu@0
|
292 CalciumSignalAnalyser::getOutputDescriptors() const
|
mathieu@0
|
293 {
|
mathieu@0
|
294 #ifdef VERBOSE
|
mathieu@0
|
295 cout << "get output desc" << endl;
|
mathieu@0
|
296 #endif
|
mathieu@0
|
297
|
mathieu@0
|
298 OutputList list;
|
mathieu@0
|
299
|
mathieu@0
|
300 OutputDescriptor pt;
|
mathieu@0
|
301 pt.identifier = "peaktimes";
|
mathieu@0
|
302 pt.name = "Peaks times";
|
mathieu@0
|
303 pt.description = "Time positions of the peaks (in seconds)";
|
mathieu@0
|
304 pt.unit = "";
|
mathieu@0
|
305 pt.hasFixedBinCount = true;
|
mathieu@0
|
306 pt.binCount = 0; //0 for time only feature
|
mathieu@0
|
307 pt.sampleType = OutputDescriptor::VariableSampleRate;
|
mathieu@0
|
308 pt.hasDuration = false;
|
mathieu@0
|
309 pt.hasKnownExtents = false;
|
mathieu@0
|
310 pt.isQuantized = false;
|
mathieu@0
|
311 pt.sampleRate = 0; //no sensible resolution, the time of the feature are given in the timestamp of the feature
|
mathieu@0
|
312 list.push_back(pt);
|
mathieu@0
|
313
|
mathieu@0
|
314 OutputDescriptor po;
|
mathieu@0
|
315 po.identifier = "peakonsets";
|
mathieu@0
|
316 po.name = "Peaks onsets";
|
mathieu@0
|
317 po.description = "Time positions of the peak onsets (in seconds)";
|
mathieu@0
|
318 po.unit = "";
|
mathieu@0
|
319 po.hasFixedBinCount = true;
|
mathieu@0
|
320 po.binCount = 0; //0 for time only feature
|
mathieu@0
|
321 po.sampleType = OutputDescriptor::VariableSampleRate;
|
mathieu@0
|
322 po.hasDuration = false;
|
mathieu@0
|
323 po.hasKnownExtents = false;
|
mathieu@0
|
324 po.isQuantized = false;
|
mathieu@0
|
325 po.sampleRate = 0; //no sensible resolution, the time of the feature are given in the timestamp of the feature
|
mathieu@0
|
326 list.push_back(po);
|
mathieu@0
|
327
|
mathieu@0
|
328 OutputDescriptor sdf;
|
mathieu@0
|
329 sdf.identifier = "smoothed_df";
|
mathieu@0
|
330 sdf.name = "Smoothed Detection Function";
|
mathieu@0
|
331 sdf.description = "Smoothed probability function used for peak-picking";
|
mathieu@0
|
332 sdf.unit = "";
|
mathieu@0
|
333 sdf.hasFixedBinCount = true;
|
mathieu@0
|
334 sdf.binCount = 1;
|
mathieu@0
|
335 sdf.hasKnownExtents = false;
|
mathieu@0
|
336 sdf.isQuantized = false;
|
mathieu@0
|
337 sdf.sampleType = OutputDescriptor::FixedSampleRate;
|
mathieu@0
|
338 sdf.sampleRate = m_inputSampleRate; //the smoothed detection function has the same sample rate as the input signal
|
mathieu@0
|
339 list.push_back(sdf);
|
mathieu@0
|
340
|
mathieu@0
|
341 OutputDescriptor pf;
|
mathieu@0
|
342 pf.identifier = "peak_frequency";
|
mathieu@0
|
343 pf.name = "Peak frequency";
|
mathieu@0
|
344 pf.description = "Frequency of the peaks (in Hz)";
|
mathieu@0
|
345 //pf.unit = "Hz"; //if unit is Hz then the feature is treated as a pitch which is not relevant in this case
|
mathieu@0
|
346 pf.unit = "";
|
mathieu@0
|
347 pf.hasFixedBinCount = true;
|
mathieu@0
|
348 pf.binCount = 1; //1 corresponds to time and value
|
mathieu@0
|
349 pf.sampleType = OutputDescriptor::VariableSampleRate;
|
mathieu@0
|
350 pf.sampleRate = 0; //no sensible resolution, the time of the feature is given in the timestamp of the feature
|
mathieu@0
|
351 pf.hasKnownExtents = true;
|
mathieu@0
|
352 pf.minValue = 0;
|
mathieu@0
|
353 pf.maxValue = MAX_PEAKFREQ;
|
mathieu@0
|
354 pf.isQuantized = false;
|
mathieu@0
|
355 pf.hasDuration = true; //duration of the signal
|
mathieu@0
|
356 list.push_back(pf);
|
mathieu@0
|
357
|
mathieu@0
|
358 OutputDescriptor mipi;
|
mathieu@0
|
359 mipi.identifier = "mean_interpeakinterval";
|
mathieu@0
|
360 mipi.name = "Mean Inter Peak Interval";
|
mathieu@0
|
361 mipi.description = "Mean of the time intervals between peaks (in seconds)";
|
mathieu@0
|
362 mipi.unit = "s";
|
mathieu@0
|
363 mipi.hasFixedBinCount = true;
|
mathieu@0
|
364 mipi.binCount = 1; //1 corresponds to time and value
|
mathieu@0
|
365 mipi.sampleType = OutputDescriptor::VariableSampleRate;
|
mathieu@0
|
366 mipi.sampleRate = 0; //no sensible resolution, the time of the feature is given in the timestamp of the feature
|
mathieu@0
|
367 mipi.hasKnownExtents = false;
|
mathieu@0
|
368 mipi.isQuantized = false;
|
mathieu@0
|
369 mipi.hasDuration = true; //duration of the signal
|
mathieu@0
|
370 list.push_back(mipi);
|
mathieu@0
|
371
|
mathieu@0
|
372 return list;
|
mathieu@0
|
373 }
|
mathieu@0
|
374
|
mathieu@0
|
375 CalciumSignalAnalyser::FeatureSet
|
mathieu@0
|
376 CalciumSignalAnalyser::process(const float *const *inputBuffers,
|
mathieu@0
|
377 Vamp::RealTime timestamp)
|
mathieu@0
|
378 {
|
mathieu@0
|
379 FeatureSet fs;
|
mathieu@0
|
380
|
mathieu@0
|
381 if (m_blockSize == 0 || m_stepSize == 0) {
|
mathieu@0
|
382 cerr << "ERROR: CalciumSignalAnalyser::process: "
|
mathieu@0
|
383 << "CalciumSignalAnalyser has not been initialised."
|
mathieu@0
|
384 << endl;
|
mathieu@0
|
385 return fs;
|
mathieu@0
|
386 }
|
mathieu@0
|
387
|
mathieu@0
|
388 data.push_back(inputBuffers[0][0]); //assumes that only one sample is read at a time
|
mathieu@0
|
389 time.push_back(timestamp);
|
mathieu@0
|
390
|
mathieu@0
|
391 processcounter ++;
|
mathieu@0
|
392
|
mathieu@0
|
393 fs = FeatureSet(); //empty feature set
|
mathieu@0
|
394
|
mathieu@0
|
395 return fs;
|
mathieu@0
|
396 }
|
mathieu@0
|
397
|
mathieu@0
|
398 CalciumSignalAnalyser::FeatureSet
|
mathieu@0
|
399 CalciumSignalAnalyser::getRemainingFeatures()
|
mathieu@0
|
400 {
|
mathieu@0
|
401 FeatureSet returnFeatures;
|
mathieu@0
|
402 returnFeatures = FeatureSet();
|
mathieu@0
|
403
|
mathieu@0
|
404 #ifdef VERBOSE
|
mathieu@0
|
405 cout << "Get remaining features" << endl << endl;
|
mathieu@0
|
406 #endif
|
mathieu@0
|
407
|
mathieu@0
|
408 /***************************************
|
mathieu@0
|
409 Peak times
|
mathieu@0
|
410 ****************************************/
|
mathieu@0
|
411
|
mathieu@0
|
412 double aCoeffs[] = { 1.0000, -0.5949, 0.2348 };
|
mathieu@0
|
413 double bCoeffs[] = { 0.1600, 0.3200, 0.1600 };
|
mathieu@0
|
414
|
mathieu@0
|
415 //The signal is directly treated as a detection function for the peak picking stage
|
mathieu@0
|
416
|
mathieu@0
|
417 PPickParams ppParams;
|
mathieu@0
|
418 ppParams.length = data.size(); //length of the signal
|
mathieu@0
|
419
|
mathieu@0
|
420 #ifdef VERBOSE
|
mathieu@0
|
421 cout << "Length: " << ppParams.length << endl;
|
mathieu@0
|
422 #endif
|
mathieu@0
|
423
|
mathieu@0
|
424 // tau and cutoff appear to be unused in PeakPicking, but I've
|
mathieu@0
|
425 // inserted some moderately plausible values rather than leave
|
mathieu@0
|
426 // them unset. The QuadThresh values come from trial and error.
|
mathieu@0
|
427 // The rest of these are copied from ttParams in the BeatTracker
|
mathieu@0
|
428 // code: I don't claim to know whether they're good or not --cc
|
mathieu@0
|
429
|
mathieu@0
|
430 ppParams.tau = m_stepSize / m_inputSampleRate; //not used for peak picking
|
mathieu@0
|
431
|
mathieu@0
|
432 ppParams.alpha = 9;
|
mathieu@0
|
433 ppParams.cutoff = m_inputSampleRate/4; //not used for peak picking
|
mathieu@0
|
434 ppParams.LPOrd = 2;
|
mathieu@0
|
435 ppParams.LPACoeffs = aCoeffs;
|
mathieu@0
|
436 ppParams.LPBCoeffs = bCoeffs;
|
mathieu@0
|
437
|
mathieu@2
|
438 int NWin;
|
mathieu@2
|
439 float samples = m_inputSampleRate*m_mfwindur;
|
mathieu@2
|
440 if (samples < 4){
|
mathieu@2
|
441 NWin = 4; //minimal possible length of the window in samples
|
mathieu@2
|
442 #ifdef VERBOSE
|
mathieu@2
|
443 cout << "WARNING: CalciumSignalAnalyser::getRemainingFeatures:"
|
mathieu@2
|
444 << "The duration of the median filtering window is too small considering the"
|
mathieu@2
|
445 << "sample frequency of the signal. Duration of "<< NWin/m_inputSampleRate
|
mathieu@2
|
446 << " s used instead."
|
mathieu@2
|
447 << endl;
|
mathieu@2
|
448 #endif
|
mathieu@2
|
449 }else{
|
mathieu@2
|
450 NWin = floor(m_inputSampleRate*m_mfwindur);
|
mathieu@2
|
451 if (NWin%2 != 0) NWin--;
|
mathieu@2
|
452 }
|
mathieu@2
|
453
|
mathieu@2
|
454 ppParams.WinT.post = NWin/2;
|
mathieu@2
|
455 ppParams.WinT.pre = NWin/2 - 1;
|
mathieu@0
|
456
|
mathieu@0
|
457 ppParams.QuadThresh.a = (100 - m_sensitivity) / 1000.0;
|
mathieu@0
|
458 ppParams.QuadThresh.b = 0;
|
mathieu@0
|
459 ppParams.QuadThresh.c = (100 - m_sensitivity) / 1500.0;
|
mathieu@0
|
460 ppParams.delta = m_delta; //delta threshold used as an offset when computing the smoothed detection function
|
mathieu@0
|
461
|
mathieu@0
|
462 PeakPicking peakPicker(ppParams);
|
mathieu@0
|
463
|
mathieu@0
|
464 double *ppSrc = new double[ppParams.length];
|
mathieu@0
|
465 if (ppSrc==NULL) {
|
mathieu@0
|
466 cerr << "ERROR: CalciumSignalAnalyser::getRemainingFeatures:"
|
mathieu@0
|
467 << "Unable to allocate memory (ppSrc)."
|
mathieu@0
|
468 << endl;
|
mathieu@0
|
469 return returnFeatures;
|
mathieu@0
|
470 }
|
mathieu@0
|
471
|
mathieu@0
|
472 for (unsigned int i = 0; i < ppParams.length; ++i) {
|
mathieu@0
|
473
|
mathieu@0
|
474 ppSrc[i] = (double) data[i];
|
mathieu@0
|
475 }
|
mathieu@0
|
476
|
mathieu@0
|
477 vector<int> peaks;
|
mathieu@0
|
478 vector<int> peak_frame;
|
mathieu@0
|
479 vector<int> onsets_frame;
|
mathieu@0
|
480
|
mathieu@0
|
481 peakPicker.process(ppSrc, ppParams.length, peaks);
|
mathieu@0
|
482
|
mathieu@0
|
483 for (size_t i = 0; i < peaks.size(); ++i) {
|
mathieu@0
|
484
|
mathieu@0
|
485 //peak times
|
mathieu@0
|
486 size_t index = peaks[i];
|
mathieu@0
|
487 size_t peakframe = peaks[i] * m_stepSize;
|
mathieu@0
|
488 peak_frame.push_back(peakframe);
|
mathieu@0
|
489
|
mathieu@0
|
490 Feature featurepeaks;
|
mathieu@0
|
491 featurepeaks.hasTimestamp = true;
|
mathieu@0
|
492 featurepeaks.timestamp = Vamp::RealTime::frame2RealTime(peakframe, lrintf(m_inputSampleRate));
|
mathieu@0
|
493
|
mathieu@0
|
494 returnFeatures[0].push_back(featurepeaks); // peak times are output 0
|
mathieu@0
|
495
|
mathieu@0
|
496 //algorithm to detect the peak onsets
|
mathieu@0
|
497
|
mathieu@0
|
498 double prevDiff = 0.0;
|
mathieu@0
|
499 while (index > 1) {
|
mathieu@0
|
500 double diff = ppSrc[index] - ppSrc[index-1];
|
mathieu@0
|
501 if (diff < prevDiff * 0.9) break;
|
mathieu@0
|
502 prevDiff = diff;
|
mathieu@0
|
503 --index;
|
mathieu@0
|
504 }
|
mathieu@0
|
505
|
mathieu@0
|
506 size_t onsetframe = index * m_stepSize;
|
mathieu@0
|
507 onsets_frame.push_back(onsetframe);
|
mathieu@0
|
508
|
mathieu@0
|
509 Feature featureonsets;
|
mathieu@0
|
510 featureonsets.hasTimestamp = true;
|
mathieu@0
|
511 featureonsets.timestamp = Vamp::RealTime::frame2RealTime(onsetframe, lrintf(m_inputSampleRate));
|
mathieu@0
|
512
|
mathieu@0
|
513 #ifdef VERBOSE
|
mathieu@0
|
514 cout << featureonsets.timestamp << endl;
|
mathieu@0
|
515 #endif
|
mathieu@0
|
516 returnFeatures[1].push_back(featureonsets); // peak onsets are output 1
|
mathieu@0
|
517 }
|
mathieu@0
|
518
|
mathieu@0
|
519 for (unsigned int i = 0; i < ppParams.length; ++i) {
|
mathieu@0
|
520
|
mathieu@0
|
521 Feature feature;
|
mathieu@0
|
522 feature.hasTimestamp = true;
|
mathieu@0
|
523 size_t frame = i * m_stepSize;
|
mathieu@0
|
524
|
mathieu@0
|
525 feature.timestamp = Vamp::RealTime::frame2RealTime(frame, lrintf(m_inputSampleRate));
|
mathieu@0
|
526
|
mathieu@0
|
527 feature.values.push_back(ppSrc[i]);
|
mathieu@0
|
528
|
mathieu@0
|
529 returnFeatures[2].push_back(feature); // smoothed df is output 2
|
mathieu@0
|
530 }
|
mathieu@0
|
531
|
mathieu@0
|
532 /***************************************
|
mathieu@0
|
533 Peak frequency
|
mathieu@0
|
534 ****************************************/
|
mathieu@0
|
535
|
mathieu@0
|
536 double peakfreq, duration;
|
mathieu@0
|
537 int npeaks; //total number of peaks
|
mathieu@0
|
538
|
mathieu@0
|
539 //last element of the time vector is the duration of the signal - converts the RealTime to a float
|
mathieu@0
|
540 duration = time.back()/Vamp::RealTime(1,0); //division by 1 s to obtain a double
|
mathieu@0
|
541
|
mathieu@0
|
542 npeaks = peaks.size();
|
mathieu@0
|
543 peakfreq = npeaks/duration;
|
mathieu@0
|
544
|
mathieu@0
|
545 #ifdef VERBOSE
|
mathieu@0
|
546 cout << npeaks << endl;
|
mathieu@0
|
547 cout << duration << endl;
|
mathieu@0
|
548 cout << peakfreq << endl;
|
mathieu@0
|
549 #endif
|
mathieu@0
|
550
|
mathieu@0
|
551 Feature pf;
|
mathieu@0
|
552 pf.hasTimestamp = true;
|
mathieu@0
|
553 pf.timestamp = time[0];
|
mathieu@0
|
554 pf.hasDuration = true;
|
mathieu@0
|
555 pf.duration = time.back()-time[0];
|
mathieu@0
|
556
|
mathieu@0
|
557 #ifdef VERBOSE
|
mathieu@0
|
558 cout << "processcounter" << processcounter << endl;
|
mathieu@0
|
559
|
mathieu@0
|
560 cout << "time.back " << time.back().toText() << endl;
|
mathieu@0
|
561 cout << "time.size " << time.size() << endl;
|
mathieu@0
|
562 cout << "data.size " << data.size() << endl;
|
mathieu@0
|
563 cout << "time[0, 1 ...] " << time[0].toText() << " " << time[1].toText() << " " << time[2].toText() << " " << time[time.size()].toText() << endl;
|
mathieu@0
|
564
|
mathieu@0
|
565 cout << "time[999] " << time[999].toText() << endl;
|
mathieu@0
|
566 cout << "data[999] " << data[999] << endl;
|
mathieu@0
|
567 cout << "time[1000] " << time[1000].toText() << endl;
|
mathieu@0
|
568 cout << "data[1000] " << data[1000]<< endl;
|
mathieu@0
|
569 #endif
|
mathieu@0
|
570
|
mathieu@0
|
571 pf.values.push_back(peakfreq);
|
mathieu@0
|
572
|
mathieu@0
|
573 char pflabel[256];
|
mathieu@0
|
574 sprintf(pflabel,"%f Hz",peakfreq);
|
mathieu@0
|
575 pf.label = pflabel;
|
mathieu@0
|
576
|
mathieu@0
|
577 #ifdef VERBOSE
|
mathieu@0
|
578 cout << "label: " << pflabel << endl;
|
mathieu@0
|
579 #endif
|
mathieu@0
|
580
|
mathieu@0
|
581 returnFeatures[3].push_back(pf); //peak frequency is output 3
|
mathieu@0
|
582
|
mathieu@0
|
583 /***************************************
|
mathieu@0
|
584 Mean Inter Peak Interval
|
mathieu@0
|
585 ****************************************/
|
mathieu@0
|
586
|
mathieu@0
|
587 double o1,o2;
|
mathieu@0
|
588
|
mathieu@0
|
589 double sumipi_s = 0;
|
mathieu@0
|
590 double meanipi_s;
|
mathieu@0
|
591
|
mathieu@0
|
592 for (int j = 0; j < npeaks-1; ++j) {
|
mathieu@0
|
593 o2 = Vamp::RealTime::frame2RealTime(peak_frame[j+1], lrintf(m_inputSampleRate))/Vamp::RealTime(1,0);
|
mathieu@0
|
594 o1 = Vamp::RealTime::frame2RealTime(peak_frame[j], lrintf(m_inputSampleRate))/Vamp::RealTime(1,0);
|
mathieu@0
|
595 #ifdef VERBOSE
|
mathieu@0
|
596 cout << "o1: " << o1 << endl;
|
mathieu@0
|
597 cout << "o2: " << o2 << endl;
|
mathieu@0
|
598 #endif
|
mathieu@0
|
599 sumipi_s = sumipi_s + o2 - o1;
|
mathieu@0
|
600 }
|
mathieu@0
|
601
|
mathieu@0
|
602 meanipi_s = sumipi_s/(npeaks-1);
|
mathieu@0
|
603
|
mathieu@0
|
604 #ifdef VERBOSE
|
mathieu@0
|
605 cout << "Sum IPI: " << sumipi_s << endl;
|
mathieu@0
|
606 cout << "Mean IPI: " << meanipi_s << endl;
|
mathieu@0
|
607 #endif
|
mathieu@0
|
608
|
mathieu@0
|
609 Feature mipi;
|
mathieu@0
|
610 mipi.hasTimestamp = true;
|
mathieu@0
|
611 mipi.timestamp = time[0];
|
mathieu@0
|
612 mipi.hasDuration = true;
|
mathieu@0
|
613 mipi.duration = time.back()-time[0];
|
mathieu@0
|
614
|
mathieu@0
|
615 mipi.values.push_back(meanipi_s); //mean inter peak interval in secs
|
mathieu@0
|
616
|
mathieu@0
|
617 returnFeatures[4].push_back(mipi); //mean inter peak interval is output 4
|
mathieu@0
|
618
|
mathieu@0
|
619 //clear data
|
mathieu@0
|
620
|
mathieu@0
|
621 delete [] ppSrc;
|
mathieu@0
|
622
|
mathieu@0
|
623 if (!data.empty()){
|
mathieu@0
|
624 #ifdef VERBOSE
|
mathieu@0
|
625 cout << "Data vector reset" << endl;
|
mathieu@0
|
626 #endif
|
mathieu@0
|
627 data.clear(); //clears the vector's elements
|
mathieu@0
|
628 }
|
mathieu@0
|
629
|
mathieu@0
|
630 if (!time.empty()){
|
mathieu@0
|
631 #ifdef VERBOSE
|
mathieu@0
|
632 cout << "Time vector reset" << endl;
|
mathieu@0
|
633 #endif
|
mathieu@0
|
634 time.clear(); //clears the vector's elements
|
mathieu@0
|
635 }
|
mathieu@0
|
636
|
mathieu@0
|
637 #ifdef VERBOSE
|
mathieu@0
|
638 cout << "End of get remaining features" << endl << endl;
|
mathieu@0
|
639 #endif
|
mathieu@0
|
640 return returnFeatures;
|
mathieu@0
|
641 }
|
mathieu@0
|
642
|