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
|