Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
overpull.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // Overpull estimator
22 //=============================================================================
23 
24 #include "overpull.h"
25 
26 #include <cmath>
27 #include <iostream>
28 
29 #include "../system/log.h"
30 #include "../math/mathtools.h"
31 
32 //-----------------------------------------------------------------------------
33 // Constructor
34 //-----------------------------------------------------------------------------
35 
39 
41  mPianoType(piano::PT_COUNT),
42  mNumberOfKeys(0),
43  mNumberOfBassKeys(0),
44  mConcertPitch(0)
45 {
46 }
47 
48 
49 //-----------------------------------------------------------------------------
50 // Initialize interaction matrix
51 //-----------------------------------------------------------------------------
52 
60 
62 {
63  if (not piano) return;
64  mNumberOfKeys = piano->getKeyboard().getNumberOfKeys();
65  mNumberOfBassKeys = piano->getKeyboard().getNumberOfBassKeys();
66  mConcertPitch = piano->getConcertPitch();
67  mPianoType = piano->getPianoType();
69 }
70 
71 
72 //-----------------------------------------------------------------------------
73 // Overpull interaction matrix
74 //-----------------------------------------------------------------------------
75 
89 
91 {
92  int K = mNumberOfKeys;
93  int B = mNumberOfBassKeys;
94  if (K<=0 or B<=0 or B>K) return;
95 
96  LogI("Compute overpull interaction matrix");
97 
98  // resize and reset the interaction matrix:
99  R.resize(K);
100  for (auto &row : R) row.resize(K);
101  for (auto &row : R) row.assign(K,0);
102 
103  double DL=0,DR=0,SL=0,SR=0,SB=0,SN=0,shift=0;
104 
105  switch(mPianoType)
106  {
107  case piano::PT_GRAND:
108  SL = 1200; // speaking length treble string left
109  SR = 50; // speaking length treble string right
110  SB = 1250; // average speaking length bass string
111  SN = 100; // average non-speaking length everywhere
112  DL = 500; // distance from bridge to frame left
113  DR = 30; // distance from bridge to frame right
114  shift = 3; // shift between bass and treble
115  break;
116  case piano::PT_UPRIGHT:
117  SL = 1200; // speaking length treble string left
118  SR = 50; // speaking length treble string right
119  SB = 1200; // average speaking length bass string
120  SN = 100; // average non-speaking length everywhere
121  DL = 450; // distance from bridge to frame left
122  DR = 50; // distance from bridge to frame right
123  shift = 10; // shift between bass and treble
124  break;
125  default:
126  LogW("Undefined piano type encountered");
127  break;
128  }
129 
130  // COMPUTE APPROXIMATE SPEAKING LENGTH OF THE TREBLE STRINGS
131  double G =-(pow(2,-0.08333333333333333)*
132  (-(SL*pow(2,0.08333333333333333 + B/12.)) +
133  K*SL*pow(2,0.08333333333333333 + B/12.) - B*SR*pow(2,K/12.))*
134  pow(1 + B - K,-1));
135  double H =((SL*pow(2,1 + B/12.) - SR*pow(2,0.9166666666666666 + K/12.))*
136  pow(1 + B - K,-1))/2.;
137  auto stringlength = [G,H] (int k) { return (G+H*k)*pow(2,-k/12.0); };
138 
139  // COMPUTE APPROXIMATE ARC LENGTH ALONG THE TREBLE BRIDGE
140  std::vector<double> arc(K,0);
141  const double d = 13.6; // Unison distance in mm
142  arc[B]=DL;
143  for (int k=B+1; k<K; ++k) arc[k] = arc[k-1] +
144  sqrt(d*d + pow(stringlength(k)-stringlength(k-1),2));
145  double L = arc[K-1]+DR;
146 
147  // DEFLECTION FORMULA FOR A BAR
148  auto def = [L,arc] (int j, int k)
149  {
150  double a = arc[k];
151  double b = L-a;
152  double x = arc[j];
153  return b/L*x*(L*L-x*x-b*b) ;
154  };
155 
156  // COMPUTE SYMMETRIC DEFLECTION RESPONSE
157  auto deflection = [def] (int j, int k)
158  {
159  if (j<=k) return def(j,k);
160  else return def(k,j);
161  };
162 
163  // COPY BEHAVIOR INTO THE BASS SECTION
164  auto epsilon = [deflection,B,shift] (int j, int k)
165  {
166  if (j<B and k<B) return deflection(j+B+shift,k+B+shift);
167  else if (j<B and k>=B) return deflection(j+B+shift,k);
168  else if (j>=B and k<B) return deflection(j,k+B+shift);
169  else return deflection(j,k);
170  };
171 
172  // TAKE NUMBER OF STRINGS AND THEIR LENGTH INTO ACCOUNT
173  auto response = [B,SB,SN,stringlength,epsilon] (int j, int k)
174  {
175  double unison=3;
176  if (k<8) unison=1; else if (k<B) unison=2;
177  double stringlen = (j<B ? SB+SN : stringlength(j)+SN);
178  return unison * epsilon(j,k) / stringlen;
179  };
180 
181  // NORMALIZATION
182  double sum = 0;
183  double p = 0.8; // weight of sound board deformation 80 %
184  for (int j=0; j<K; ++j) for (int k=0; k<K; ++k) sum += (R[j][k] = response(j,k));
185  sum /= K;
186  for (int j=0; j<K; ++j) for (int k=0; k<K; ++k)
187  R[j][k] *= p * averagePull / sum;
188 
189 
190  // BRIDGE TILT
191  const double sigma = 20;
192  const double amplitude = averagePull * (1-p);
193  double avstring = B/SB;
194  for (int k=B; k<K; k++) avstring += 1.0/stringlength(k);
195  avstring /= K;
196  double prefactor = amplitude / sqrt(2*3.141) / sigma / avstring / 0.7;
197  for (int j=0; j<B; ++j) for (int k=0; k<B; ++k) R[j][k] += prefactor*exp(-0.5*(j-k)*(j-k)/sigma/sigma)/SB;
198  for (int j=B; j<K; ++j) for (int k=B; k<K; ++k) R[j][k] += prefactor*exp(-0.5*(j-k)*(j-k)/sigma/sigma)/stringlength(j);
199 
200 // // Compute average (only for testing)
201 // sum=0;
202 // for (int j=0; j<K; ++j) for (int k=0; k<K; ++k) sum += R[j][k];
203 // std::cout << "************************" << sum/K << "******************************" << std::endl;
204 
205 // // Write data (only for testing purposes)
206 // std::ofstream os("/home/hinrichsen/mat.m");
207 // os << "M={";
208 // for (int j=0; j<K; ++j)
209 // {
210 // os << "{";
211 // for (int k=0; k<K-1; ++k) os << R[j][k] << ",";
212 // os << R[j][K-1];
213 // if (j!=K-1) os << "},\n";
214 // else os << "}};" << std::endl;
215 // }
216 // os.close();
217 
218 // os.open("/home/hinrichsen/response.dat");
219 // for (int j=0; j<K; ++j)
220 // {
221 // double sum=0;
222 // for (int k=0; k<K; ++k) sum += R[j][k];
223 // os << sum << std::endl;
224 // }
225 // os.close();
226 
227 }
228 
229 
230 //-----------------------------------------------------------------------------
231 // Compute overpull
232 //-----------------------------------------------------------------------------
233 
244 
245 double OverpullEstimator::getOverpull (int keynumber, const Piano *piano)
246 {
247  // CHECK PARAMETERS FOR CONSISTENCY, OTHERWISE RETURN 0
248  if (not piano) return 0;
249  int K = piano->getKeyboard().getNumberOfKeys();
250  int B = piano->getKeyboard().getNumberOfBassKeys();
251  double cpratio = piano->getConcertPitch() / 440.0;
252  if (K<=0 or B<=0 or keynumber<0 or keynumber>=K) return 0;
253 
254  // IF KEYBOARD PARAMETERS OR PITCH HAS CHANGED RE-INITIALIZE AGAIN
255  if (K != mNumberOfKeys or B != mNumberOfBassKeys
256  or piano->getConcertPitch() != mConcertPitch
257  or piano->getPianoType() != mPianoType) init(piano);
258 
259  //
260  std::vector<double> weightedRedMarkers (K,0);
261  const int maxGapsize = 7;
262  int gapsize = 0;
263  int lastkey = 0;
264  double lastchi = 0;
265  for (int k=0; k<K; ++k)
266  {
267  double computed = piano->getKey(k).getComputedFrequency();
268  double tuned = piano->getKey(k).getTunedFrequency();
269  if (computed>20 and computed<20000 and tuned>20 and tuned<20000 and cpratio>0)
270  {
271  double chi = 1200.0*log(tuned/computed/cpratio)/MathTools::LOG2;
272  weightedRedMarkers[k] = chi*(gapsize+1);
273  lastkey = k; lastchi = chi;
274  gapsize=0;
275  }
276  else gapsize++;
277  if (gapsize > maxGapsize) return 0;
278  if (k==K-1 and gapsize>0)
279  weightedRedMarkers[lastkey] += lastchi*gapsize;
280  }
281 
282  // COMPUTE CURRENTLY NEEDED OVERPULL
283  double overpull = 0, totaldeviation = 0;
284  for (int k=0; k<K; k++) if (weightedRedMarkers[k])
285  {
286  totaldeviation += weightedRedMarkers[k];
287  overpull -= weightedRedMarkers[k] * R[keynumber][k];
288  }
289 
290 
291  // THE OVERPULL IS ONLY SHOWN IF THE PIANO IS ON AVERAGE MORE THAN 5 CENTS FLAT
292  if (fabs(totaldeviation/K)>5) return overpull;
293  else return 0;
294 }
const double LOG2
Definition: mathtools.h:39
void init(const Piano *piano)
Initialize.
Definition: overpull.cpp:61
int mNumberOfKeys
Total number of keys.
Definition: overpull.h:63
#define LogW(...)
Definition: log.h:56
double mConcertPitch
Concert pitch (A4)
Definition: overpull.h:65
Definition: piano.h:40
The piano is a upgright piano.
Definition: pianodefines.h:31
#define LogI(...)
Definition: log.h:50
OverpullEstimator()
Constructor, resetting the member variables.
Definition: overpull.cpp:40
piano::PianoType mPianoType
Piano type (upright/grand)
Definition: overpull.h:62
double getOverpull(int keynumber, const Piano *piano)
Compute the required overpull on the basis of the interaction matrix.
Definition: overpull.cpp:245
void computeInteractionMatrix(double averagePull=0.22)
Compute the interaction matrix between the string.
Definition: overpull.cpp:90
std::vector< std::vector< float > > R
Response matrix.
Definition: overpull.h:66
The piano is a grand piano.
Definition: pianodefines.h:30
int mNumberOfBassKeys
Keys on the bass bridge.
Definition: overpull.h:64