Merge branch 'export-dialog' into cairocanvas
[ardour.git] / libs / vamp-plugins / PercussionOnsetDetector.cpp
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
3 /*
4     Vamp
5
6     An API for audio analysis and feature extraction plugins.
7
8     Centre for Digital Music, Queen Mary, University of London.
9     Copyright 2006 Chris Cannam.
10   
11     Permission is hereby granted, free of charge, to any person
12     obtaining a copy of this software and associated documentation
13     files (the "Software"), to deal in the Software without
14     restriction, including without limitation the rights to use, copy,
15     modify, merge, publish, distribute, sublicense, and/or sell copies
16     of the Software, and to permit persons to whom the Software is
17     furnished to do so, subject to the following conditions:
18
19     The above copyright notice and this permission notice shall be
20     included in all copies or substantial portions of the Software.
21
22     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
26     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30     Except as contained in this notice, the names of the Centre for
31     Digital Music; Queen Mary, University of London; and Chris Cannam
32     shall not be used in advertising or otherwise to promote the sale,
33     use or other dealings in this Software without prior written
34     authorization.
35 */
36
37 #ifdef COMPILER_MSVC
38 #include <ardourext/float_cast.h>
39 #endif
40 #include "PercussionOnsetDetector.h"
41
42 using std::string;
43 using std::vector;
44 using std::cerr;
45 using std::endl;
46
47 #include <cmath>
48
49
50 PercussionOnsetDetector::PercussionOnsetDetector(float inputSampleRate) :
51     Plugin(inputSampleRate),
52     m_stepSize(0),
53     m_blockSize(0),
54     m_threshold(3),
55     m_sensitivity(40),
56     m_priorMagnitudes(0),
57     m_dfMinus1(0),
58     m_dfMinus2(0)
59 {
60 }
61
62 PercussionOnsetDetector::~PercussionOnsetDetector()
63 {
64     delete[] m_priorMagnitudes;
65 }
66
67 string
68 PercussionOnsetDetector::getIdentifier() const
69 {
70     return "percussiononsets";
71 }
72
73 string
74 PercussionOnsetDetector::getName() const
75 {
76     return "Simple Percussion Onset Detector";
77 }
78
79 string
80 PercussionOnsetDetector::getDescription() const
81 {
82     return "Detect percussive note onsets by identifying broadband energy rises";
83 }
84
85 string
86 PercussionOnsetDetector::getMaker() const
87 {
88     return "Vamp SDK Example Plugins";
89 }
90
91 int
92 PercussionOnsetDetector::getPluginVersion() const
93 {
94     return 2;
95 }
96
97 string
98 PercussionOnsetDetector::getCopyright() const
99 {
100     return "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005.  Freely redistributable (BSD license)";
101 }
102
103 size_t
104 PercussionOnsetDetector::getPreferredStepSize() const
105 {
106     return 0;
107 }
108
109 size_t
110 PercussionOnsetDetector::getPreferredBlockSize() const
111 {
112     return 1024;
113 }
114
115 bool
116 PercussionOnsetDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
117 {
118     if (channels < getMinChannelCount() ||
119         channels > getMaxChannelCount()) return false;
120
121     m_stepSize = stepSize;
122     m_blockSize = blockSize;
123
124     m_priorMagnitudes = new float[m_blockSize/2];
125
126     for (size_t i = 0; i < m_blockSize/2; ++i) {
127         m_priorMagnitudes[i] = 0.f;
128     }
129
130     m_dfMinus1 = 0.f;
131     m_dfMinus2 = 0.f;
132
133     return true;
134 }
135
136 void
137 PercussionOnsetDetector::reset()
138 {
139     for (size_t i = 0; i < m_blockSize/2; ++i) {
140         m_priorMagnitudes[i] = 0.f;
141     }
142
143     m_dfMinus1 = 0.f;
144     m_dfMinus2 = 0.f;
145 }
146
147 PercussionOnsetDetector::ParameterList
148 PercussionOnsetDetector::getParameterDescriptors() const
149 {
150     ParameterList list;
151
152     ParameterDescriptor d;
153     d.identifier = "threshold";
154     d.name = "Energy rise threshold";
155     d.description = "Energy rise within a frequency bin necessary to count toward broadband total";
156     d.unit = "dB";
157     d.minValue = 0;
158     d.maxValue = 20;
159     d.defaultValue = 3;
160     d.isQuantized = false;
161     list.push_back(d);
162
163     d.identifier = "sensitivity";
164     d.name = "Sensitivity";
165     d.description = "Sensitivity of peak detector applied to broadband detection function";
166     d.unit = "%";
167     d.minValue = 0;
168     d.maxValue = 100;
169     d.defaultValue = 40;
170     d.isQuantized = false;
171     list.push_back(d);
172
173     return list;
174 }
175
176 float
177 PercussionOnsetDetector::getParameter(std::string id) const
178 {
179     if (id == "threshold") return m_threshold;
180     if (id == "sensitivity") return m_sensitivity;
181     return 0.f;
182 }
183
184 void
185 PercussionOnsetDetector::setParameter(std::string id, float value)
186 {
187     if (id == "threshold") {
188         if (value < 0) value = 0;
189         if (value > 20) value = 20;
190         m_threshold = value;
191     } else if (id == "sensitivity") {
192         if (value < 0) value = 0;
193         if (value > 100) value = 100;
194         m_sensitivity = value;
195     }
196 }
197
198 PercussionOnsetDetector::OutputList
199 PercussionOnsetDetector::getOutputDescriptors() const
200 {
201     OutputList list;
202
203     OutputDescriptor d;
204     d.identifier = "onsets";
205     d.name = "Onsets";
206     d.description = "Percussive note onset locations";
207     d.unit = "";
208     d.hasFixedBinCount = true;
209     d.binCount = 0;
210     d.hasKnownExtents = false;
211     d.isQuantized = false;
212     d.sampleType = OutputDescriptor::VariableSampleRate;
213     d.sampleRate = m_inputSampleRate;
214     list.push_back(d);
215
216     d.identifier = "detectionfunction";
217     d.name = "Detection Function";
218     d.description = "Broadband energy rise detection function";
219     d.binCount = 1;
220     d.isQuantized = true;
221     d.quantizeStep = 1.0;
222     d.sampleType = OutputDescriptor::OneSamplePerStep;
223     list.push_back(d);
224
225     return list;
226 }
227
228 PercussionOnsetDetector::FeatureSet
229 PercussionOnsetDetector::process(const float *const *inputBuffers,
230                                  Vamp::RealTime ts)
231 {
232     if (m_stepSize == 0) {
233         cerr << "ERROR: PercussionOnsetDetector::process: "
234              << "PercussionOnsetDetector has not been initialised"
235              << endl;
236         return FeatureSet();
237     }
238
239     int count = 0;
240
241     for (size_t i = 1; i < m_blockSize/2; ++i) {
242
243         float real = inputBuffers[0][i*2];
244         float imag = inputBuffers[0][i*2 + 1];
245
246         float sqrmag = real * real + imag * imag;
247
248         if (m_priorMagnitudes[i] > 0.f) {
249             float diff = 10.f * log10f(sqrmag / m_priorMagnitudes[i]);
250
251 //        std::cout << "i=" << i << ", mag=" << mag << ", prior=" << m_priorMagnitudes[i] << ", diff=" << diff << ", threshold=" << m_threshold << std::endl;
252
253             if (diff >= m_threshold) ++count;
254         }
255
256         m_priorMagnitudes[i] = sqrmag;
257     }
258
259     FeatureSet returnFeatures;
260
261     Feature detectionFunction;
262     detectionFunction.hasTimestamp = false;
263     detectionFunction.values.push_back(count);
264     returnFeatures[1].push_back(detectionFunction);
265
266     if (m_dfMinus2 < m_dfMinus1 &&
267         m_dfMinus1 >= count &&
268         m_dfMinus1 > ((100 - m_sensitivity) * m_blockSize) / 200) {
269
270         Feature onset;
271         onset.hasTimestamp = true;
272         onset.timestamp = ts - Vamp::RealTime::frame2RealTime
273             (m_stepSize, lrintf(m_inputSampleRate));
274         returnFeatures[0].push_back(onset);
275     }
276
277     m_dfMinus2 = m_dfMinus1;
278     m_dfMinus1 = count;
279
280     return returnFeatures;
281 }
282
283 PercussionOnsetDetector::FeatureSet
284 PercussionOnsetDetector::getRemainingFeatures()
285 {
286     return FeatureSet();
287 }
288