28 #include <QGenericMatrix> 71 const QVector<double> pitches,
72 const int referenceKey,
73 const int wolfsIntervalShift)
75 for (
KeyData &key : keys)
if (key.intensity > 0)
77 int distance = (key.key + 240 - referenceKey + wolfsIntervalShift)%12;
78 int A4dist = (69 + 240 - referenceKey + wolfsIntervalShift)%12;
79 key.pitch = pitches[distance]-pitches[A4dist];
110 const QVector<double> intervals,
111 const QVector<double> weights,
114 using namespace Eigen;
122 QMultiMap<double,int> audibleKeys, memorizedKeys;
123 for (
auto &key : keyDataVector)
125 if (key.intensity>0) audibleKeys.insert (-key.intensity, key.key);
126 if (key.memory>0) memorizedKeys.insert(-key.memory, key.key);
134 const int Pmax=8, Nmax=16;
135 QVector<int> keys = audibleKeys.values().mid(0,Pmax).toVector();
136 const int P = keys.size();
137 keys += memorizedKeys.values().mid(0,Nmax-P).toVector();
138 const int N = keys.size();
145 VectorXd pitch(N), significance(N);
146 bool newKeys =
false;
147 for (
int i=0; i<N; ++i)
149 KeyData& key = keyDataVector[keys[i]];
150 pitch(i) = key.
pitch;
156 MatrixXi variants(N,N), semitones(N,N), direction(N,N);
157 MatrixXd weight(N,N); weight.setZero();
158 for (
int i=0; i<N; ++i)
for (
int j=0; j<N; ++j)
if (i<P or j<P)
160 semitones(i,j) = abs(keys[j] - keys[i]);
161 direction(i,j) = (keys[j] >= keys[i] ? 1:-1);
165 weight(i,j) = weights[semitones(i,j)%12] *
167 sqrt(significance(i) * significance(j));
168 if (i>P or j>P)
if (keys[i]!=keys[j]) weight(i,j) *=
memoryWeight;
173 VectorXd diagonal = VectorXd(weight.array().rowwise().sum());
174 MatrixXd A = -weight.block(0,0,P,P) + MatrixXd::Identity(P,P)*
epsilon 175 + MatrixXd(diagonal.head(P).asDiagonal());
176 MatrixXd AI = A.inverse();
179 MatrixXd interval(N,N); interval.setZero();
181 if (optimizedJI and newKeys)
188 MatrixXi m(N,N); m.setZero();
189 for (
int i=0; i<N; i++)
for (
int j=0; j<N; j++)
if (i<P or j<P)
191 if (keyDataVector[keys[i]].newlyPressed or
192 keyDataVector[keys[j]].newlyPressed) m(j,i)=m(i,j)=0;
195 bool searching =
true;
198 QTimer timer; timer.start(20);
200 while (searching and timer.remainingTime()>0)
202 MatrixXd optimalInterval(N,N); optimalInterval.setZero();
203 for (
int i=0; i<N; ++i)
for (
int j=0; j<N; ++j)
if (i<P or j<P)
205 optimalInterval(i,j) = direction(i,j) *
207 if (j>=P) optimalInterval(i,j) -= pitch(j);
208 if (i>=P) optimalInterval(i,j) += pitch(i);
210 VectorXd B = (optimalInterval * weight).diagonal().head(P)
212 VectorXd V = -AI * B;
213 double C = (optimalInterval.array() * optimalInterval.array()
214 * weight.array()).sum() / 4;
215 double P = V.dot(A*V)/2 + B.dot(V) + C;
216 if (P<Pmin-1E-7) { Pmin=P; mmin=m; }
220 for (
int i=0; i<N; ++i)
for (
int j=0; j<N; ++j)
if (i<P or j<P)
if (i<j)
221 if (keyDataVector[keys[i]].newlyPressed
222 or keyDataVector[keys[j]].newlyPressed)
223 if (variants(i,j)>1 and not searching)
225 m(j,i) = m(i,j) = (m(i,j)+1) % variants(i,j);
226 if (m(i,j)>0) searching =
true;
231 for (
int i=0; i<N; i++)
for (
int j=0; j<N; j++)
if (i<P or j<P)
234 interval(i,j) = direction(i,j) *
245 for (
int i=0; i<N; ++i)
for (
int j=0; j<N; ++j)
if (i<P or j<P)
246 interval(i,j) = direction(i,j) * intervals[semitones(i,j)%12];
250 for (
int i=0; i<P; ++i)
for (
int j=P; j<N; ++j)
252 interval(i,j) -= pitch(j);
253 interval(j,i) += pitch(j);
257 VectorXd B = (interval * weight).diagonal().head(P) -
epsilon*pitch.head(P);
258 VectorXd U = - AI * B;
259 for (
int i=0; i<P; i++) keyDataVector[keys[i]].pitch = U(i);
262 for (
int i=0; i<P; ++i) keyDataVector[keys[i]].newlyPressed =
false;
265 double C = (interval.array() * interval.array() * weight.array()).sum() / 4;
266 return C - B.dot(AI*B)/2;
const double octaveWeightFactor
void tuneStatically(KeyDataVector &keys, const QVector< double > pitches, const int referenceKey, const int wolfsIntervalShift)
Tune statically in a given unequal temperament (UT)
QVector< KeyData > KeyDataVector
Data of all keys of the keyboard.
double mProgression
Actual pitch progression.
Structure holding the tuner's data of a single key.
bool newlyPressed
Flag indicating a newly pressed key.
TunerAlgorithm()
Constructor of the TunerAlgorithm.
double memory
Psychoacoustic memory M(t)
const double memoryWeight
const QVector< QVector< double > > cJustIntonation
major seventh 15/8
Eigen::MatrixXi mSelectedVariant
Previously used pitch variant.
double intensity
Intensity (volume) I(t)
double pitch
Actual pitch.
double tuneDynamically(KeyDataVector &keyData, const QVector< double > intervals, const QVector< double > weights, bool optimizedJI)
Main Tuninig Procedure: Tune dynamically.