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
tuningcurvegraphdrawer.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 // Draw the tuning curve
22 //=============================================================================
23 
24 #include "tuningcurvegraphdrawer.h"
25 
26 #include <iostream>
27 
28 #include "../system/eptexception.h"
29 #include "../system/log.h"
30 #include "../messages/messagehandler.h"
31 #include "../messages/messageprojectfile.h"
32 #include "../messages/messagemodechanged.h"
33 #include "../messages/messagechangetuningcurve.h"
34 #include "../messages/messagekeyselectionchanged.h"
35 #include "../messages/messagekeydatachanged.h"
36 #include "../math/mathtools.h"
37 #include "../piano/piano.h"
38 #include "../core/system/log.h"
39 
40 
41 // Various pen types used by this drawer:
42 
53 
58 
60 
61 
62 //-----------------------------------------------------------------------------
63 // Constructor
64 //-----------------------------------------------------------------------------
65 
69 
71  : DrawerBase(graphics, 1.0),
72  mPiano(nullptr),
73  mConcertPitch(0),
74  mKeyNumberOfA4(0),
75  mNumberOfKeys(0),
76  mOperationMode(MODE_COUNT)
77 {
78 }
79 
80 
81 //-----------------------------------------------------------------------------
82 // Message dispatcher
83 //-----------------------------------------------------------------------------
84 
92 
94 {
95  EptAssert(m, "Message has to exist");
96 
97  switch (m->getType())
98  {
100  {
101  // get the piano data and its most important parameters:
102  auto mpf(std::static_pointer_cast<MessageProjectFile>(m));
103  mPiano = &mpf->getPiano();
107  redraw();
108  break;
109  }
111  {
112  // upate the marker positions upon key data change
113  auto mkdc(std::static_pointer_cast<MessageKeyDataChanged>(m));
114  int index = mkdc->getIndex();
120  break;
121  }
123  {
124  redraw(true);
125  break;
126  }
128  {
129  auto mmc(std::static_pointer_cast<MessageModeChanged>(m));
130  mOperationMode = mmc->getMode();
131  redraw(true);
132  break;
133  }
134  default:
135  break;
136  }
137 }
138 
139 
140 //-----------------------------------------------------------------------------
141 // Convert cents to Y-coordinate
142 //-----------------------------------------------------------------------------
143 
154 
156 {
157  if (cents>rangeInCents) return 0;
158  else if (cents<-rangeInCents) return 1;
159  else if (mOperationMode == MODE_TUNING)
160  return 0.5 - 0.5 / atan(3.1) * atan(3*cents/rangeInCents);
161  else return 0.5 - 0.5*cents/rangeInCents;
162 }
163 
164 //-----------------------------------------------------------------------------
165 // Manually edit tuning curve by mouse click
166 //-----------------------------------------------------------------------------
167 
178 
180 {
181  if (relX < 0 or relX > 1 or relY < 0 or relY > 1) return; // out of range
182 
183  int keynumber = (int)(mNumberOfKeys * relX);
184  if (keynumber < 0 or keynumber >= mNumberOfKeys) return;
185 
186  if (keynumber == mKeyNumberOfA4)
187  {
188  LogI("The pitch of A4 cannot be changed manually.");
189  return;
190  }
191  double yfrac=1 - relY;
192  int cents = MathTools::roundToInteger(rangeInCents * (2 * yfrac - 1));
193  LogW("Set tuningcurve manually keynumber=%d, cents=%d.",keynumber,cents);
194  MessageHandler::send<MessageChangeTuningCurve>
195  (keynumber,mPiano->getDefiningTempFrequency(keynumber,cents,440));
196 }
197 
198 
199 //-----------------------------------------------------------------------------
200 // Draw a grid in the background
201 //-----------------------------------------------------------------------------
202 
210 
212 {
213  // Draw a horizontal background line for each 10 cents
214  for (int cents=-rangeInCents+1; cents<rangeInCents; cents++)
215  if (cents%10==0 or (abs(cents)<10 and mOperationMode==MODE_TUNING))
216  {
217  double y = convertCentsToY(cents);
218  auto type = (cents ? centlines : middleline);
219  mGraphics->drawLine(0, y, 1, y, type)->setItemRole(ROLE_GRID);
220  }
221  // Draw vertical lines according to the keys
222  const double dx = 1.0 / mNumberOfKeys;
223  for (int i = 0; i <= mNumberOfKeys; i++)
224  mGraphics->drawLine(i*dx, 0,i*dx, 1, gridcolor)->setItemRole(ROLE_GRID);
225 
226  // Draw green background in tuning mode
227  if (mOperationMode == MODE_TUNING) {
228  const double allowedDeviationInCents = 3;
229  double allowedStartCents = convertCentsToY(allowedDeviationInCents);
230  GraphicsItem *item = mGraphics->drawFilledRect(0, allowedStartCents, 1,
231  convertCentsToY(-allowedDeviationInCents) - allowedStartCents,
233  item->setItemRole(ROLE_GRID);
234  item->setZOrder(-1);
235  }
236 }
237 
238 
239 //-----------------------------------------------------------------------------
240 // Draw the markers of the tuning curve
241 //-----------------------------------------------------------------------------
242 
247 
249 {
250  if (!mPiano) return; // no keys set yet (see setKeys)
251 
252  // Draw inharmonicity levels
254 
255  // Draw tuning curve
257 
258  // Draw recorded frequency levels
260 
261  // Draw tuned frequency levels
263 
264  // Draw tuned frequency levels
265  updateMarkerPosition (keynumber, ROLE_OVERPULL);
266 }
267 
268 
269 //-----------------------------------------------------------------------------
270 // Main drawing function
271 //-----------------------------------------------------------------------------
272 
279 
281 {
282  drawGrid();
283  for (int i = 0; i < mNumberOfKeys; i++) drawMarkers(i);
284 }
285 
286 
287 //-----------------------------------------------------------------------------
288 // Compute the position of a marker
289 //-----------------------------------------------------------------------------
290 
303 
305 {
306  // Get the key data of the piano
307  if (not mPiano) return -1;
308  const Key &key = mPiano->getKey(keyindex);
309 
310  // lambda function converting a frequency ratio to cents
311  auto ratioToCents = [this](double ratio)
312  { return 1200*log(ratio)/MathTools::LOG2; };
313 
314  if (role & ROLE_INHARMONICITY)
315  {
317  {
318  double B = key.getMeasuredInharmonicity();
319  if (B > 0)return (log(B)+10.5)/8;
320  }
321  }
322  else if (role & ROLE_COMPUTED_FREQUENCY)
323  {
324  if (mOperationMode == MODE_TUNING) return 0.5; // horizontal line
325  else if (mOperationMode == MODE_CALCULATION)
326  {
327  double f = mPiano->getDefiningTempFrequency(keyindex,0,440);
328  return convertCentsToY(ratioToCents(key.getComputedFrequency() / f));
329  }
330  }
331  else if (role & ROLE_RECORDED_FREQUENCY)
332  {
334  {
335  double ratio = key.getRecordedFrequency() /
336  mPiano->getEqualTempFrequency(keyindex);
337  if (ratio > 0) return convertCentsToY(ratioToCents(ratio));
338  }
339  else if (mOperationMode == MODE_CALCULATION)
340  {
341  int keyA4 = mPiano->getKeyboard().getKeyNumberOfA4();
342  double fA4 = mPiano->getKey(keyA4).getRecordedFrequency();
343  double ratio = key.getRecordedFrequency() /
344  mPiano->getEqualTempFrequency(keyindex,0,fA4);
345  if (ratio > 0) return convertCentsToY(ratioToCents(ratio));
346  }
347  }
348  else if (role & ROLE_TUNED_FREQUENCY)
349  {
351  {
352  double ratio = key.getTunedFrequency() /
353  key.getComputedFrequency() * 440.0 / mConcertPitch;
354  if (ratio > 0) return convertCentsToY(ratioToCents(ratio));
355  }
356  else if (mOperationMode == MODE_IDLE)
357  {
358  double ratio = key.getTunedFrequency() /
359  mPiano->getEqualTempFrequency(keyindex);
360  if (ratio > 0) return convertCentsToY(ratioToCents(ratio));
361  }
362  }
363  else if (role & ROLE_OVERPULL)
364  {
366  {
367  double overpull = key.getOverpull();
368  if (fabs(overpull)>0.1) return convertCentsToY(overpull);
369  }
370  }
371  return -1;
372 }
373 
374 
375 //-----------------------------------------------------------------------------
376 // Get the pen type of a marker with a given role
377 //-----------------------------------------------------------------------------
378 
388 
390 {
391  if (role & ROLE_INHARMONICITY) return bmarkers;
392  else if (role & ROLE_COMPUTED_FREQUENCY) return fcmarkers;
393  else if (role & ROLE_TUNED_FREQUENCY) return ftmarkers;
394  else if (role & ROLE_OVERPULL) return opmarkers;
395  else if (role & ROLE_RECORDED_FREQUENCY)
397  else EPT_EXCEPT(EptException::ERR_NOT_IMPLEMENTED, "Color type not implemented");
398 }
399 
400 
401 //-----------------------------------------------------------------------------
402 // Update the position of a marker
403 //-----------------------------------------------------------------------------
404 
406 {
407  // Compute the y coordinate where the marker should be
408  double y = getMarkerPosition(keyindex, role);
409 
410  // See whether the marker already exists
411  GraphicsItem *item = mGraphics->getGraphicItem(keyindex, role);
412  if (item)
413  {
414  // if y-range valid upate position, otherwise delete it
415  if (y > 0 and y < 1)
416  item->setPosition(static_cast<double>(keyindex) / mNumberOfKeys, y);
417  else delete item;
418  }
419  else // if the marker does not exist
420  {
421  // compute x-coordinate (y-coordinate already exists)
422  double x = (double)(keyindex) / mNumberOfKeys;
423  double dx = 1.0 / mNumberOfKeys;
424  if (y > 0 && y < 1)
425  {
426  item = mGraphics->drawLine(x, y, x + dx, y, getMarkerPenType(role));
427  item->setKeyIndexAndItemRole(keyindex, role);
428  }
429  }
430 }
431 
432 
433 
double getRecordedFrequency() const
Get recorded frequency.
Definition: key.cpp:119
GraphicsItem * getGraphicItem(int keyIndex, RoleType role)
Get a single graphics element specified by its index and its role.
int mKeyNumberOfA4
Index of A4 (reference key)
std::shared_ptr< Message > MessagePtr
Global type of a shared message pointer.
Definition: message.h:98
const double LOG2
Definition: mathtools.h:39
called when the recording was cleared
Definition: message.h:50
GraphicsViewAdapter * mGraphics
Pointer to the graphics view adapter.
Definition: drawerbase.h:53
void setItemRole(RoleType role)
Setter function for mRole.
Definition: graphicsitem.h:104
data of a key changed
Definition: message.h:62
#define LogW(...)
Definition: log.h:56
double getDefiningTempFrequency(int keynumber, double cents=0, double A4=0) const
Compute the defining temperatent.
Definition: piano.cpp:148
int mNumberOfKeys
Total number of keys (88)
Message that a change was made with the current project file.
Definition: message.h:68
virtual void setPosition(double x, double y)=0
Function to set the position of the element.
virtual void setZOrder(double z)
Set the z order of the element (here implemented without function)
Definition: graphicsitem.h:138
double getMarkerPosition(int keyindex, RoleType role)
Compute the position of a marker.
Class describing a single piano key.
Definition: key.h:45
static const PenType fcmarkers
Pen type for computed frequency markers.
GraphicsItem::RoleType RoleType
Message that the operation mode has changed.
Definition: message.h:65
OperationMode mOperationMode
Current operation mode.
int roundToInteger(T x)
Round a floating point number to an integer.
Definition: mathtools.h:43
Mode where the entropy optimization is carried out.
Definition: prerequisites.h:70
virtual void draw() final
Main drawing function.
Mode for recording the piano keys.
Definition: prerequisites.h:69
TuningCurveGraphDrawer(GraphicsViewAdapter *graphics)
Constructor, calls the DrawerBase constructor and resets variables.
The count of modes.
Definition: prerequisites.h:73
static const PenType bmarkers
Pen type for inharmonicity markers.
void redraw(bool force=false)
Function to completely redraw the scene.
Definition: drawerbase.cpp:57
#define EPT_EXCEPT(num, desc)
Definition: eptexception.h:119
const Piano * mPiano
Pointer to the actual piano.
FillTypes
Available filling colors (e.g. to fill rectangles)
const Keyboard & getKeyboard() const
Definition: piano.h:83
virtual GraphicsItem * drawFilledRect(double x, double y, double w, double h, PenType pen=PEN_THIN_BLACK, FillTypes fill=FILL_RED)=0
Abstract function: Draw a filled rectangle.
double getComputedFrequency() const
Get computed frequency.
Definition: key.cpp:174
static const FillType allowdAreaFill
Filling for the allowed tuning area.
double getMeasuredInharmonicity() const
Get estimated inharmonicity.
Definition: key.cpp:141
static const PenType gridcolor
Pen type for background grid pentype.
static const PenType middleline
Pen type for bold middle line of grid.
static const PenType fgmarkers
Pen type for recorded frequency in background.
#define LogI(...)
Definition: log.h:50
Inharmonicity marker (shown upside down)
static const PenType opmarkers
Pen type for tuned frequency markers.
static const PenType frmarkers
Pen type for recorded frequency markers.
Mode for manually tuning the piano.
Definition: prerequisites.h:71
PenType
Available pen types for drawing.
int getKeyNumberOfA4() const
Definition: keyboard.h:73
Marker indicating recorded frequency.
Abstract base class for implementations rendering graphics.
double convertCentsToY(double cents)
Convert cents to y-coordinate between 0 and 1.
const double & getConcertPitch() const
Definition: piano.h:80
static const PenType ftmarkers
Pen type for tuned frequency markers.
#define EptAssert(a, b)
Definition: eptexception.h:47
Class for a single item in a graphics view.
Definition: graphicsitem.h:55
virtual GraphicsItem * drawLine(double x1, double y1, double x2, double y2, PenType pen=PEN_THIN_BLACK)=0
Abstract function: Draw a line.
virtual void handleMessage(MessagePtr m) override
Message listener and dispatcher.
double mConcertPitch
Chosen concert pitch in Hz.
static const int rangeInCents
Constant, defining the visible range above and below the equal temperament level in cents...
void updateMarkerPosition(int keyindex, RoleType role)
void setKeyIndexAndItemRole(int index, RoleType role)
Short function for setting the key index and the role.
void manuallyEditTuningCurveByClick(double relX, double relY)
Manually edit tuning curve by mouse click.
Marker indicating computed frequency.
void drawMarkers(int8_t key)
Draw the tuning markers in the tuning curve panel.
Do nothing.
Definition: prerequisites.h:68
void drawGrid()
Draw the background grid in the tuning curve panel.
int getNumberOfKeys() const
Definition: keyboard.h:72
double getTunedFrequency() const
Get tuned frequency.
Definition: key.cpp:193
static const PenType centlines
Pen type for thin grid indicating cents.
double getOverpull() const
Get overpull in cents.
Definition: key.cpp:211
Abstract base class for drawing 2d graphics.
Definition: drawerbase.h:40
PenType getMarkerPenType(RoleType role)
Get the pen type of a marker with a given role.