forked from ibsh/libKeyFinder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keyfinder.cpp
90 lines (69 loc) · 2.98 KB
/
keyfinder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include "keyfinder.h"
namespace KeyFinder{
KeyDetectionResult KeyFinder::findKey(const AudioData& originalAudio, const Parameters& params){
KeyDetectionResult result;
AudioData* workingAudio = new AudioData(originalAudio);
workingAudio->reduceToMono();
// TODO: there is presumably some good maths to determine filter frequencies
float lpfCutoff = params.getLastFrequency() * 1.05;
float dsCutoff = params.getLastFrequency() * 1.10;
unsigned int downsampleFactor = (int)floor( workingAudio->getFrameRate() / 2 / dsCutoff );
// get filter
LowPassFilter* lpf = lpfFactory.getLowPassFilter(160, workingAudio->getFrameRate(), lpfCutoff, 2048);
// feeding in the downsampleFactor for a shortcut
lpf->filter(workingAudio, downsampleFactor);
// note we don't delete the LPF; it's stored in the factory for reuse
Downsampler ds;
ds.downsample(workingAudio, downsampleFactor);
SpectrumAnalyser* sa = new SpectrumAnalyser(workingAudio->getFrameRate(), params, &ctFactory);
// run spectral analysis
Chromagram* ch = sa->chromagram(workingAudio);
delete workingAudio;
delete sa;
// reduce chromagram
ch->reduceTuningBins(params);
result.fullChromagram = Chromagram(*ch);
ch->reduceToOneOctave(params);
result.oneOctaveChromagram = Chromagram(*ch);
// get harmonic change signal
Segmentation* segmenter = Segmentation::getSegmentation(params);
result.harmonicChangeSignal = segmenter->getRateOfChange(*ch, params);
// get track segmentation
std::vector<unsigned int> segmentBoundaries = segmenter->getSegments(result.harmonicChangeSignal, params);
segmentBoundaries.push_back(ch->getHops()); // sentinel
delete segmenter;
// get key estimates for each segment
KeyClassifier hc(params);
std::vector<float> keyWeights(24); // TODO: not ideal using int cast of key_t enum. Hash?
for (int s = 0; s < (signed)segmentBoundaries.size()-1; s++){
KeyDetectionSegment segment;
segment.firstHop = segmentBoundaries[s];
segment.lastHop = segmentBoundaries[s+1] - 1;
// collapse segment's time dimension
std::vector<float> segmentChroma(ch->getBins());
for (unsigned int hop = segment.firstHop; hop <= segment.lastHop; hop++) {
for (unsigned int bin = 0; bin < ch->getBins(); bin++) {
float value = ch->getMagnitude(hop, bin);
segmentChroma[bin] += value;
segment.energy += value;
}
}
segment.key = hc.classify(segmentChroma);
if(segment.key != SILENCE){
keyWeights[segment.key] += segment.energy;
}
result.segments.push_back(segment);
}
delete ch;
// get global key
result.globalKeyEstimate = SILENCE;
float mostCommonKeyWeight = 0.0;
for (int k = 0; k < (signed)keyWeights.size(); k++){
if(keyWeights[k] > mostCommonKeyWeight){
mostCommonKeyWeight = keyWeights[k];
result.globalKeyEstimate = (key_t)k;
}
}
return result;
}
}