annotate ConstrainedHarmonicPeak.cpp @ 14:de970142809f tip

Fix some warnings
author Chris Cannam
date Wed, 10 Jul 2019 10:39:57 +0100
parents f82a28c2209f
children
rev   line source
Chris@7 1 /*
Chris@7 2 CHP
Chris@7 3 Copyright (c) 2014 Queen Mary, University of London
Chris@7 4
Chris@7 5 Permission is hereby granted, free of charge, to any person
Chris@7 6 obtaining a copy of this software and associated documentation
Chris@7 7 files (the "Software"), to deal in the Software without
Chris@7 8 restriction, including without limitation the rights to use, copy,
Chris@7 9 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@7 10 of the Software, and to permit persons to whom the Software is
Chris@7 11 furnished to do so, subject to the following conditions:
Chris@7 12
Chris@7 13 The above copyright notice and this permission notice shall be
Chris@7 14 included in all copies or substantial portions of the Software.
Chris@7 15
Chris@7 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@7 17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@7 18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@7 19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
Chris@7 20 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@7 21 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@7 22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@7 23 */
Chris@0 24
Chris@0 25 #include "ConstrainedHarmonicPeak.h"
Chris@0 26
Chris@0 27 #include <cmath>
Chris@0 28 #include <cstdio>
Chris@14 29 #include <climits>
Chris@0 30
Chris@0 31 using std::cerr;
Chris@0 32 using std::endl;
Chris@0 33 using std::vector;
Chris@0 34
Chris@0 35 ConstrainedHarmonicPeak::ConstrainedHarmonicPeak(float inputSampleRate) :
Chris@0 36 Plugin(inputSampleRate),
Chris@4 37 m_fftSize(0),
Chris@0 38 m_minFreq(0),
Chris@6 39 m_maxFreq(22050),
Chris@1 40 m_harmonics(5)
Chris@0 41 {
Chris@0 42 }
Chris@0 43
Chris@0 44 ConstrainedHarmonicPeak::~ConstrainedHarmonicPeak()
Chris@0 45 {
Chris@0 46 }
Chris@0 47
Chris@0 48 string
Chris@0 49 ConstrainedHarmonicPeak::getIdentifier() const
Chris@0 50 {
Chris@0 51 return "constrainedharmonicpeak";
Chris@0 52 }
Chris@0 53
Chris@0 54 string
Chris@0 55 ConstrainedHarmonicPeak::getName() const
Chris@0 56 {
Chris@0 57 return "Frequency-Constrained Harmonic Peak";
Chris@0 58 }
Chris@0 59
Chris@0 60 string
Chris@0 61 ConstrainedHarmonicPeak::getDescription() const
Chris@0 62 {
Chris@6 63 return "Return the interpolated peak frequency of a harmonic product spectrum within a given frequency range";
Chris@0 64 }
Chris@0 65
Chris@0 66 string
Chris@0 67 ConstrainedHarmonicPeak::getMaker() const
Chris@0 68 {
Chris@0 69 return "Queen Mary, University of London";
Chris@0 70 }
Chris@0 71
Chris@0 72 int
Chris@0 73 ConstrainedHarmonicPeak::getPluginVersion() const
Chris@0 74 {
Chris@0 75 return 1;
Chris@0 76 }
Chris@0 77
Chris@0 78 string
Chris@0 79 ConstrainedHarmonicPeak::getCopyright() const
Chris@0 80 {
Chris@0 81 return "GPL";
Chris@0 82 }
Chris@0 83
Chris@0 84 ConstrainedHarmonicPeak::InputDomain
Chris@0 85 ConstrainedHarmonicPeak::getInputDomain() const
Chris@0 86 {
Chris@0 87 return FrequencyDomain;
Chris@0 88 }
Chris@0 89
Chris@0 90 size_t
Chris@0 91 ConstrainedHarmonicPeak::getPreferredBlockSize() const
Chris@0 92 {
Chris@0 93 return 2048;
Chris@0 94 }
Chris@0 95
Chris@0 96 size_t
Chris@0 97 ConstrainedHarmonicPeak::getPreferredStepSize() const
Chris@0 98 {
Chris@0 99 return 512;
Chris@0 100 }
Chris@0 101
Chris@0 102 size_t
Chris@0 103 ConstrainedHarmonicPeak::getMinChannelCount() const
Chris@0 104 {
Chris@0 105 return 1;
Chris@0 106 }
Chris@0 107
Chris@0 108 size_t
Chris@0 109 ConstrainedHarmonicPeak::getMaxChannelCount() const
Chris@0 110 {
Chris@0 111 return 1;
Chris@0 112 }
Chris@0 113
Chris@0 114 ConstrainedHarmonicPeak::ParameterList
Chris@0 115 ConstrainedHarmonicPeak::getParameterDescriptors() const
Chris@0 116 {
Chris@0 117 ParameterList list;
Chris@0 118
Chris@0 119 ParameterDescriptor d;
Chris@0 120 d.identifier = "minfreq";
Chris@0 121 d.name = "Minimum frequency";
Chris@6 122 d.description = "Minimum frequency for peak finding. Will be rounded down to the nearest spectral bin.";
Chris@0 123 d.unit = "Hz";
Chris@0 124 d.minValue = 0;
Chris@0 125 d.maxValue = m_inputSampleRate/2;
Chris@0 126 d.defaultValue = 0;
Chris@0 127 d.isQuantized = false;
Chris@0 128 list.push_back(d);
Chris@0 129
Chris@0 130 d.identifier = "maxfreq";
Chris@0 131 d.name = "Maximum frequency";
Chris@6 132 d.description = "Maximum frequency for peak finding. Will be rounded up to the nearest spectral bin.";
Chris@0 133 d.unit = "Hz";
Chris@0 134 d.minValue = 0;
Chris@0 135 d.maxValue = m_inputSampleRate/2;
Chris@6 136 d.defaultValue = 22050;
Chris@0 137 d.isQuantized = false;
Chris@0 138 list.push_back(d);
Chris@0 139
Chris@1 140 d.identifier = "harmonics";
Chris@1 141 d.name = "Harmonics";
Chris@1 142 d.description = "Maximum number of harmonics to consider";
Chris@1 143 d.unit = "";
Chris@1 144 d.minValue = 1;
Chris@1 145 d.maxValue = 20;
Chris@1 146 d.defaultValue = 5;
Chris@1 147 d.isQuantized = true;
Chris@1 148 d.quantizeStep = 1;
Chris@1 149 list.push_back(d);
Chris@1 150
Chris@0 151 return list;
Chris@0 152 }
Chris@0 153
Chris@0 154 float
Chris@0 155 ConstrainedHarmonicPeak::getParameter(string identifier) const
Chris@0 156 {
Chris@0 157 if (identifier == "minfreq") {
Chris@0 158 return m_minFreq;
Chris@0 159 } else if (identifier == "maxfreq") {
Chris@0 160 return m_maxFreq;
Chris@1 161 } else if (identifier == "harmonics") {
Chris@14 162 return float(m_harmonics);
Chris@0 163 }
Chris@0 164 return 0;
Chris@0 165 }
Chris@0 166
Chris@0 167 void
Chris@0 168 ConstrainedHarmonicPeak::setParameter(string identifier, float value)
Chris@0 169 {
Chris@0 170 if (identifier == "minfreq") {
Chris@0 171 m_minFreq = value;
Chris@0 172 } else if (identifier == "maxfreq") {
Chris@0 173 m_maxFreq = value;
Chris@1 174 } else if (identifier == "harmonics") {
Chris@1 175 m_harmonics = int(round(value));
Chris@0 176 }
Chris@0 177 }
Chris@0 178
Chris@0 179 ConstrainedHarmonicPeak::ProgramList
Chris@0 180 ConstrainedHarmonicPeak::getPrograms() const
Chris@0 181 {
Chris@0 182 ProgramList list;
Chris@0 183 return list;
Chris@0 184 }
Chris@0 185
Chris@0 186 string
Chris@0 187 ConstrainedHarmonicPeak::getCurrentProgram() const
Chris@0 188 {
Chris@0 189 return ""; // no programs
Chris@0 190 }
Chris@0 191
Chris@0 192 void
Chris@14 193 ConstrainedHarmonicPeak::selectProgram(string)
Chris@0 194 {
Chris@0 195 }
Chris@0 196
Chris@0 197 ConstrainedHarmonicPeak::OutputList
Chris@0 198 ConstrainedHarmonicPeak::getOutputDescriptors() const
Chris@0 199 {
Chris@0 200 OutputList list;
Chris@0 201
Chris@0 202 OutputDescriptor d;
Chris@0 203 d.identifier = "peak";
Chris@0 204 d.name = "Peak frequency";
Chris@6 205 d.description = "Interpolated frequency of the harmonic spectral peak within the given frequency range";
Chris@0 206 d.unit = "Hz";
Chris@0 207 d.sampleType = OutputDescriptor::OneSamplePerStep;
Chris@0 208 d.hasDuration = false;
Chris@0 209 list.push_back(d);
Chris@0 210
Chris@0 211 return list;
Chris@0 212 }
Chris@0 213
Chris@0 214 bool
Chris@14 215 ConstrainedHarmonicPeak::initialise(size_t channels, size_t, size_t blockSize)
Chris@0 216 {
Chris@0 217 if (channels < getMinChannelCount() ||
Chris@0 218 channels > getMaxChannelCount()) {
Chris@0 219 cerr << "ConstrainedHarmonicPeak::initialise: ERROR: channels " << channels
Chris@0 220 << " out of acceptable range " << getMinChannelCount()
Chris@0 221 << " -> " << getMaxChannelCount() << endl;
Chris@0 222 return false;
Chris@0 223 }
Chris@0 224
Chris@14 225 if (blockSize > INT_MAX) {
Chris@14 226 return false; // arf
Chris@14 227 }
Chris@14 228
Chris@14 229 m_fftSize = int(blockSize);
Chris@0 230
Chris@0 231 return true;
Chris@0 232 }
Chris@0 233
Chris@0 234 void
Chris@0 235 ConstrainedHarmonicPeak::reset()
Chris@0 236 {
Chris@0 237 }
Chris@0 238
Chris@1 239 double
Chris@1 240 ConstrainedHarmonicPeak::findInterpolatedPeak(const double *in,
Chris@1 241 int peakbin,
Chris@1 242 int bins)
Chris@1 243 {
Chris@1 244 // duplicate with SimpleCepstrum plugin
Chris@1 245 // after jos,
Chris@1 246 // https://ccrma.stanford.edu/~jos/sasp/Quadratic_Interpolation_Spectral_Peaks.html
Chris@1 247
Chris@1 248 if (peakbin < 1 || peakbin > bins - 2) {
Chris@1 249 return peakbin;
Chris@1 250 }
Chris@1 251
Chris@1 252 double alpha = in[peakbin-1];
Chris@1 253 double beta = in[peakbin];
Chris@1 254 double gamma = in[peakbin+1];
Chris@1 255
Chris@1 256 double denom = (alpha - 2*beta + gamma);
Chris@1 257
Chris@1 258 if (denom == 0) {
Chris@1 259 // flat
Chris@1 260 return peakbin;
Chris@1 261 }
Chris@1 262
Chris@1 263 double p = ((alpha - gamma) / denom) / 2.0;
Chris@1 264
Chris@1 265 return double(peakbin) + p;
Chris@1 266 }
Chris@1 267
Chris@0 268 ConstrainedHarmonicPeak::FeatureSet
Chris@14 269 ConstrainedHarmonicPeak::process(const float *const *inputBuffers, Vamp::RealTime)
Chris@0 270 {
Chris@0 271 FeatureSet fs;
Chris@0 272
Chris@6 273 // This could be better. The procedure here is
Chris@6 274 //
Chris@6 275 // 1 Produce a harmonic product spectrum within a limited
Chris@6 276 // frequency range by effectively summing the dB values of the
Chris@6 277 // bins at each multiple of the bin numbers (up to a given
Chris@6 278 // number of harmonics) in the range under consideration
Chris@6 279 // 2 Find the peak bin
Chris@6 280 // 3 Calculate the peak location by quadratic interpolation
Chris@6 281 // from the peak bin and its two neighbouring bins
Chris@6 282 //
Chris@6 283 // Problems with this:
Chris@6 284 //
Chris@6 285 // 1 Harmonics might not be located at integer multiples of the
Chris@6 286 // original bin frequency
Chris@6 287 // 2 Quadratic interpolation works "correctly" for dB-valued
Chris@6 288 // magnitude spectra but might not produce the right results in
Chris@6 289 // the dB-summed hps, especially in light of the first problem
Chris@6 290 // 3 Interpolation might not make sense at all if there are
Chris@6 291 // multiple nearby frequencies interfering across the three
Chris@6 292 // bins used for interpolation (we may be unable to identify
Chris@6 293 // the right frequency at all, but it's possible interpolation
Chris@6 294 // will make our guess worse rather than better)
Chris@6 295 //
Chris@6 296 // Possible improvements:
Chris@6 297 //
Chris@6 298 // 1 Find the higher harmonics by looking for the peak bin within
Chris@6 299 // a range around the nominal peak location
Chris@6 300 // 2 Once a peak has been identified as the peak of the HPS, use
Chris@6 301 // the original spectrum (not the HPS) to obtain the values for
Chris@6 302 // interpolation? (would help with problem 2 but might make
Chris@6 303 // problem 3 worse)
Chris@6 304
Chris@4 305 int hs = m_fftSize/2;
Chris@1 306
Chris@1 307 double *mags = new double[hs+1];
Chris@1 308 for (int i = 0; i <= hs; ++i) {
Chris@4 309 mags[i] = sqrt(inputBuffers[0][i*2] * inputBuffers[0][i*2] +
Chris@4 310 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]);
Chris@1 311 }
Chris@1 312
Chris@1 313 // bin freq is bin * samplerate / fftsize
Chris@1 314
Chris@14 315 int minbin = int(floor((double(m_minFreq) * m_fftSize) / m_inputSampleRate));
Chris@14 316 int maxbin = int(ceil((double(m_maxFreq) * m_fftSize) / m_inputSampleRate));
Chris@1 317 if (minbin > hs) minbin = hs;
Chris@1 318 if (maxbin > hs) maxbin = hs;
Chris@1 319 if (maxbin <= minbin) return fs;
Chris@1 320
Chris@1 321 double *hps = new double[maxbin - minbin + 1];
Chris@1 322
Chris@1 323 // HPS in dB after MzHarmonicSpectrum
Chris@1 324
Chris@1 325 for (int bin = minbin; bin <= maxbin; ++bin) {
Chris@1 326
Chris@1 327 int i = bin - minbin;
Chris@1 328 hps[i] = 1.0;
Chris@1 329
Chris@1 330 int contributing = 0;
Chris@1 331
Chris@1 332 for (int j = 1; j <= m_harmonics; ++j) {
Chris@1 333 if (j * bin > hs) break;
Chris@1 334 hps[i] *= mags[j * bin];
Chris@1 335 ++contributing;
Chris@1 336 }
Chris@1 337
Chris@1 338 if (hps[i] <= 0.0) {
Chris@1 339 hps[i] = -120.0;
Chris@1 340 } else {
Chris@1 341 hps[i] = 20.0 / contributing * log10(hps[i]);
Chris@1 342 }
Chris@1 343 }
Chris@1 344
Chris@1 345 double maxdb = -120.0;
Chris@1 346 int maxidx = 0;
Chris@1 347 for (int i = 0; i <= maxbin - minbin; ++i) {
matthiasm@9 348 if (hps[i] > maxdb) {
matthiasm@9 349 maxdb = hps[i];
matthiasm@9 350 maxidx = i;
matthiasm@9 351 }
matthiasm@9 352 }
matthiasm@9 353
matthiasm@9 354 if (maxidx == 0 || maxidx == maxbin - minbin) { // edge cases are useless
matthiasm@9 355 return fs;
Chris@1 356 }
Chris@1 357
Chris@1 358 double interpolated = findInterpolatedPeak(hps, maxidx, maxbin - minbin + 1);
matthiasm@9 359
Chris@1 360 interpolated = interpolated + minbin;
Chris@1 361
Chris@4 362 double freq = interpolated * m_inputSampleRate / m_fftSize;
matthiasm@9 363
matthiasm@9 364 if (freq < m_minFreq || freq > m_maxFreq) {
matthiasm@9 365 return fs;
matthiasm@9 366 }
Chris@1 367
Chris@1 368 Feature f;
Chris@14 369 f.values.push_back(float(freq));
Chris@1 370 fs[0].push_back(f);
Chris@1 371
Chris@0 372 return fs;
Chris@0 373 }
Chris@0 374
Chris@0 375 ConstrainedHarmonicPeak::FeatureSet
Chris@0 376 ConstrainedHarmonicPeak::getRemainingFeatures()
Chris@0 377 {
Chris@0 378 FeatureSet fs;
Chris@0 379 return fs;
Chris@0 380 }
Chris@0 381