1 /*
2     Virtual Piano Widget for Qt5
3     Copyright (C) 2008-2021, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License along
16     with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <QApplication>
20 #include <QDataStream>
21 #include <QByteArray>
22 #include <QGraphicsSceneMouseEvent>
23 #include <QKeyEvent>
24 #include <QPalette>
25 #include <QPixmap>
26 #include <QtMath>
27 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
28 #include <QTouchDevice>
29 #else
30 #include <QInputDevice>
31 #endif
32 #include <drumstick/pianokeybd.h>
33 #include "pianoscene.h"
34 
35 /**
36  * @file pianoscene.cpp
37  * Implementation of the Piano Scene
38  */
39 
40 /**
41  * @class QGraphicsScene
42  * The QGraphicsScene class provides a surface for managing a large number of 2D graphical items.
43  * @see https://doc.qt.io/qt-5/qgraphicsscene.html
44  */
45 
46 namespace drumstick { namespace widgets {
47 
48 class PianoScene::PianoScenePrivate
49 {
50 public:
PianoScenePrivate(const int baseOctave,const int numKeys,const int startKey)51     PianoScenePrivate ( const int baseOctave,
52                         const int numKeys,
53                         const int startKey ):
54         m_baseOctave( baseOctave ),
55         m_numKeys( numKeys ),
56         m_startKey( startKey ),
57         m_minNote( 0 ),
58         m_maxNote( 127 ),
59         m_transpose( 0 ),
60         m_showLabels( ShowNever ),
61         m_alterations( ShowSharps ),
62         m_octave( OctaveC4 ),
63         m_orientation( HorizontalOrientation ),
64         m_rawkbd( false ),
65         m_keyboardEnabled( true ),
66         m_mouseEnabled( true ),
67         m_touchEnabled( true ),
68         m_mousePressed( false ),
69         m_velocity( 100 ),
70         m_channel( 0 ),
71         m_velocityTint( true ),
72         m_handler( nullptr ),
73         m_keybdMap( nullptr ),
74         m_showColorScale( false ),
75         m_hilightPalette(PianoPalette(PAL_SINGLE)),
76         m_backgroundPalette(PianoPalette(PAL_KEYS)),
77         m_foregroundPalette(PianoPalette(PAL_FONT)),
78         m_useKeyPix( true )
79     { }
80 
saveData(QByteArray & buffer)81     void saveData(QByteArray& buffer)
82     {
83         QDataStream ds(&buffer, QIODevice::WriteOnly);
84         ds << m_minNote;
85         ds << m_maxNote;
86         ds << m_transpose;
87         ds << m_showLabels;
88         ds << m_alterations;
89         ds << m_octave;
90         ds << m_orientation;
91         ds << m_rawkbd;
92         ds << m_keyboardEnabled;
93         ds << m_mouseEnabled;
94         ds << m_touchEnabled;
95         ds << m_mousePressed;
96         ds << m_velocity;
97         ds << m_channel;
98         ds << m_velocityTint;
99         ds << m_noteNames;
100         ds << m_names_s;
101         ds << m_names_f;
102         ds << m_showColorScale;
103         ds << m_hilightPalette;
104         ds << m_backgroundPalette;
105         ds << m_foregroundPalette;
106         ds << m_useKeyPix;
107         ds << m_keyPix[0];
108         ds << m_keyPix[1];
109     }
110 
loadData(QByteArray & buffer)111     void loadData(QByteArray& buffer)
112     {
113         quint32 u;
114         QDataStream ds(&buffer, QIODevice::ReadOnly);
115         ds >> m_minNote;
116         ds >> m_maxNote;
117         ds >> m_transpose;
118         ds >> u; m_showLabels = LabelVisibility(u);
119         ds >> u; m_alterations = LabelAlteration(u);
120         ds >> u; m_octave = LabelCentralOctave(u);
121         ds >> u; m_orientation =  LabelOrientation(u);
122         ds >> m_rawkbd;
123         ds >> m_keyboardEnabled;
124         ds >> m_mouseEnabled;
125         ds >> m_touchEnabled;
126         ds >> m_mousePressed;
127         ds >> m_velocity;
128         ds >> m_channel;
129         ds >> m_velocityTint;
130         ds >> m_noteNames;
131         ds >> m_names_s;
132         ds >> m_names_f;
133         ds >> m_showColorScale;
134         ds >> m_hilightPalette;
135         ds >> m_backgroundPalette;
136         ds >> m_foregroundPalette;
137         ds >> m_useKeyPix;
138         ds >> m_keyPix[0];
139         ds >> m_keyPix[1];
140     }
141 
142     int m_baseOctave;
143     int m_numKeys;
144     int m_startKey;
145     int m_minNote;
146     int m_maxNote;
147     int m_transpose;
148     LabelVisibility m_showLabels;
149     LabelAlteration m_alterations;
150     LabelCentralOctave m_octave;
151     LabelOrientation m_orientation;
152     bool m_rawkbd;
153     bool m_keyboardEnabled;
154     bool m_mouseEnabled;
155     bool m_touchEnabled;
156     bool m_mousePressed;
157     int m_velocity;
158     int m_channel;
159     bool m_velocityTint;
160     PianoHandler *m_handler;
161     KeyboardMap *m_keybdMap;
162     QHash<int, PianoKey *> m_keys;
163     QMap<int, KeyLabel *> m_labels;
164     QStringList m_noteNames;
165     QStringList m_names_s;
166     QStringList m_names_f;
167     bool m_showColorScale;
168     PianoPalette m_hilightPalette;
169     PianoPalette m_backgroundPalette;
170     PianoPalette m_foregroundPalette;
171     bool m_useKeyPix;
172     QPixmap m_keyPix[2];
173 };
174 
175 const int KEYWIDTH = 180;
176 const int KEYHEIGHT = 720;
177 
sceneWidth(int keys)178 static qreal sceneWidth(int keys) {
179     return KEYWIDTH * qCeil( keys * 7.0 / 12.0 );
180 }
181 
182 /**
183  * Constructor.
184  * @param baseOctave octave base number
185  * @param numKeys number of keys
186  * @param startKey starting key
187  * @param keyPressedColor highlight keys color
188  * @param parent owner object
189  */
PianoScene(const int baseOctave,const int numKeys,const int startKey,const QColor & keyPressedColor,QObject * parent)190 PianoScene::PianoScene ( const int baseOctave,
191                          const int numKeys,
192                          const int startKey,
193                          const QColor& keyPressedColor,
194                          QObject * parent )
195     : QGraphicsScene( QRectF(0, 0, sceneWidth(numKeys), KEYHEIGHT), parent ),
196       d(new PianoScenePrivate(baseOctave, numKeys, startKey))
197 {
198     if (keyPressedColor.isValid()) {
199         setKeyPressedColor(keyPressedColor);
200     }
201     QBrush hilightBrush(getKeyPressedColor());
202     PianoKeybd* view = dynamic_cast<PianoKeybd*>(parent);
203     if (view != nullptr) {
204         setFont(view->font());
205     }
206     int upperLimit = d->m_numKeys + d->m_startKey;
207     int adj = d->m_startKey % 12;
208     if (adj >= 5) adj++;
209     for(int i = d->m_startKey; i < upperLimit; ++i)
210     {
211         float x = 0;
212         PianoKey* key = nullptr;
213         KeyLabel* lbl = nullptr;
214         int ocs = i / 12 * 7;
215         int j = i % 12;
216         if (j >= 5) j++;
217         if ((j % 2) == 0) {
218             x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH;
219             key = new PianoKey( QRectF(x, 0, KEYWIDTH, KEYHEIGHT), false, i );
220             lbl = new KeyLabel(key);
221             lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(0));
222         } else {
223             x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH + KEYWIDTH * 0.6 + 1;
224             key = new PianoKey( QRectF( x, 0, KEYWIDTH * 0.8 - 1, KEYHEIGHT * 0.6 ), true, i );
225             key->setZValue( 1 );
226             lbl = new KeyLabel(key);
227             lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(1));
228         }
229         addItem( key );
230         lbl->setFont(font());
231         key->setAcceptTouchEvents(true);
232         key->setPressedBrush(hilightBrush);
233         d->m_keys.insert(i, key);
234         d->m_labels.insert(i, lbl);
235     }
236     hideOrShowKeys();
237     retranslate();
238 }
239 
240 /**
241  * Destructor.
242  */
~PianoScene()243 PianoScene::~PianoScene()
244 { }
245 
246 /**
247  * Returns the calculated size of the scene.
248  * @return the calculated size of the scene
249  */
sizeHint() const250 QSize PianoScene::sizeHint() const
251 {
252     return {static_cast<int>(sceneWidth(d->m_numKeys)), KEYHEIGHT};
253 }
254 
255 /**
256  * Assigns the computer keyboard note map.
257  * @param map the computer keyboard note map.
258  */
setKeyboardMap(KeyboardMap * map)259 void PianoScene::setKeyboardMap(KeyboardMap *map)
260 {
261     d->m_keybdMap = map;
262 }
263 
264 /**
265  * Returns the computer keyboard note map.
266  * @return the computer keyboard note map
267  */
getKeyboardMap() const268 KeyboardMap *PianoScene::getKeyboardMap() const
269 {
270     return d->m_keybdMap;
271 }
272 
273 /**
274  * Gets the PianoHandler pointer to the note receiver.
275  *
276  * If this method returns null, then there is not a PianoHandler class assigned,
277  * and then the signals noteOn() and noteOff() are emitted instead.
278  * @return pointer to the PianoHandler class, if there is one assigned
279  */
getPianoHandler() const280 PianoHandler *PianoScene::getPianoHandler() const
281 {
282     return d->m_handler;
283 }
284 
285 /**
286  * Assigns a PianoHandler pointer for processing note events.
287  *
288  * When this member is used to assign a PianoHandler instance, then
289  * the methods in that instance are called instead of emitting the
290  * signals noteOn() and noteOff().
291  * @param handler pointer to a PianoHandler instance
292  */
setPianoHandler(PianoHandler * handler)293 void PianoScene::setPianoHandler(PianoHandler *handler)
294 {
295     d->m_handler = handler;
296 }
297 
298 /**
299  * Returns the palette used for highlighting the played keys
300  * @return The PianoPalette used to highlight the played keys
301  */
getHighlightPalette()302 PianoPalette PianoScene::getHighlightPalette()
303 {
304     return d->m_hilightPalette;
305 }
306 
307 /**
308  * Displays the note label over a highligted key
309  * @param key the activated key
310  */
displayKeyOn(PianoKey * key)311 void PianoScene::displayKeyOn(PianoKey* key)
312 {
313     key->setPressed(true);
314     int n = key->getNote() + d->m_baseOctave*12 + d->m_transpose;
315     QString s = QString("#%1 (%2)").arg(n).arg(noteName(key));
316     emit signalName(s);
317     KeyLabel* lbl = dynamic_cast<KeyLabel*>(key->childItems().constFirst());
318     if (lbl != nullptr) {
319         lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 3 : 2));
320         if (d->m_showLabels == ShowActivated) {
321             lbl->setVisible(true);
322         }
323     }
324 }
325 
326 /**
327  * Displays highlighted the activated key with the supplied color and note velocity
328  * @param key the activated key
329  * @param color the highlight color
330  * @param vel the MIDI note velocity
331  */
showKeyOn(PianoKey * key,QColor color,int vel)332 void PianoScene::showKeyOn( PianoKey* key, QColor color, int vel )
333 {
334     //qDebug() << Q_FUNC_INFO << key->getNote() << vel << color << d->m_velocityTint;
335     if (d->m_velocityTint && (vel >= 0) && (vel < 128) && color.isValid() ) {
336         QBrush hilightBrush(color.lighter(200 - vel));
337         key->setPressedBrush(hilightBrush);
338     } else if (color.isValid()) {
339         key->setPressedBrush(color);
340     }
341     displayKeyOn(key);
342 }
343 
344 /**
345  * Displays highlighted the activated key with the supplied note velocity
346  * @param key the activated key
347  * @param vel the MIDI note velocity
348  */
showKeyOn(PianoKey * key,int vel)349 void PianoScene::showKeyOn( PianoKey* key, int vel )
350 {
351     setHighlightColorFromPolicy(key, vel);
352     displayKeyOn(key);
353 }
354 
355 /**
356  * Displays as deactivated a key
357  * @param key the deactivated key
358  * @param vel the MIDI note velocity
359  */
showKeyOff(PianoKey * key,int vel)360 void PianoScene::showKeyOff( PianoKey* key, int vel)
361 {
362     Q_UNUSED(vel)
363     key->setPressed(false);
364     emit signalName(QString());
365     KeyLabel* lbl = dynamic_cast<KeyLabel*>(key->childItems().constFirst());
366     if (lbl != nullptr) {
367         lbl->restoreColor();
368         if (d->m_showLabels == ShowActivated) {
369             lbl->setVisible(false);
370         }
371     }
372 }
373 
374 /**
375  * Displays highlighted the corresponding key for a given MIDI note, with a color and MIDI velocity
376  * @param note The MIDI note number
377  * @param color The highlight color
378  * @param vel The MIDI note velocity
379  */
showNoteOn(const int note,QColor color,int vel)380 void PianoScene::showNoteOn( const int note, QColor color, int vel )
381 {
382     //qDebug() << Q_FUNC_INFO << note << vel << color;
383     int n = note - d->m_baseOctave*12 - d->m_transpose;
384     if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n) && color.isValid())
385         showKeyOn(d->m_keys.value(n), color, vel);
386 }
387 
388 /**
389  * Displays highlighted the corresponding key for a given MIDI note, with MIDI velocity
390  * @param note The MIDI note number
391  * @param vel The MIDI note velocity
392  */
showNoteOn(const int note,int vel)393 void PianoScene::showNoteOn( const int note, int vel )
394 {
395     //qDebug() << Q_FUNC_INFO << note << vel;
396     int n = note - d->m_baseOctave*12 - d->m_transpose;
397     if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
398         showKeyOn(d->m_keys.value(n), vel);
399     }
400 }
401 
402 /**
403  * Displays deactivated the corresponding key for a given MIDI note, with MIDI velocity
404  * @param note The MIDI note number
405  * @param vel The MIDI note velocity
406  */
showNoteOff(const int note,int vel)407 void PianoScene::showNoteOff( const int note, int vel )
408 {
409     int n = note - d->m_baseOctave*12 - d->m_transpose;
410     if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
411         showKeyOff(d->m_keys.value(n), vel);
412     }
413 }
414 
415 /**
416  * Returns the base octave number.
417  * @see setBaseOctave()
418  * @return the base octave number
419  */
baseOctave() const420 int PianoScene::baseOctave() const { return d->m_baseOctave; }
421 
422 /**
423  * Performs a Note On MIDI event for the given MIDI note number and velocity.
424  * If a PianoHandler instance is assigned, its PianoHandler::noteOn() method is called,
425  * otherwise the noteOn() signal is triggered.
426  * @param note The MIDI note number
427  * @param vel The MIDI velocity
428  */
triggerNoteOn(const int note,const int vel)429 void PianoScene::triggerNoteOn( const int note, const int vel )
430 {
431     int n = d->m_baseOctave*12 + note + d->m_transpose;
432     if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
433         if (d->m_handler != nullptr) {
434             d->m_handler->noteOn(n, vel);
435         } else {
436             emit noteOn(n, vel);
437         }
438     }
439 }
440 
441 /**
442  * Performs a Note Off MIDI event for the given MIDI note number and velocity.
443  * If a PianoHandler instance is assigned, its PianoHandler::noteOff() method is called,
444  * otherwise the noteOff() signal is triggered.
445  * @param note The MIDI note number
446  * @param vel The MIDI velocity
447  */
triggerNoteOff(const int note,const int vel)448 void PianoScene::triggerNoteOff( const int note, const int vel )
449 {
450     int n = d->m_baseOctave*12 + note + d->m_transpose;
451     if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
452         if (d->m_handler != nullptr) {
453             d->m_handler->noteOff(n, vel);
454         } else {
455             emit noteOff(n, vel);
456         }
457     }
458 }
459 
460 /**
461  * Assigns to the given key the highlight color from the active highlight palette
462  * and the given MIDI velocity.
463  * @param key The given piano key
464  * @param vel The MIDI note velocity
465  */
setHighlightColorFromPolicy(PianoKey * key,int vel)466 void PianoScene::setHighlightColorFromPolicy(PianoKey* key, int vel)
467 {
468     QColor c;
469     //qDebug() << Q_FUNC_INFO << key->getNote() << vel << d->m_velocityTint;
470     switch (d->m_hilightPalette.paletteId()) {
471     case PAL_SINGLE:
472         c = d->m_hilightPalette.getColor(0);
473         break;
474     case PAL_DOUBLE:
475         c = d->m_hilightPalette.getColor(key->getType());
476         break;
477     case PAL_CHANNELS:
478         c = d->m_hilightPalette.getColor(d->m_channel);
479         break;
480     case PAL_HISCALE:
481         c = d->m_hilightPalette.getColor(key->getDegree());
482         break;
483     default:
484         return;
485     }
486     if (c.isValid()) {
487         if (d->m_velocityTint && (vel >= 0) && (vel < 128)) {
488             QBrush h(c.lighter(200 - vel));
489             key->setPressedBrush(h);
490         } else {
491             key->setPressedBrush(c);
492         }
493     }
494 }
495 
496 /**
497  * Produces a MIDI Note On event and highlights the given key
498  * @param key The given key
499  */
keyOn(PianoKey * key)500 void PianoScene::keyOn( PianoKey* key )
501 {
502     triggerNoteOn(key->getNote(), d->m_velocity);
503     showKeyOn(key, d->m_velocity);
504 }
505 
506 /**
507  * Produces a MIDI Note Off event and deactivates the given key
508  * @param key The given key
509  */
keyOff(PianoKey * key)510 void PianoScene::keyOff( PianoKey* key )
511 {
512     triggerNoteOff(key->getNote(), 0);
513     showKeyOff(key, 0);
514 }
515 
516 /**
517  * Produces a MIDI Note On event and highlights the given key with the given pressure
518  * @param key The given key
519  * @param pressure The applied pressure
520  */
keyOn(PianoKey * key,qreal pressure)521 void PianoScene::keyOn( PianoKey* key, qreal pressure )
522 {
523     int vel = d->m_velocity * pressure;
524     triggerNoteOn(key->getNote(), vel);
525     showKeyOn(key, vel);
526 }
527 
528 /**
529  * Produces a MIDI Note Off event and deactivates the given key with the given pressure.
530  * @param key The given key
531  * @param pressure The applied pressure
532  */
keyOff(PianoKey * key,qreal pressure)533 void PianoScene::keyOff( PianoKey* key, qreal pressure )
534 {
535     int vel = d->m_velocity * pressure;
536     triggerNoteOff(key->getNote(), vel);
537     showKeyOff(key, vel);
538 }
539 
540 /**
541  * Produces a MIDI Note On event and highlights the corresponding key for the given MIDI note number.
542  * @param note The given MIDI note number
543  */
keyOn(const int note)544 void PianoScene::keyOn(const int note)
545 {
546     if (d->m_keys.contains(note))
547         keyOn(d->m_keys.value(note));
548     else
549         triggerNoteOn(note, d->m_velocity);
550 }
551 
552 /**
553  * Produces a MIDI Note Off event and deactivates the corresponding key for the given MIDI note number.
554  * @param note The given MIDI note number
555  */
keyOff(const int note)556 void PianoScene::keyOff(const int note)
557 {
558     if (d->m_keys.contains(note))
559         keyOff(d->m_keys.value(note));
560     else
561         triggerNoteOff(note, d->m_velocity);
562 }
563 
564 /**
565  * Returns whether the low level computer keyboard mode is enabled.
566  * @return true if the low level computer keyboard mode is enabled
567  */
getRawKeyboardMode() const568 bool PianoScene::getRawKeyboardMode() const
569 {
570     return d->m_rawkbd;
571 }
572 
573 /**
574  * Returns the piano key for the given scene point coordenates.
575  * @param p The given scene point coordenates
576  * @return
577  */
getKeyForPos(const QPointF & p) const578 PianoKey* PianoScene::getKeyForPos( const QPointF& p ) const
579 {
580     PianoKey* key = nullptr;
581     QList<QGraphicsItem *> ptitems = this->items(p, Qt::IntersectsItemShape, Qt::DescendingOrder);
582     foreach(QGraphicsItem *itm, ptitems) {
583         key = dynamic_cast<PianoKey*>(itm);
584         if (key != nullptr)
585             break;
586     }
587     return key;
588 }
589 
590 /**
591  * This event handler, for event mouseEvent, is reimplemented to receive mouse move events for the scene.
592  * @param mouseEvent The mouse move event object pointer
593  */
mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)594 void PianoScene::mouseMoveEvent ( QGraphicsSceneMouseEvent * mouseEvent )
595 {
596     if (d->m_mouseEnabled) {
597         if (d->m_mousePressed) {
598             PianoKey* key = getKeyForPos(mouseEvent->scenePos());
599             PianoKey* lastkey = getKeyForPos(mouseEvent->lastScenePos());
600             if ((lastkey != nullptr) && (lastkey != key) && lastkey->isPressed()) {
601                 keyOff(lastkey);
602             }
603             if ((key != nullptr) && !key->isPressed()) {
604                 keyOn(key);
605             }
606             mouseEvent->accept();
607             return;
608         }
609     }
610 }
611 
612 /**
613  * This event handler, for event mouseEvent, is reimplemented to receive mouse press events for the scene.
614  * @param mouseEvent The mouse press event object pointer
615  */
mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)616 void PianoScene::mousePressEvent ( QGraphicsSceneMouseEvent * mouseEvent )
617 {
618     if (d->m_mouseEnabled) {
619         PianoKey* key = getKeyForPos(mouseEvent->scenePos());
620         if (key != nullptr && !key->isPressed()) {
621             keyOn(key);
622             d->m_mousePressed = true;
623             mouseEvent->accept();
624             return;
625         }
626     }
627 }
628 
629 /**
630  * This event handler, for event mouseEvent, is reimplemented to receive mouse release events for the scene.
631  * @param mouseEvent The mouse release event object pointer
632  */
mouseReleaseEvent(QGraphicsSceneMouseEvent * mouseEvent)633 void PianoScene::mouseReleaseEvent ( QGraphicsSceneMouseEvent * mouseEvent )
634 {
635     if (d->m_mouseEnabled) {
636         d->m_mousePressed = false;
637         PianoKey* key = getKeyForPos(mouseEvent->scenePos());
638         if (key != nullptr && key->isPressed()) {
639             keyOff(key);
640             mouseEvent->accept();
641             return;
642         }
643     }
644 }
645 
646 /**
647  * Returns the note number for the given computer keyboard key code.
648  * @param key The given computer keyboard key code
649  * @return The note number
650  */
getNoteFromKey(const int key) const651 int PianoScene::getNoteFromKey( const int key ) const
652 {
653     if (d->m_keybdMap != nullptr) {
654         KeyboardMap::ConstIterator it = d->m_keybdMap->constFind(key);
655         if ((it != d->m_keybdMap->constEnd()) && (it.key() == key)) {
656             int note = it.value();
657             return note;
658         }
659     }
660     return -1;
661 }
662 
663 /**
664  * Returns the piano key object corresponding to the given computer keyboard key.
665  * @param key The given computer keyboard key
666  * @return The Piano Key object pointer
667  */
getPianoKey(const int key) const668 PianoKey* PianoScene::getPianoKey( const int key ) const
669 {
670     int note = getNoteFromKey(key);
671     if (d->m_keys.contains(note))
672         return d->m_keys.value(note);
673     return nullptr;
674 }
675 
676 /**
677  * This event handler, for event keyEvent, is reimplemented to receive keypress events.
678  * @param keyEvent The computer keyboard pressed event
679  */
keyPressEvent(QKeyEvent * keyEvent)680 void PianoScene::keyPressEvent ( QKeyEvent * keyEvent )
681 {
682     if ( d->m_keyboardEnabled) {
683         if ( !d->m_rawkbd && !keyEvent->isAutoRepeat() ) { // ignore auto-repeats
684             int note = getNoteFromKey(keyEvent->key());
685             if (note > -1)
686                 keyOn(note);
687         }
688         keyEvent->accept();
689         return;
690     }
691     keyEvent->ignore();
692 }
693 
694 /**
695  * This event handler, for event keyEvent, is reimplemented to receive key release events.
696  * @param keyEvent The computer keyboard released event
697  */
keyReleaseEvent(QKeyEvent * keyEvent)698 void PianoScene::keyReleaseEvent ( QKeyEvent * keyEvent )
699 {
700     if (d->m_keyboardEnabled) {
701         if ( !d->m_rawkbd && !keyEvent->isAutoRepeat() ) { // ignore auto-repeats
702             int note = getNoteFromKey(keyEvent->key());
703             if (note > -1)
704                 keyOff(note);
705         }
706         keyEvent->accept();
707         return;
708     }
709     keyEvent->ignore();
710 }
711 
712 /**
713  * Processes touch screen events
714  * @param event The given event
715  * @return true if the event was processed
716  */
event(QEvent * event)717 bool PianoScene::event(QEvent *event)
718 {
719     switch(event->type()) {
720     case QEvent::TouchBegin:
721     case QEvent::TouchEnd:
722     case QEvent::TouchUpdate:
723     {
724         QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
725 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
726         const auto touchScreen = QTouchDevice::DeviceType::TouchScreen;
727 #else
728         const auto touchScreen = QInputDevice::DeviceType::TouchScreen;
729 #endif
730         if (d->m_touchEnabled && touchEvent->device()->type() == touchScreen) {
731             QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
732             bool hasPressure =
733 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
734                         touchEvent->device()->capabilities().testFlag(QTouchDevice::Pressure);
735 #else
736                         touchEvent->device()->capabilities().testFlag(QInputDevice::Capability::Pressure);
737 #endif
738             foreach(const QTouchEvent::TouchPoint& touchPoint, touchPoints) {
739                 switch (touchPoint.state()) {
740                 //case Qt::TouchPointPrimary:
741                 case Qt::TouchPointStationary:
742                     continue;
743                 case Qt::TouchPointReleased: {
744                         PianoKey* key = getKeyForPos(touchPoint.scenePos());
745                         if (key != nullptr && key->isPressed()) {
746                             if (hasPressure) {
747                                 keyOff(key, touchPoint.pressure());
748                             } else {
749                                 keyOff(key);
750                             }
751                         }
752                         break;
753                     }
754                 case Qt::TouchPointPressed: {
755                         PianoKey* key = getKeyForPos(touchPoint.scenePos());
756                         if (key != nullptr && !key->isPressed()) {
757                             if (hasPressure) {
758                                 keyOn(key, touchPoint.pressure());
759                             } else {
760                                 keyOn(key);
761                             }
762                             key->ensureVisible();
763                         }
764                         break;
765                     }
766                 case Qt::TouchPointMoved: {
767                         PianoKey* key = getKeyForPos(touchPoint.scenePos());
768                         PianoKey* lastkey = getKeyForPos(touchPoint.lastScenePos());
769                         if ((lastkey != nullptr) && (lastkey != key) && lastkey->isPressed()) {
770                             if (hasPressure) {
771                                 keyOff(lastkey, touchPoint.pressure());
772                             } else {
773                                 keyOff(lastkey);
774                             }
775                         }
776                         if ((key != nullptr) && !key->isPressed()) {
777                             if (hasPressure) {
778                                 keyOn(key, touchPoint.pressure());
779                             } else {
780                                 keyOn(key);
781                             }
782                         }
783                         break;
784                     }
785                 default:
786                     //qDebug() << "TouchPoint state: " << touchPoint.state();
787                     break;
788                 }
789             }
790             //qDebug() << "accepted event: " << event;
791             event->accept();
792             return true;
793         }
794         break;
795     }
796     default:
797         break;
798     }
799     //qDebug() << "unprocessed event: " << event;
800     return QGraphicsScene::event(event);
801 }
802 
803 /**
804  * Deactivates all keys.
805  */
allKeysOff()806 void PianoScene::allKeysOff()
807 {
808     foreach(PianoKey* key, d->m_keys) {
809         key->setPressed(false);
810     }
811 }
812 
813 /**
814  * Assigns a single color for key highlight. This is an alternative to creating a
815  * highlight palette with a single color and assigning it.
816  * @see setHighlightPalette()
817  * @param color Color for key highlight
818  */
setKeyPressedColor(const QColor & color)819 void PianoScene::setKeyPressedColor(const QColor& color)
820 {
821     if (color.isValid()) {
822         d->m_hilightPalette = PianoPalette(PAL_SINGLE);
823         d->m_hilightPalette.setColor(0, color);
824         QBrush hilightBrush(color);
825         for (PianoKey* key : qAsConst(d->m_keys)) {
826             key->setPressedBrush(hilightBrush);
827         }
828     }
829 }
830 
831 /**
832  * Assigns the default highlight palette colors and assigns it to the scene.
833  */
resetKeyPressedColor()834 void PianoScene::resetKeyPressedColor()
835 {
836     d->m_hilightPalette.resetColors();
837     QBrush hilightBrush(getKeyPressedColor());
838     for (PianoKey* key : qAsConst(d->m_keys)) {
839         key->setPressedBrush(hilightBrush);
840     }
841 }
842 
843 /**
844  * Returns the minimum MIDI note number that will be displayed.
845  * @return the minimum MIDI note number
846  */
getMinNote() const847 int PianoScene::getMinNote() const
848 {
849     return d->m_minNote;
850 }
851 
852 /**
853  * Hides or shows keys
854  */
hideOrShowKeys()855 void PianoScene::hideOrShowKeys()
856 {
857     for (PianoKey* key : qAsConst(d->m_keys)) {
858         int n = d->m_baseOctave*12 + key->getNote() + d->m_transpose;
859         bool b = !(n > d->m_maxNote) && !(n < d->m_minNote);
860         key->setVisible(b);
861     }
862 }
863 
864 /**
865  * Assigns the minimum MIDI note number that will be displayed.
866  * @param note the minimum MIDI note number
867  */
setMinNote(const int note)868 void PianoScene::setMinNote(const int note)
869 {
870     if (d->m_minNote != note) {
871         d->m_minNote = note;
872         hideOrShowKeys();
873     }
874 }
875 
876 /**
877  * Returns the maximum MIDI note number that will be displayed.
878  * @return the maximum MIDI note number
879  */
getMaxNote() const880 int PianoScene::getMaxNote() const
881 {
882     return d->m_maxNote;
883 }
884 
885 /**
886  * Assigns the maximum MIDI note number that will be displayed.
887  * @param note the maximum MIDI note number
888  */
setMaxNote(const int note)889 void PianoScene::setMaxNote(const int note)
890 {
891     if (d->m_maxNote != note) {
892         d->m_maxNote = note;
893         hideOrShowKeys();
894     }
895 }
896 
897 /**
898  * Returns the transpose amount in semitones.
899  * @return the transpose amount in semitones
900  */
getTranspose() const901 int PianoScene::getTranspose() const
902 {
903     return d->m_transpose;
904 }
905 
906 /**
907  * Assigns the octave base number
908  * @param base the octave base number
909  */
setBaseOctave(const int base)910 void PianoScene::setBaseOctave(const int base)
911 {
912     if (d->m_baseOctave != base) {
913         d->m_baseOctave = base;
914         hideOrShowKeys();
915         refreshLabels();
916     }
917 }
918 
919 /**
920  * Returns the number of keys that will be displayed.
921  * @return the number of keys
922  */
numKeys() const923 int PianoScene::numKeys() const
924 {
925     return d->m_numKeys;
926 }
927 
928 /**
929  * Returns the first key number that will be displayed.
930  * @return the first key number
931  */
startKey() const932 int PianoScene::startKey() const
933 {
934     return d->m_startKey;
935 }
936 
937 /**
938  * Returns whether the given note number is a octave startup note
939  * @param note The given note number
940  * @return true if the given note number is a octave startup note
941  */
isOctaveStart(const int note)942 bool PianoScene::isOctaveStart(const int note)
943 {
944     return (note + d->m_transpose + 12) % 12 == 0;
945 }
946 
947 /**
948  * Returns the note name string that will be displayed over a given piano key.
949  * @param key The given piano key
950  * @return the note name string
951  */
noteName(PianoKey * key)952 QString PianoScene::noteName( PianoKey* key )
953 {
954     Q_ASSERT(key != nullptr);
955     int note = key->getNote();
956     int num = (note + d->m_transpose + 12) % 12;
957     int adj = ((note + d->m_transpose < 0) ? 2 : 1) - d->m_octave + 1;
958     int oct = d->m_baseOctave + ((note + d->m_transpose) / 12) - adj;
959     if (d->m_noteNames.isEmpty()) {
960         QString name;
961         if (!d->m_names_f.isEmpty() && !d->m_names_s.isEmpty()) {
962             switch(d->m_alterations) {
963             case ShowFlats:
964                 name = d->m_names_f.value(num);
965                 break;
966             case ShowSharps:
967                 name =  d->m_names_s.value(num);
968                 break;
969             case ShowNothing:
970                 if (key->isBlack()) {
971                     return QString();
972                 }
973                 name =  d->m_names_s.value(num);
974                 break;
975             default:
976                 break;
977             }
978         }
979         if (d->m_octave==OctaveNothing) {
980             return name;
981         } else {
982             return QString("%1%2").arg(name).arg(oct);
983         }
984     } else {
985         if (d->m_noteNames.length() == 128) {
986             int n = d->m_baseOctave*12 + note + d->m_transpose;
987             //qDebug() << Q_FUNC_INFO << n << note;
988             if (n >= 0 && n < d->m_noteNames.length()) {
989                 return d->m_noteNames.value(n);
990             }
991         } else if (d->m_noteNames.length() >= 12) {
992             if (d->m_octave==OctaveNothing) {
993                 return d->m_noteNames.value(num);
994             } else {
995                 return QString("%1%2").arg(d->m_noteNames.value(num)).arg(oct);
996             }
997         }
998         return QString();
999     }
1000 }
1001 
1002 /**
1003  * Refresh the visibility and other attributes of the labels shown over the piano keys.
1004  */
refreshLabels()1005 void PianoScene::refreshLabels()
1006 {
1007     for (KeyLabel* lbl : qAsConst(d->m_labels)) {
1008         PianoKey* key = dynamic_cast<PianoKey*>(lbl->parentItem());
1009         if (key != nullptr) {
1010             lbl->setVisible(false);
1011             lbl->setFont(font());
1012             lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 1 : 0));
1013             lbl->setOrientation(d->m_orientation);
1014             lbl->setPlainText(noteName(key));
1015             lbl->adjust();
1016             lbl->setVisible((d->m_showLabels == ShowAlways) ||
1017                 (d->m_showLabels == ShowMinimum && isOctaveStart(key->getNote())));
1018         }
1019     }
1020 }
1021 
1022 /**
1023  * Refresh the background colors of all the piano keys
1024  */
refreshKeys()1025 void PianoScene::refreshKeys()
1026 {
1027     for (PianoKey* key : qAsConst(d->m_keys)) {
1028         if (d->m_showColorScale && (d->m_backgroundPalette.paletteId() == PAL_SCALE)) {
1029             int degree = key->getNote() % 12;
1030             key->setBrush(d->m_backgroundPalette.getColor(degree));
1031         } else {
1032             key->setBrush(d->m_backgroundPalette.getColor(key->isBlack() ? 1 : 0));
1033         }
1034         key->setPressed(false);
1035     }
1036 }
1037 
1038 /**
1039  * Assigns the label visibility policy to the piano keys
1040  * @see LabelVisibility
1041  * @param show the new label visibility policy
1042  */
setShowLabels(const LabelVisibility show)1043 void PianoScene::setShowLabels(const LabelVisibility show)
1044 {
1045     //qDebug() << Q_FUNC_INFO << show;
1046     if (d->m_showLabels != show) {
1047         d->m_showLabels = show;
1048         refreshLabels();
1049     }
1050 }
1051 
1052 /**
1053  * Returns the alterations name policy.
1054  * @see LabelAlteration, setAlterations()
1055  * @return the alterations name policy
1056  */
alterations() const1057 LabelAlteration PianoScene::alterations() const
1058 {
1059     return d->m_alterations;
1060 }
1061 
1062 /**
1063  * Assigns the alterations name policy
1064  * @see LabelAlteration, alterations()
1065  * @param use the new alterations name policy
1066  */
setAlterations(const LabelAlteration use)1067 void PianoScene::setAlterations(const LabelAlteration use)
1068 {
1069     if (d->m_alterations != use) {
1070         d->m_alterations = use;
1071         refreshLabels();
1072     }
1073 }
1074 
1075 /**
1076  * Returns the central octave name policy.
1077  * @return the central octave name policy
1078  */
getOctave() const1079 LabelCentralOctave PianoScene::getOctave() const
1080 {
1081     return d->m_octave;
1082 }
1083 
1084 /**
1085  * Assigns the label orientation policy.
1086  * @param orientation the label orientation policy
1087  */
setOrientation(const LabelOrientation orientation)1088 void PianoScene::setOrientation(const LabelOrientation orientation)
1089 {
1090     if (d->m_orientation != orientation) {
1091         d->m_orientation = orientation;
1092         refreshLabels();
1093     }
1094 }
1095 
isKeyboardEnabled() const1096 bool PianoScene::isKeyboardEnabled() const
1097 {
1098     return d->m_keyboardEnabled;
1099 }
1100 
setOctave(const LabelCentralOctave octave)1101 void PianoScene::setOctave(const LabelCentralOctave octave)
1102 {
1103     if (d->m_octave != octave) {
1104         d->m_octave = octave;
1105         refreshLabels();
1106     }
1107 }
1108 
getOrientation() const1109 LabelOrientation PianoScene::getOrientation() const
1110 {
1111     return d->m_orientation;
1112 }
1113 
1114 /**
1115  * Assigns the transpose amount in semitones.
1116  * @param transpose the transpose amount in semitones
1117  */
setTranspose(const int transpose)1118 void PianoScene::setTranspose(const int transpose)
1119 {
1120     if (d->m_transpose != transpose && transpose > -12 && transpose < 12) {
1121         d->m_transpose = transpose;
1122         hideOrShowKeys();
1123         refreshLabels();
1124     }
1125 }
1126 
1127 /**
1128  * Returns the label visibility policy (display note names over the piano keys).
1129  * @see LabelVisibility, setShowLabels()
1130  * @return the label visibility policy
1131  */
showLabels() const1132 LabelVisibility PianoScene::showLabels() const
1133 {
1134     return d->m_showLabels;
1135 }
1136 
1137 /**
1138  * Assigns the low level computer keyboard mode.
1139  * @param b the low level computer keyboard mode
1140  */
setRawKeyboardMode(bool b)1141 void PianoScene::setRawKeyboardMode(bool b)
1142 {
1143     if (d->m_rawkbd != b) {
1144         d->m_rawkbd = b;
1145     }
1146 }
1147 
1148 /**
1149  * Returns the custom note names list.
1150  * @return the custom note names list
1151  */
customNoteNames() const1152 QStringList PianoScene::customNoteNames() const
1153 {
1154     return d->m_noteNames;
1155 }
1156 
1157 /**
1158  * Returns the standard note names list.
1159  * @return the standard note names list
1160  */
standardNoteNames() const1161 QStringList PianoScene::standardNoteNames() const
1162 {
1163     return d->m_names_s;
1164 }
1165 
1166 /**
1167  * Returns the MIDI note velocity parameter that is assigned to the MIDI OUT notes.
1168  * @return the MIDI note velocity
1169  */
getVelocity()1170 int PianoScene::getVelocity()
1171 {
1172     return d->m_velocity;
1173 }
1174 
1175 /**
1176  * Assigns the MIDI note velocity parameter that is assigned to the MIDI OUT notes.
1177  * @param velocity the MIDI note velocity
1178  */
setVelocity(const int velocity)1179 void PianoScene::setVelocity(const int velocity)
1180 {
1181     d->m_velocity = velocity;
1182 }
1183 
1184 /**
1185  * Returns the MIDI channel that is assigned to the output events, or used to filter
1186  * the input events (unless MIDI OMNI mode is enabled).
1187  * @return the MIDI channel
1188  */
getChannel() const1189 int PianoScene::getChannel() const
1190 {
1191     return d->m_channel;
1192 }
1193 
1194 /**
1195  * Assigns the MIDI channel that is included into the output events, or used to filter
1196  * the input events (unless MIDI OMNI mode is enabled).
1197  * @param channel the MIDI channel
1198  */
setChannel(const int channel)1199 void PianoScene::setChannel(const int channel)
1200 {
1201     d->m_channel = channel;
1202 }
1203 
1204 /**
1205  * Assigns the list of custom note names, and enables this mode.
1206  * @param names the list of custom note names
1207  */
useCustomNoteNames(const QStringList & names)1208 void PianoScene::useCustomNoteNames(const QStringList& names)
1209 {
1210     //qDebug() << Q_FUNC_INFO << names;
1211     d->m_noteNames = names;
1212     refreshLabels();
1213 }
1214 
1215 /**
1216  * Assigns the standard note names, clearing the list of custom note names.
1217  */
useStandardNoteNames()1218 void PianoScene::useStandardNoteNames()
1219 {
1220     //qDebug() << Q_FUNC_INFO;
1221     d->m_noteNames.clear();
1222     refreshLabels();
1223 }
1224 
1225 /**
1226  * Enables or disables the computer keyboard note generation.
1227  * @param enable the computer keyboard note generation
1228  */
setKeyboardEnabled(const bool enable)1229 void PianoScene::setKeyboardEnabled(const bool enable)
1230 {
1231     if (enable != d->m_keyboardEnabled) {
1232         d->m_keyboardEnabled = enable;
1233     }
1234 }
1235 
1236 /**
1237  * Returns whether the computer keyboard note generation is enabled
1238  * @return true if the computer keyboard note generation is enabled
1239  */
isMouseEnabled() const1240 bool PianoScene::isMouseEnabled() const
1241 {
1242     return d->m_mouseEnabled;
1243 }
1244 
1245 /**
1246  * Enables or disables the mouse note generation.
1247  * @param enable the mouse note generation
1248  */
setMouseEnabled(const bool enable)1249 void PianoScene::setMouseEnabled(const bool enable)
1250 {
1251     if (enable != d->m_mouseEnabled) {
1252         d->m_mouseEnabled = enable;
1253     }
1254 }
1255 
1256 /**
1257  * Returns whether the touch screen note generation is enabled.
1258  * @return true if the touch screen note generation is enabled
1259  */
isTouchEnabled() const1260 bool PianoScene::isTouchEnabled() const
1261 {
1262     return d->m_touchEnabled;
1263 }
1264 
1265 /**
1266  * Enables or disables the touch screen note generation.
1267  * @param enable the touch screen note generation
1268  */
setTouchEnabled(const bool enable)1269 void PianoScene::setTouchEnabled(const bool enable)
1270 {
1271     if (enable != d->m_touchEnabled) {
1272         d->m_touchEnabled = enable;
1273     }
1274 }
1275 
1276 /**
1277  * Returns whether the velocity parameter of note events is used to influence the highlight key colors.
1278  * @return whether the velocity parameter of note events is used to influence the highlight key colors
1279  */
velocityTint() const1280 bool PianoScene::velocityTint() const
1281 {
1282     return d->m_velocityTint;
1283 }
1284 
1285 /**
1286  * Enables or disables the velocity parameter of note events to influence the highlight key colors.
1287  * @param enable the velocity parameter of note events to influence the highlight key colors
1288  */
setVelocityTint(const bool enable)1289 void PianoScene::setVelocityTint(const bool enable)
1290 {
1291     //qDebug() << Q_FUNC_INFO << enable;
1292     d->m_velocityTint = enable;
1293 }
1294 
1295 /**
1296  * Retranslates the standard note names
1297  */
retranslate()1298 void PianoScene::retranslate()
1299 {
1300     d->m_names_s = QStringList{
1301         tr("C"),
1302         tr("C♯"),
1303         tr("D"),
1304         tr("D♯"),
1305         tr("E"),
1306         tr("F"),
1307         tr("F♯"),
1308         tr("G"),
1309         tr("G♯"),
1310         tr("A"),
1311         tr("A♯"),
1312         tr("B")};
1313     d->m_names_f = QStringList{
1314         tr("C"),
1315         tr("D♭"),
1316         tr("D"),
1317         tr("E♭"),
1318         tr("E"),
1319         tr("F"),
1320         tr("G♭"),
1321         tr("G"),
1322         tr("A♭"),
1323         tr("A"),
1324         tr("B♭"),
1325         tr("B")};
1326     refreshLabels();
1327 }
1328 
1329 /**
1330  * Enables or disables the color scale key background mode.
1331  * @param show the color scale key background mode
1332  */
setShowColorScale(const bool show)1333 void PianoScene::setShowColorScale(const bool show)
1334 {
1335     if (d->m_showColorScale != show) {
1336         d->m_showColorScale = show;
1337         refreshKeys();
1338         invalidate();
1339     }
1340 }
1341 
1342 /**
1343  * Returns the single highlight palette color.
1344  * @return the single highlight palette color
1345  */
getKeyPressedColor() const1346 QColor PianoScene::getKeyPressedColor() const
1347 {
1348     return d->m_hilightPalette.getColor(0);
1349 }
1350 
1351 /**
1352  * Assigns the active highlight palette.
1353  * @param p the active highlight palette
1354  */
setHighlightPalette(const PianoPalette & p)1355 void PianoScene::setHighlightPalette( const PianoPalette& p )
1356 {
1357     if (d->m_hilightPalette != p) {
1358         d->m_hilightPalette = p;
1359         refreshKeys();
1360         invalidate();
1361     }
1362 }
1363 
1364 /**
1365  * Returns the background palette.
1366  * @return the background palette
1367  */
getBackgroundPalette()1368 PianoPalette PianoScene::getBackgroundPalette()
1369 {
1370     return d->m_backgroundPalette;
1371 }
1372 
1373 /**
1374  * Assigns the active background palette.
1375  * @param p the active background palette
1376  */
setBackgroundPalette(const PianoPalette & p)1377 void PianoScene::setBackgroundPalette(const PianoPalette& p )
1378 {
1379     if (d->m_backgroundPalette != p) {
1380         d->m_backgroundPalette = p;
1381         refreshKeys();
1382         invalidate();
1383     }
1384 }
1385 
1386 /**
1387  * Returns the active foreground palette.
1388  * @return the active foreground palette
1389  */
getForegroundPalette()1390 PianoPalette PianoScene::getForegroundPalette()
1391 {
1392     return d->m_foregroundPalette;
1393 }
1394 
1395 /**
1396  * Assigns the active foreground palette.
1397  * @param p the foreground palette
1398  */
setForegroundPalette(const PianoPalette & p)1399 void PianoScene::setForegroundPalette(const PianoPalette &p)
1400 {
1401     if (d->m_foregroundPalette != p) {
1402         d->m_foregroundPalette = p;
1403         refreshLabels();
1404         invalidate();
1405     }
1406 }
1407 
1408 /**
1409  * Returns whether the color scale mode is enabled.
1410  * @return true if the color scale mode is enabled
1411  */
showColorScale() const1412 bool PianoScene::showColorScale() const
1413 {
1414     return d->m_showColorScale;
1415 }
1416 
setKeyPicture(const bool natural,const QPixmap & pix)1417 void PianoScene::setKeyPicture(const bool natural, const QPixmap &pix)
1418 {
1419     d->m_keyPix[int(natural)] = pix;
1420     for (PianoKey* key : qAsConst(d->m_keys)) {
1421         if (key->isBlack() == !natural) {
1422             key->setPixmap(pix);
1423         }
1424     }
1425 }
1426 
getKeyPicture(const bool natural)1427 QPixmap PianoScene::getKeyPicture(const bool natural)
1428 {
1429     return d->m_keyPix[int(natural)];
1430 }
1431 
setUseKeyPictures(const bool enable)1432 void PianoScene::setUseKeyPictures(const bool enable)
1433 {
1434     d->m_useKeyPix = enable;
1435     for (PianoKey* key : qAsConst(d->m_keys)) {
1436         key->setUsePixmap(enable);
1437     }
1438 }
1439 
getUseKeyPictures() const1440 bool PianoScene::getUseKeyPictures() const
1441 {
1442     return d->m_useKeyPix;
1443 }
1444 
saveData(QByteArray & ba)1445 void PianoScene::saveData(QByteArray &ba)
1446 {
1447     d->saveData(ba);
1448 }
1449 
loadData(QByteArray & ba)1450 void PianoScene::loadData(QByteArray &ba)
1451 {
1452     d->loadData(ba);
1453 }
1454 
1455 } // namespace widgets
1456 } // namespace drumstick
1457