annotate spectrum-compare-plugin/TuningDifference.cpp @ 20:331a520cdadb

Update to reflect changes in Chromagram api
author Chris Cannam
date Thu, 05 Feb 2015 09:52:28 +0000
parents 812e4d021443
children
rev   line source
Chris@0 1
Chris@0 2 #include "TuningDifference.h"
Chris@0 3
Chris@1 4 #include <iostream>
Chris@1 5
Chris@4 6 #include <cmath>
Chris@4 7 #include <cstdio>
Chris@4 8
Chris@1 9 using std::cerr;
Chris@1 10 using std::endl;
Chris@1 11
Chris@5 12 static double targetFmin = 60.0;
Chris@5 13 static double targetFmax = 1500.0;
Chris@5 14
Chris@0 15 TuningDifference::TuningDifference(float inputSampleRate) :
Chris@0 16 Plugin(inputSampleRate)
Chris@0 17 {
Chris@0 18 }
Chris@0 19
Chris@0 20 TuningDifference::~TuningDifference()
Chris@0 21 {
Chris@0 22 }
Chris@0 23
Chris@0 24 string
Chris@0 25 TuningDifference::getIdentifier() const
Chris@0 26 {
Chris@1 27 return "tuning-difference";
Chris@0 28 }
Chris@0 29
Chris@0 30 string
Chris@0 31 TuningDifference::getName() const
Chris@0 32 {
Chris@1 33 return "Tuning Difference";
Chris@0 34 }
Chris@0 35
Chris@0 36 string
Chris@0 37 TuningDifference::getDescription() const
Chris@0 38 {
Chris@0 39 // Return something helpful here!
Chris@0 40 return "";
Chris@0 41 }
Chris@0 42
Chris@0 43 string
Chris@0 44 TuningDifference::getMaker() const
Chris@0 45 {
Chris@0 46 // Your name here
Chris@0 47 return "";
Chris@0 48 }
Chris@0 49
Chris@0 50 int
Chris@0 51 TuningDifference::getPluginVersion() const
Chris@0 52 {
Chris@0 53 // Increment this each time you release a version that behaves
Chris@0 54 // differently from the previous one
Chris@0 55 return 1;
Chris@0 56 }
Chris@0 57
Chris@0 58 string
Chris@0 59 TuningDifference::getCopyright() const
Chris@0 60 {
Chris@0 61 // This function is not ideally named. It does not necessarily
Chris@0 62 // need to say who made the plugin -- getMaker does that -- but it
Chris@0 63 // should indicate the terms under which it is distributed. For
Chris@0 64 // example, "Copyright (year). All Rights Reserved", or "GPL"
Chris@0 65 return "";
Chris@0 66 }
Chris@0 67
Chris@0 68 TuningDifference::InputDomain
Chris@0 69 TuningDifference::getInputDomain() const
Chris@0 70 {
Chris@1 71 return FrequencyDomain;
Chris@0 72 }
Chris@0 73
Chris@0 74 size_t
Chris@0 75 TuningDifference::getPreferredBlockSize() const
Chris@0 76 {
Chris@2 77 return 16384;
Chris@0 78 }
Chris@0 79
Chris@0 80 size_t
Chris@0 81 TuningDifference::getPreferredStepSize() const
Chris@0 82 {
Chris@1 83 return 0;
Chris@0 84 }
Chris@0 85
Chris@0 86 size_t
Chris@0 87 TuningDifference::getMinChannelCount() const
Chris@0 88 {
Chris@1 89 return 2;
Chris@0 90 }
Chris@0 91
Chris@0 92 size_t
Chris@0 93 TuningDifference::getMaxChannelCount() const
Chris@0 94 {
Chris@1 95 return 2;
Chris@0 96 }
Chris@0 97
Chris@0 98 TuningDifference::ParameterList
Chris@0 99 TuningDifference::getParameterDescriptors() const
Chris@0 100 {
Chris@0 101 ParameterList list;
Chris@0 102 return list;
Chris@0 103 }
Chris@0 104
Chris@0 105 float
Chris@1 106 TuningDifference::getParameter(string) const
Chris@0 107 {
Chris@0 108 return 0;
Chris@0 109 }
Chris@0 110
Chris@0 111 void
Chris@1 112 TuningDifference::setParameter(string, float)
Chris@0 113 {
Chris@0 114 }
Chris@0 115
Chris@0 116 TuningDifference::ProgramList
Chris@0 117 TuningDifference::getPrograms() const
Chris@0 118 {
Chris@0 119 ProgramList list;
Chris@0 120 return list;
Chris@0 121 }
Chris@0 122
Chris@0 123 string
Chris@0 124 TuningDifference::getCurrentProgram() const
Chris@0 125 {
Chris@0 126 return ""; // no programs
Chris@0 127 }
Chris@0 128
Chris@0 129 void
Chris@1 130 TuningDifference::selectProgram(string)
Chris@0 131 {
Chris@0 132 }
Chris@0 133
Chris@0 134 TuningDifference::OutputList
Chris@0 135 TuningDifference::getOutputDescriptors() const
Chris@0 136 {
Chris@0 137 OutputList list;
Chris@0 138
Chris@1 139 OutputDescriptor d;
Chris@1 140 d.identifier = "cents";
Chris@1 141 d.name = "Tuning Difference";
Chris@1 142 d.description = "Difference in averaged frequency profile between channels 1 and 2, in cents. A positive value means channel 2 is higher.";
Chris@1 143 d.unit = "cents";
Chris@1 144 d.hasFixedBinCount = true;
Chris@1 145 d.binCount = 1;
Chris@1 146 d.hasKnownExtents = false;
Chris@1 147 d.isQuantized = false;
Chris@1 148 d.sampleType = OutputDescriptor::VariableSampleRate;
Chris@1 149 d.hasDuration = false;
Chris@1 150 list.push_back(d);
Chris@0 151
Chris@1 152 d.identifier = "tuningfreq";
Chris@1 153 d.name = "Relative Tuning Frequency";
Chris@1 154 d.description = "Tuning frequency of channel 2, if channel 1 is assumed to contain the same music as it at a tuning frequency of A=440Hz.";
Chris@4 155 d.unit = "hz";
Chris@1 156 d.hasFixedBinCount = true;
Chris@1 157 d.binCount = 1;
Chris@1 158 d.hasKnownExtents = false;
Chris@1 159 d.isQuantized = false;
Chris@1 160 d.sampleType = OutputDescriptor::VariableSampleRate;
Chris@1 161 d.hasDuration = false;
Chris@1 162 list.push_back(d);
Chris@1 163
Chris@4 164 d.identifier = "curve";
Chris@4 165 d.name = "Shift Correlation Curve";
Chris@4 166 d.description = "Correlation between shifted and unshifted sources, for each probed shift in cents.";
Chris@0 167 d.unit = "";
Chris@0 168 d.hasFixedBinCount = true;
Chris@0 169 d.binCount = 1;
Chris@0 170 d.hasKnownExtents = false;
Chris@0 171 d.isQuantized = false;
Chris@1 172 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@1 173 d.sampleRate = 100;
Chris@0 174 d.hasDuration = false;
Chris@0 175 list.push_back(d);
Chris@0 176
Chris@5 177 int targetBinMin = int(floor(targetFmin * m_blockSize / m_inputSampleRate));
Chris@5 178 int targetBinMax = int(ceil(targetFmax * m_blockSize / m_inputSampleRate));
Chris@5 179 cerr << "target bin range: " << targetBinMin << " -> " << targetBinMax << endl;
Chris@5 180
Chris@4 181 d.identifier = "averages";
Chris@9 182 d.name = "Harmonic spectrum averages";
Chris@9 183 d.description = "Average of harmonic spectrum for each channel.";
Chris@4 184 d.unit = "";
Chris@4 185 d.hasFixedBinCount = true;
Chris@5 186 d.binCount = (targetBinMax > targetBinMin ? targetBinMax - targetBinMin + 1 : 100);
Chris@4 187 d.hasKnownExtents = false;
Chris@4 188 d.isQuantized = false;
Chris@4 189 d.sampleType = OutputDescriptor::FixedSampleRate;
Chris@5 190 d.sampleRate = 1;
Chris@4 191 d.hasDuration = false;
Chris@4 192 list.push_back(d);
Chris@4 193
Chris@0 194 return list;
Chris@0 195 }
Chris@0 196
Chris@0 197 bool
Chris@0 198 TuningDifference::initialise(size_t channels, size_t stepSize, size_t blockSize)
Chris@0 199 {
Chris@0 200 if (channels < getMinChannelCount() ||
Chris@0 201 channels > getMaxChannelCount()) return false;
Chris@0 202
Chris@1 203 if (blockSize != getPreferredBlockSize() ||
Chris@1 204 stepSize != blockSize/2) return false;
Chris@0 205
Chris@1 206 m_blockSize = blockSize;
Chris@1 207
Chris@1 208 reset();
Chris@1 209
Chris@0 210 return true;
Chris@0 211 }
Chris@0 212
Chris@0 213 void
Chris@0 214 TuningDifference::reset()
Chris@0 215 {
Chris@1 216 m_sum[0].clear();
Chris@1 217 m_sum[1].clear();
Chris@1 218 m_frameCount = 0;
Chris@0 219 }
Chris@0 220
Chris@0 221 TuningDifference::FeatureSet
Chris@0 222 TuningDifference::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
Chris@0 223 {
Chris@1 224 for (int c = 0; c < 2; ++c) {
Chris@4 225 if (m_sum[c].size() == 0) {
Chris@4 226 m_sum[c].resize(m_blockSize/2 + 1, 0.0);
Chris@4 227 }
Chris@9 228 vector<double> mags(m_blockSize/2 + 1, 0.0);
Chris@4 229 for (int i = 0; i <= m_blockSize/2; ++i) {
Chris@1 230 double energy =
Chris@1 231 inputBuffers[c][i*2 ] * inputBuffers[c][i*2 ] +
Chris@1 232 inputBuffers[c][i*2+1] * inputBuffers[c][i*2+1];
Chris@5 233 double mag = sqrt(energy);
Chris@9 234 mags[i] = mag;
Chris@9 235 }
Chris@9 236 for (int i = 0; i <= m_blockSize/2; ++i) {
Chris@9 237 for (int h = 2; h <= 4; ++h) {
Chris@9 238 double max = 0.0;
Chris@9 239 for (int j = 0; j <= h; ++j) {
Chris@9 240 int ix = i * h + j - h/2;
Chris@9 241 if (ix <= m_blockSize/2) {
Chris@9 242 double harmMag = mags[ix];
Chris@9 243 if (j == 0 || harmMag > max) {
Chris@9 244 max = harmMag;
Chris@9 245 }
Chris@9 246 }
Chris@9 247 }
Chris@9 248 mags[i] += max;
Chris@9 249 }
Chris@9 250 }
Chris@9 251 for (int i = 0; i <= m_blockSize/2; ++i) {
Chris@9 252 m_sum[c][i] += mags[i];
Chris@1 253 }
Chris@1 254 }
Chris@1 255
Chris@1 256 ++m_frameCount;
Chris@0 257 return FeatureSet();
Chris@0 258 }
Chris@0 259
Chris@0 260 TuningDifference::FeatureSet
Chris@0 261 TuningDifference::getRemainingFeatures()
Chris@0 262 {
Chris@1 263 int n = m_sum[0].size();
Chris@1 264 if (n == 0) return FeatureSet();
Chris@1 265
Chris@1 266 Feature f;
Chris@1 267 FeatureSet fs;
Chris@3 268
Chris@4 269 int maxshift = 2400; // cents
Chris@4 270
Chris@4 271 int bestshift = -1;
Chris@4 272 double bestdist = 0;
Chris@4 273
Chris@4 274 for (int i = 0; i < n; ++i) {
Chris@4 275 m_sum[0][i] /= m_frameCount;
Chris@4 276 m_sum[1][i] /= m_frameCount;
Chris@4 277 }
Chris@4 278
Chris@4 279 for (int c = 0; c < 2; ++c) {
Chris@9 280 for (int i = 0; i < n; ++i) {
Chris@9 281 if (i == 0 || i == n-1 ||
Chris@9 282 m_sum[c][i] < m_sum[c][i-1] ||
Chris@9 283 m_sum[c][i] < m_sum[c][i+1]) {
Chris@9 284 m_sum[c][i] = 0.0;
Chris@9 285 }
Chris@9 286 }
Chris@9 287 }
Chris@9 288
Chris@9 289 int targetBinMin = int(floor(targetFmin * m_blockSize / m_inputSampleRate));
Chris@9 290 int targetBinMax = int(ceil(targetFmax * m_blockSize / m_inputSampleRate));
Chris@9 291 cerr << "target bin range: " << targetBinMin << " -> " << targetBinMax << endl;
Chris@9 292
Chris@9 293 for (int c = 0; c < 2; ++c) {
Chris@4 294 double tot = 0.0;
Chris@9 295 for (int i = targetBinMin; i <= targetBinMax; ++i) {
Chris@4 296 tot += m_sum[c][i];
Chris@4 297 }
Chris@4 298 if (tot != 0.0) {
Chris@4 299 for (int i = 0; i < n; ++i) {
Chris@4 300 m_sum[c][i] /= tot;
Chris@1 301 }
Chris@1 302 }
Chris@4 303 }
Chris@4 304
Chris@5 305 f.values.clear();
Chris@5 306 for (int i = targetBinMin; i < targetBinMax; ++i) {
Chris@4 307 f.values.push_back(m_sum[0][i]);
Chris@5 308 }
Chris@5 309 fs[3].push_back(f);
Chris@5 310 f.values.clear();
Chris@5 311 for (int i = targetBinMin; i < targetBinMax; ++i) {
Chris@4 312 f.values.push_back(m_sum[1][i]);
Chris@1 313 }
Chris@5 314 fs[3].push_back(f);
Chris@1 315
Chris@4 316 f.values.clear();
Chris@5 317
Chris@4 318 for (int shift = -maxshift; shift <= maxshift; ++shift) {
Chris@4 319
Chris@4 320 double multiplier = pow(2.0, double(shift) / 1200.0);
Chris@4 321 double dist = 0.0;
Chris@4 322
Chris@4 323 // cerr << "shift = " << shift << ", multiplier = " << multiplier << endl;
Chris@5 324
Chris@5 325 int contributing = 0;
Chris@4 326
Chris@5 327 for (int i = targetBinMin; i < targetBinMax; ++i) {
Chris@4 328
Chris@4 329 double source = i / multiplier;
Chris@4 330 int s0 = int(source), s1 = s0 + 1;
Chris@4 331 double p1 = source - s0;
Chris@4 332 double p0 = 1.0 - p1;
Chris@4 333
Chris@4 334 double value = 0.0;
Chris@4 335 if (s0 >= 0 && s0 < n) {
Chris@4 336 value += p0 * m_sum[1][s0];
Chris@5 337 ++contributing;
Chris@4 338 }
Chris@4 339 if (s1 >= 0 && s1 < n) {
Chris@4 340 value += p1 * m_sum[1][s1];
Chris@5 341 ++contributing;
Chris@4 342 }
Chris@4 343
Chris@4 344 // if (shift == -1) {
Chris@4 345 // cerr << "for multiplier " << multiplier << ", target " << i << ", source " << source << ", value " << p0 << " * " << m_sum[1][s0] << " + " << p1 << " * " << m_sum[1][s1] << " = " << value << ", other " << m_sum[0][i] << endl;
Chris@4 346 // }
Chris@4 347
Chris@4 348 double diff = fabs(m_sum[0][i] - value);
Chris@4 349 dist += diff;
Chris@4 350 }
Chris@4 351
Chris@5 352 dist /= contributing;
Chris@5 353
Chris@4 354 f.values.clear();
Chris@4 355 f.values.push_back(dist);
Chris@4 356 char label[100];
Chris@4 357 sprintf(label, "%f at shift %d freq mult %f", dist, shift, multiplier);
Chris@4 358 f.label = label;
Chris@4 359 fs[2].push_back(f);
Chris@4 360
Chris@4 361 if (bestshift == -1 || dist < bestdist) {
Chris@4 362 bestshift = shift;
Chris@4 363 bestdist = dist;
Chris@4 364 }
Chris@4 365 }
Chris@4 366
Chris@4 367 f.timestamp = Vamp::RealTime::zeroTime;
Chris@4 368 f.hasTimestamp = true;
Chris@5 369 f.label = "";
Chris@4 370
Chris@4 371 f.values.clear();
Chris@9 372 cerr << "best dist = " << bestdist << " at shift " << bestshift << endl;
Chris@4 373 f.values.push_back(-bestshift);
Chris@4 374 fs[0].push_back(f);
Chris@4 375
Chris@4 376 f.values.clear();
Chris@4 377 f.values.push_back(440.0 / pow(2.0, double(bestshift) / 1200.0));
Chris@4 378 fs[1].push_back(f);
Chris@4 379
Chris@1 380 return fs;
Chris@0 381 }
Chris@0 382