annotate spectrum-compare-plugin/TuningDifference.cpp @ 12:23572f9d25d9

Rename & duplicate plugin
author Chris Cannam
date Wed, 04 Feb 2015 10:15:57 +0000
parents spectrum-compare/TuningDifference.cpp@1924df3245f4
children 812e4d021443
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@4 182 d.name = "Spectrum averages";
Chris@4 183 d.description = "Average magnitude 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@4 228 for (int i = 0; i <= m_blockSize/2; ++i) {
Chris@1 229 double energy =
Chris@1 230 inputBuffers[c][i*2 ] * inputBuffers[c][i*2 ] +
Chris@1 231 inputBuffers[c][i*2+1] * inputBuffers[c][i*2+1];
Chris@5 232 double mag = sqrt(energy);
Chris@5 233 m_sum[c][i] += mag;
Chris@5 234 m_sum[c][i/2] += mag;
Chris@1 235 }
Chris@1 236 }
Chris@1 237
Chris@1 238 ++m_frameCount;
Chris@0 239 return FeatureSet();
Chris@0 240 }
Chris@0 241
Chris@0 242 TuningDifference::FeatureSet
Chris@0 243 TuningDifference::getRemainingFeatures()
Chris@0 244 {
Chris@1 245 int n = m_sum[0].size();
Chris@1 246 if (n == 0) return FeatureSet();
Chris@1 247
Chris@1 248 Feature f;
Chris@1 249 FeatureSet fs;
Chris@3 250
Chris@4 251 int maxshift = 2400; // cents
Chris@4 252
Chris@4 253 int bestshift = -1;
Chris@4 254 double bestdist = 0;
Chris@4 255
Chris@4 256 for (int i = 0; i < n; ++i) {
Chris@4 257 m_sum[0][i] /= m_frameCount;
Chris@4 258 m_sum[1][i] /= m_frameCount;
Chris@4 259 }
Chris@4 260
Chris@4 261 for (int c = 0; c < 2; ++c) {
Chris@4 262 double tot = 0.0;
Chris@1 263 for (int i = 0; i < n; ++i) {
Chris@4 264 tot += m_sum[c][i];
Chris@4 265 }
Chris@4 266 if (tot != 0.0) {
Chris@4 267 for (int i = 0; i < n; ++i) {
Chris@4 268 m_sum[c][i] /= tot;
Chris@1 269 }
Chris@1 270 }
Chris@4 271 }
Chris@5 272
Chris@5 273 int targetBinMin = int(floor(targetFmin * m_blockSize / m_inputSampleRate));
Chris@5 274 int targetBinMax = int(ceil(targetFmax * m_blockSize / m_inputSampleRate));
Chris@5 275 cerr << "target bin range: " << targetBinMin << " -> " << targetBinMax << endl;
Chris@4 276
Chris@5 277 f.values.clear();
Chris@5 278 for (int i = targetBinMin; i < targetBinMax; ++i) {
Chris@4 279 f.values.push_back(m_sum[0][i]);
Chris@5 280 }
Chris@5 281 fs[3].push_back(f);
Chris@5 282 f.values.clear();
Chris@5 283 for (int i = targetBinMin; i < targetBinMax; ++i) {
Chris@4 284 f.values.push_back(m_sum[1][i]);
Chris@1 285 }
Chris@5 286 fs[3].push_back(f);
Chris@1 287
Chris@4 288 f.values.clear();
Chris@5 289
Chris@4 290 for (int shift = -maxshift; shift <= maxshift; ++shift) {
Chris@4 291
Chris@4 292 double multiplier = pow(2.0, double(shift) / 1200.0);
Chris@4 293 double dist = 0.0;
Chris@4 294
Chris@4 295 // cerr << "shift = " << shift << ", multiplier = " << multiplier << endl;
Chris@5 296
Chris@5 297 int contributing = 0;
Chris@4 298
Chris@5 299 for (int i = targetBinMin; i < targetBinMax; ++i) {
Chris@4 300
Chris@4 301 double source = i / multiplier;
Chris@4 302 int s0 = int(source), s1 = s0 + 1;
Chris@4 303 double p1 = source - s0;
Chris@4 304 double p0 = 1.0 - p1;
Chris@4 305
Chris@4 306 double value = 0.0;
Chris@4 307 if (s0 >= 0 && s0 < n) {
Chris@4 308 value += p0 * m_sum[1][s0];
Chris@5 309 ++contributing;
Chris@4 310 }
Chris@4 311 if (s1 >= 0 && s1 < n) {
Chris@4 312 value += p1 * m_sum[1][s1];
Chris@5 313 ++contributing;
Chris@4 314 }
Chris@4 315
Chris@4 316 // if (shift == -1) {
Chris@4 317 // 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 318 // }
Chris@4 319
Chris@4 320 double diff = fabs(m_sum[0][i] - value);
Chris@4 321 dist += diff;
Chris@4 322 }
Chris@4 323
Chris@5 324 dist /= contributing;
Chris@5 325
Chris@4 326 f.values.clear();
Chris@4 327 f.values.push_back(dist);
Chris@4 328 char label[100];
Chris@4 329 sprintf(label, "%f at shift %d freq mult %f", dist, shift, multiplier);
Chris@4 330 f.label = label;
Chris@4 331 fs[2].push_back(f);
Chris@4 332
Chris@4 333 if (bestshift == -1 || dist < bestdist) {
Chris@4 334 bestshift = shift;
Chris@4 335 bestdist = dist;
Chris@4 336 }
Chris@4 337 }
Chris@4 338
Chris@4 339 f.timestamp = Vamp::RealTime::zeroTime;
Chris@4 340 f.hasTimestamp = true;
Chris@5 341 f.label = "";
Chris@4 342
Chris@4 343 f.values.clear();
Chris@4 344 // cerr << "best dist = " << bestdist << " at shift " << bestshift << endl;
Chris@4 345 f.values.push_back(-bestshift);
Chris@4 346 fs[0].push_back(f);
Chris@4 347
Chris@4 348 f.values.clear();
Chris@4 349 f.values.push_back(440.0 / pow(2.0, double(bestshift) / 1200.0));
Chris@4 350 fs[1].push_back(f);
Chris@4 351
Chris@1 352 return fs;
Chris@0 353 }
Chris@0 354