Chris@7
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@7
|
2
|
Chris@7
|
3 /*
|
Chris@7
|
4 Vamp feature extraction plugin for the BeatRoot beat tracker.
|
Chris@7
|
5
|
Chris@7
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@7
|
7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
|
Chris@7
|
8
|
Chris@7
|
9 This program is free software; you can redistribute it and/or
|
Chris@7
|
10 modify it under the terms of the GNU General Public License as
|
Chris@7
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@7
|
12 License, or (at your option) any later version. See the file
|
Chris@7
|
13 COPYING included with this distribution for more information.
|
Chris@7
|
14 */
|
Chris@7
|
15
|
Chris@9
|
16 #include "Induction.h"
|
Chris@9
|
17
|
Chris@7
|
18 double Induction::clusterWidth = 0.025;
|
Chris@7
|
19 double Induction::minIOI = 0.070;
|
Chris@7
|
20 double Induction::maxIOI = 2.500;
|
Chris@7
|
21 double Induction::minIBI = 0.3;
|
Chris@7
|
22 double Induction::maxIBI = 1.0;
|
Chris@7
|
23 int Induction::topN = 10;
|
Chris@7
|
24
|
Chris@15
|
25
|
Chris@23
|
26 AgentList Induction::beatInduction(AgentParameters params, EventList events) {
|
Chris@15
|
27 int i, j, b, bestCount;
|
Chris@15
|
28 bool submult;
|
Chris@15
|
29 int intervals = 0; // number of interval clusters
|
Chris@15
|
30 vector<int> bestn;// count of high-scoring clusters
|
Chris@15
|
31 bestn.resize(topN);
|
Chris@15
|
32
|
Chris@15
|
33 double ratio, err;
|
Chris@15
|
34 int degree;
|
Chris@15
|
35 int maxClusterCount = (int) ceil((maxIOI - minIOI) / clusterWidth);
|
Chris@15
|
36 vector<double> clusterMean;
|
Chris@15
|
37 clusterMean.resize(maxClusterCount);
|
Chris@15
|
38 vector<int> clusterSize;
|
Chris@15
|
39 clusterSize.resize(maxClusterCount);
|
Chris@15
|
40 vector<int> clusterScore;
|
Chris@15
|
41 clusterScore.resize(maxClusterCount);
|
Chris@15
|
42
|
Chris@15
|
43 EventList::iterator ptr1, ptr2;
|
Chris@15
|
44 Event e1, e2;
|
Chris@15
|
45 ptr1 = events.begin();
|
Chris@15
|
46 while (ptr1 != events.end()) {
|
Chris@15
|
47 e1 = *ptr1;
|
Chris@15
|
48 ++ptr1;
|
Chris@15
|
49 ptr2 = events.begin();
|
Chris@15
|
50 e2 = *ptr2;
|
Chris@15
|
51 ++ptr2;
|
Chris@15
|
52 while (e2 != e1 && ptr2 != events.end()) {
|
Chris@15
|
53 e2 = *ptr2;
|
Chris@15
|
54 ++ptr2;
|
Chris@15
|
55 }
|
Chris@15
|
56 while (ptr2 != events.end()) {
|
Chris@15
|
57 e2 = *ptr2;
|
Chris@15
|
58 ++ptr2;
|
Chris@15
|
59 double ioi = e2.time - e1.time;
|
Chris@15
|
60 if (ioi < minIOI) // skip short intervals
|
Chris@15
|
61 continue;
|
Chris@15
|
62 if (ioi > maxIOI) // ioi too long
|
Chris@15
|
63 break;
|
Chris@15
|
64 for (b = 0; b < intervals; b++) // assign to nearest cluster
|
Chris@15
|
65 if (fabs(clusterMean[b] - ioi) < clusterWidth) {
|
Chris@15
|
66 if ((b < intervals - 1) && (
|
Chris@15
|
67 fabs(clusterMean[b+1] - ioi) <
|
Chris@15
|
68 fabs(clusterMean[b] - ioi)))
|
Chris@15
|
69 b++; // next cluster is closer
|
Chris@15
|
70 clusterMean[b] = (clusterMean[b] * clusterSize[b] +ioi)/
|
Chris@15
|
71 (clusterSize[b] + 1);
|
Chris@15
|
72 clusterSize[b]++;
|
Chris@15
|
73 break;
|
Chris@15
|
74 }
|
Chris@15
|
75 if (b == intervals) { // no suitable cluster; create new one
|
Chris@15
|
76 if (intervals == maxClusterCount) {
|
Chris@15
|
77 // System.err.println("Warning: Too many clusters");
|
Chris@15
|
78 continue; // ignore this IOI
|
Chris@15
|
79 }
|
Chris@15
|
80 intervals++;
|
Chris@15
|
81 for ( ; (b>0) && (clusterMean[b-1] > ioi); b--) {
|
Chris@15
|
82 clusterMean[b] = clusterMean[b-1];
|
Chris@15
|
83 clusterSize[b] = clusterSize[b-1];
|
Chris@15
|
84 }
|
Chris@15
|
85 clusterMean[b] = ioi;
|
Chris@15
|
86 clusterSize[b] = 1;
|
Chris@15
|
87 }
|
Chris@15
|
88 }
|
Chris@15
|
89 }
|
Chris@15
|
90 for (b = 0; b < intervals; b++) // merge similar intervals
|
Chris@15
|
91 // TODO: they are now in order, so don't need the 2nd loop
|
Chris@15
|
92 // TODO: check BOTH sides before averaging or upper gps don't work
|
Chris@15
|
93 for (i = b+1; i < intervals; i++)
|
Chris@15
|
94 if (fabs(clusterMean[b] - clusterMean[i]) < clusterWidth) {
|
Chris@15
|
95 clusterMean[b] = (clusterMean[b] * clusterSize[b] +
|
Chris@15
|
96 clusterMean[i] * clusterSize[i]) /
|
Chris@15
|
97 (clusterSize[b] + clusterSize[i]);
|
Chris@15
|
98 clusterSize[b] = clusterSize[b] + clusterSize[i];
|
Chris@15
|
99 --intervals;
|
Chris@15
|
100 for (j = i+1; j <= intervals; j++) {
|
Chris@15
|
101 clusterMean[j-1] = clusterMean[j];
|
Chris@15
|
102 clusterSize[j-1] = clusterSize[j];
|
Chris@15
|
103 }
|
Chris@15
|
104 }
|
Chris@15
|
105 if (intervals == 0)
|
Chris@15
|
106 return AgentList();
|
Chris@15
|
107 for (b = 0; b < intervals; b++)
|
Chris@15
|
108 clusterScore[b] = 10 * clusterSize[b];
|
Chris@15
|
109 bestn[0] = 0;
|
Chris@15
|
110 bestCount = 1;
|
Chris@15
|
111 for (b = 0; b < intervals; b++)
|
Chris@15
|
112 for (i = 0; i <= bestCount; i++)
|
Chris@15
|
113 if ((i < topN) && ((i == bestCount) ||
|
Chris@15
|
114 (clusterScore[b] > clusterScore[bestn[i]]))){
|
Chris@15
|
115 if (bestCount < topN)
|
Chris@15
|
116 bestCount++;
|
Chris@15
|
117 for (j = bestCount - 1; j > i; j--)
|
Chris@15
|
118 bestn[j] = bestn[j-1];
|
Chris@15
|
119 bestn[i] = b;
|
Chris@15
|
120 break;
|
Chris@15
|
121 }
|
Chris@15
|
122 for (b = 0; b < intervals; b++) // score intervals
|
Chris@15
|
123 for (i = b+1; i < intervals; i++) {
|
Chris@15
|
124 ratio = clusterMean[b] / clusterMean[i];
|
Chris@15
|
125 submult = ratio < 1;
|
Chris@15
|
126 if (submult)
|
Chris@15
|
127 degree = (int) nearbyint(1/ratio);
|
Chris@15
|
128 else
|
Chris@15
|
129 degree = (int) nearbyint(ratio);
|
Chris@15
|
130 if ((degree >= 2) && (degree <= 8)) {
|
Chris@15
|
131 if (submult)
|
Chris@15
|
132 err = fabs(clusterMean[b]*degree - clusterMean[i]);
|
Chris@15
|
133 else
|
Chris@15
|
134 err = fabs(clusterMean[b] - clusterMean[i]*degree);
|
Chris@15
|
135 if (err < (submult? clusterWidth : clusterWidth * degree)) {
|
Chris@15
|
136 if (degree >= 5)
|
Chris@15
|
137 degree = 1;
|
Chris@15
|
138 else
|
Chris@15
|
139 degree = 6 - degree;
|
Chris@15
|
140 clusterScore[b] += degree * clusterSize[i];
|
Chris@15
|
141 clusterScore[i] += degree * clusterSize[b];
|
Chris@15
|
142 }
|
Chris@15
|
143 }
|
Chris@15
|
144 }
|
Chris@15
|
145
|
Chris@15
|
146 AgentList a;
|
Chris@15
|
147 for (int index = 0; index < bestCount; index++) {
|
Chris@15
|
148 b = bestn[index];
|
Chris@15
|
149 // Adjust it, using the size of super- and sub-intervals
|
Chris@15
|
150 double newSum = clusterMean[b] * clusterScore[b];
|
Chris@15
|
151 int newCount = clusterSize[b];
|
Chris@15
|
152 int newWeight = clusterScore[b];
|
Chris@15
|
153 for (i = 0; i < intervals; i++) {
|
Chris@15
|
154 if (i == b)
|
Chris@15
|
155 continue;
|
Chris@15
|
156 ratio = clusterMean[b] / clusterMean[i];
|
Chris@15
|
157 if (ratio < 1) {
|
Chris@15
|
158 degree = (int) nearbyint(1 / ratio);
|
Chris@15
|
159 if ((degree >= 2) && (degree <= 8)) {
|
Chris@15
|
160 err = fabs(clusterMean[b]*degree - clusterMean[i]);
|
Chris@15
|
161 if (err < clusterWidth) {
|
Chris@15
|
162 newSum += clusterMean[i] / degree * clusterScore[i];
|
Chris@15
|
163 newCount += clusterSize[i];
|
Chris@15
|
164 newWeight += clusterScore[i];
|
Chris@15
|
165 }
|
Chris@15
|
166 }
|
Chris@15
|
167 } else {
|
Chris@15
|
168 degree = (int) nearbyint(ratio);
|
Chris@15
|
169 if ((degree >= 2) && (degree <= 8)) {
|
Chris@15
|
170 err = fabs(clusterMean[b] - degree*clusterMean[i]);
|
Chris@15
|
171 if (err < clusterWidth * degree) {
|
Chris@15
|
172 newSum += clusterMean[i] * degree * clusterScore[i];
|
Chris@15
|
173 newCount += clusterSize[i];
|
Chris@15
|
174 newWeight += clusterScore[i];
|
Chris@15
|
175 }
|
Chris@15
|
176 }
|
Chris@15
|
177 }
|
Chris@15
|
178 }
|
Chris@15
|
179 double beat = newSum / newWeight;
|
Chris@15
|
180 // Scale within range ... hope the grouping isn't ternary :(
|
Chris@15
|
181 while (beat < minIBI) // Maximum speed
|
Chris@15
|
182 beat *= 2.0;
|
Chris@15
|
183 while (beat > maxIBI) // Minimum speed
|
Chris@15
|
184 beat /= 2.0;
|
Chris@15
|
185 if (beat >= minIBI) {
|
Chris@23
|
186 a.push_back(new Agent(params, beat));
|
Chris@15
|
187 }
|
Chris@15
|
188 }
|
Chris@15
|
189 #ifdef DEBUG_BEATROOT
|
Chris@15
|
190 std::cerr << "Induction complete, returning " << a.size() << " agent(s)" << std::endl;
|
Chris@15
|
191 #endif
|
Chris@15
|
192 return a;
|
Chris@15
|
193 } // beatInduction()
|
Chris@15
|
194
|