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 
PercussionOnsetDetector(float inputSampleRate)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 
~PercussionOnsetDetector()62 PercussionOnsetDetector::~PercussionOnsetDetector()
63 {
64     delete[] m_priorMagnitudes;
65 }
66 
67 string
getIdentifier() const68 PercussionOnsetDetector::getIdentifier() const
69 {
70     return "percussiononsets";
71 }
72 
73 string
getName() const74 PercussionOnsetDetector::getName() const
75 {
76     return "Simple Percussion Onset Detector";
77 }
78 
79 string
getDescription() const80 PercussionOnsetDetector::getDescription() const
81 {
82     return "Detect percussive note onsets by identifying broadband energy rises";
83 }
84 
85 string
getMaker() const86 PercussionOnsetDetector::getMaker() const
87 {
88     return "Vamp SDK Example Plugins";
89 }
90 
91 int
getPluginVersion() const92 PercussionOnsetDetector::getPluginVersion() const
93 {
94     return 2;
95 }
96 
97 string
getCopyright() const98 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
getPreferredStepSize() const104 PercussionOnsetDetector::getPreferredStepSize() const
105 {
106     return 0;
107 }
108 
109 size_t
getPreferredBlockSize() const110 PercussionOnsetDetector::getPreferredBlockSize() const
111 {
112     return 1024;
113 }
114 
115 bool
initialise(size_t channels,size_t stepSize,size_t blockSize)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
reset()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
getParameterDescriptors() const148 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
getParameter(std::string id) const177 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
setParameter(std::string id,float value)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
getOutputDescriptors() const199 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
process(const float * const * inputBuffers,Vamp::RealTime ts)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
getRemainingFeatures()284 PercussionOnsetDetector::getRemainingFeatures()
285 {
286     return FeatureSet();
287 }
288 
289