annotate TuningDifference.cpp @ 4:16aa4b565780

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