drumstick 2.10.0
C++ MIDI libraries using Qt objects, idioms, and style.
pianoscene.cpp
Go to the documentation of this file.
1/*
2 Virtual Piano Widget for Qt
3 Copyright (C) 2008-2024, 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 <QDebug>
20#include <QApplication>
21#include <QDataStream>
22#include <QByteArray>
23#include <QGraphicsSceneMouseEvent>
24#include <QKeyEvent>
25#include <QPalette>
26#include <QPixmap>
27#include <QtMath>
28#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
29#include <QTouchDevice>
30#else
31#include <QInputDevice>
32#endif
34#include "pianoscene.h"
35
47namespace drumstick { namespace widgets {
48
49class PianoScene::PianoScenePrivate
50{
51public:
52 PianoScenePrivate ( const int baseOctave,
53 const int numKeys,
54 const int startKey ):
55 m_baseOctave( baseOctave ),
56 m_numKeys( numKeys ),
57 m_startKey( startKey ),
58 m_minNote( 0 ),
59 m_maxNote( 127 ),
60 m_transpose( 0 ),
61 m_showLabels( ShowNever ),
62 m_alterations( ShowSharps ),
63 m_octave( OctaveC4 ),
64 m_orientation( HorizontalOrientation ),
65 m_rawkbd( false ),
66 m_keyboardEnabled( true ),
67 m_mouseEnabled( true ),
68 m_touchEnabled( true ),
69 m_mousePressed( false ),
70 m_velocity( 100 ),
71 m_channel( 0 ),
72 m_velocityTint( true ),
73 m_handler( nullptr ),
74 m_keybdMap( nullptr ),
75 m_showColorScale( false ),
76 m_hilightPalette(PianoPalette(PAL_SINGLE)),
77 m_backgroundPalette(PianoPalette(PAL_KEYS)),
78 m_foregroundPalette(PianoPalette(PAL_FONT)),
79 m_useKeyPix( true ),
80 m_usingNativeFilter( false ),
81 m_octaveSubscript( true )
82 { }
83
84 void saveData(QByteArray& buffer)
85 {
86 QDataStream ds(&buffer, QIODevice::WriteOnly);
87 ds << m_minNote;
88 ds << m_maxNote;
89 ds << m_transpose;
90 ds << m_showLabels;
91 ds << m_alterations;
92 ds << m_octave;
93 ds << m_orientation;
94 ds << m_rawkbd;
95 ds << m_keyboardEnabled;
96 ds << m_mouseEnabled;
97 ds << m_touchEnabled;
98 ds << m_mousePressed;
99 ds << m_velocity;
100 ds << m_channel;
101 ds << m_velocityTint;
102 ds << m_noteNames;
103 ds << m_names_s;
104 ds << m_names_f;
105 ds << m_showColorScale;
106 ds << m_hilightPalette;
107 ds << m_backgroundPalette;
108 ds << m_foregroundPalette;
109 ds << m_useKeyPix;
110 ds << m_keyPix[0];
111 ds << m_keyPix[1];
112 ds << m_usingNativeFilter;
113 ds << m_octaveSubscript;
114 }
115
116 void loadData(QByteArray& buffer)
117 {
118 quint32 u;
119 QDataStream ds(&buffer, QIODevice::ReadOnly);
120 ds >> m_minNote;
121 ds >> m_maxNote;
122 ds >> m_transpose;
123 ds >> u; m_showLabels = LabelVisibility(u);
124 ds >> u; m_alterations = LabelAlteration(u);
125 ds >> u; m_octave = LabelCentralOctave(u);
126 ds >> u; m_orientation = LabelOrientation(u);
127 ds >> m_rawkbd;
128 ds >> m_keyboardEnabled;
129 ds >> m_mouseEnabled;
130 ds >> m_touchEnabled;
131 ds >> m_mousePressed;
132 ds >> m_velocity;
133 ds >> m_channel;
134 ds >> m_velocityTint;
135 ds >> m_noteNames;
136 ds >> m_names_s;
137 ds >> m_names_f;
138 ds >> m_showColorScale;
139 ds >> m_hilightPalette;
140 ds >> m_backgroundPalette;
141 ds >> m_foregroundPalette;
142 ds >> m_useKeyPix;
143 ds >> m_keyPix[0];
144 ds >> m_keyPix[1];
145 ds >> m_usingNativeFilter;
146 ds >> m_octaveSubscript;
147 }
148
149 QString noteName( PianoKey* key, bool richText )
150 {
151 Q_ASSERT(key != nullptr);
152 int note = key->getNote();
153 int num = (note + m_transpose + 12) % 12;
154 int adj = ((note + m_transpose < 0) ? 2 : 1) - m_octave + 1;
155 int oct = m_baseOctave + ((note + m_transpose) / 12) - adj;
156 QString nameMask = QLatin1String(richText && m_octaveSubscript ? "%1<sub>%2</sub>" : "%1%2");
157 if (m_noteNames.isEmpty()) {
158 QString name;
159 if (!m_names_f.isEmpty() && !m_names_s.isEmpty()) {
160 switch(m_alterations) {
161 case ShowFlats:
162 name = m_names_f.value(num);
163 break;
164 case ShowSharps:
165 name = m_names_s.value(num);
166 break;
167 case ShowNothing:
168 if (key->isBlack()) {
169 return QString();
170 }
171 name = m_names_s.value(num);
172 break;
173 default:
174 break;
175 }
176 }
177 if (m_octave==OctaveNothing) {
178 return name;
179 } else {
180 return nameMask.arg(name).arg(oct);
181 }
182 } else {
183 if (m_noteNames.length() == 128) {
184 int n = m_baseOctave*12 + note + m_transpose;
185 //qDebug() << Q_FUNC_INFO << n << note;
186 if (n >= 0 && n < m_noteNames.length()) {
187 return m_noteNames.value(n);
188 }
189 } else if (m_noteNames.length() >= 12) {
190 if (m_octave==OctaveNothing) {
191 return m_noteNames.value(num);
192 } else {
193 return nameMask.arg(m_noteNames.value(num)).arg(oct);
194 }
195 }
196 return QString();
197 }
198 }
199
200 int m_baseOctave;
201 int m_numKeys;
202 int m_startKey;
203 int m_minNote;
204 int m_maxNote;
205 int m_transpose;
206 LabelVisibility m_showLabels;
207 LabelAlteration m_alterations;
208 LabelCentralOctave m_octave;
209 LabelOrientation m_orientation;
210 bool m_rawkbd;
211 bool m_keyboardEnabled;
212 bool m_mouseEnabled;
213 bool m_touchEnabled;
214 bool m_mousePressed;
215 int m_velocity;
216 int m_channel;
217 bool m_velocityTint;
218 PianoHandler *m_handler;
219 KeyboardMap *m_keybdMap;
220 QHash<int, PianoKey *> m_keys;
221 QMap<int, KeyLabel *> m_labels;
222 QStringList m_noteNames;
223 QStringList m_names_s;
224 QStringList m_names_f;
225 bool m_showColorScale;
226 PianoPalette m_hilightPalette;
227 PianoPalette m_backgroundPalette;
228 PianoPalette m_foregroundPalette;
229 bool m_useKeyPix;
230 QPixmap m_keyPix[2];
231 bool m_usingNativeFilter;
232 bool m_octaveSubscript;
233 /* not serialized */
234 PianoKeybd* m_view;
235 QMap<int, PianoKey *> m_touched;
236};
237
238const int KEYWIDTH = 180;
239const int KEYHEIGHT = 720;
240
241static qreal sceneWidth(int keys) {
242 return KEYWIDTH * qCeil( keys * 7.0 / 12.0 );
243}
244
253PianoScene::PianoScene ( const int baseOctave,
254 const int numKeys,
255 const int startKey,
256 const QColor& keyPressedColor,
257 QObject * parent )
258 : QGraphicsScene( QRectF(0, 0, sceneWidth(numKeys), KEYHEIGHT), parent ),
259 d(new PianoScenePrivate(baseOctave, numKeys, startKey))
260{
261 if (keyPressedColor.isValid()) {
262 setKeyPressedColor(keyPressedColor);
263 }
264 QBrush hilightBrush(getKeyPressedColor());
265 d->m_view = dynamic_cast<PianoKeybd*>(parent);
266 if (d->m_view != nullptr) {
267 setFont(d->m_view->font());
268 }
269 int upperLimit = d->m_numKeys + d->m_startKey;
270 int adj = d->m_startKey % 12;
271 if (adj >= 5) adj++;
272 for(int i = d->m_startKey; i < upperLimit; ++i)
273 {
274 float x = 0;
275 PianoKey* key = nullptr;
276 KeyLabel* lbl = nullptr;
277 int ocs = i / 12 * 7;
278 int j = i % 12;
279 if (j >= 5) j++;
280 if ((j % 2) == 0) {
281 x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH;
282 key = new PianoKey( QRectF(x, 0, KEYWIDTH, KEYHEIGHT), false, i );
283 lbl = new KeyLabel(key);
284 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(0));
285 } else {
286 x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH + KEYWIDTH * 0.6 + 1;
287 key = new PianoKey( QRectF( x, 0, KEYWIDTH * 0.8 - 1, KEYHEIGHT * 0.6 ), true, i );
288 key->setZValue( 1 );
289 lbl = new KeyLabel(key);
290 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(1));
291 }
292 addItem( key );
293 lbl->setFont(font());
294 key->setAcceptTouchEvents(true);
295 key->setPressedBrush(hilightBrush);
296 d->m_keys.insert(i, key);
297 d->m_labels.insert(i, lbl);
298 }
299 hideOrShowKeys();
300 retranslate();
301}
302
306PianoScene::~PianoScene()
307{ }
308
313QSize PianoScene::sizeHint() const
314{
315 return {static_cast<int>(sceneWidth(d->m_numKeys)), KEYHEIGHT};
316}
317
322void PianoScene::setKeyboardMap(KeyboardMap *map)
323{
324 d->m_keybdMap = map;
325}
326
331KeyboardMap *PianoScene::getKeyboardMap() const
332{
333 return d->m_keybdMap;
334}
335
343PianoHandler *PianoScene::getPianoHandler() const
344{
345 return d->m_handler;
346}
347
356void PianoScene::setPianoHandler(PianoHandler *handler)
357{
358 d->m_handler = handler;
359}
360
365PianoPalette PianoScene::getHighlightPalette()
366{
367 return d->m_hilightPalette;
368}
369
374void PianoScene::displayKeyOn(PianoKey* key)
375{
376 key->setPressed(true);
377 int n = key->getNote() + d->m_baseOctave*12 + d->m_transpose;
378 QString s = QString("#%1 (%2)").arg(n).arg(d->noteName(key, false));
379 Q_EMIT signalName(s);
380 KeyLabel* lbl = dynamic_cast<KeyLabel*>(key->childItems().constFirst());
381 if (lbl != nullptr) {
382 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 3 : 2));
383 if (d->m_showLabels == ShowActivated) {
384 lbl->setVisible(true);
385 }
386 }
387}
388
395void PianoScene::showKeyOn( PianoKey* key, QColor color, int vel )
396{
397 //qDebug() << Q_FUNC_INFO << key->getNote() << vel << color << d->m_velocityTint;
398 if (d->m_velocityTint && (vel >= 0) && (vel < 128) && color.isValid() ) {
399 QBrush hilightBrush(color.lighter(200 - vel));
400 key->setPressedBrush(hilightBrush);
401 } else if (color.isValid()) {
402 key->setPressedBrush(color);
403 }
404 displayKeyOn(key);
405}
406
412void PianoScene::showKeyOn( PianoKey* key, int vel )
413{
414 setHighlightColorFromPolicy(key, vel);
415 displayKeyOn(key);
416}
417
423void PianoScene::showKeyOff( PianoKey* key, int vel)
424{
425 Q_UNUSED(vel)
426 key->setPressed(false);
427 Q_EMIT signalName(QString());
428 KeyLabel* lbl = dynamic_cast<KeyLabel*>(key->childItems().constFirst());
429 if (lbl != nullptr) {
430 lbl->restoreColor();
431 if (d->m_showLabels == ShowActivated) {
432 lbl->setVisible(false);
433 }
434 }
435}
436
443void PianoScene::showNoteOn( const int note, QColor color, int vel )
444{
445 //qDebug() << Q_FUNC_INFO << note << vel << color;
446 int n = note - d->m_baseOctave*12 - d->m_transpose;
447 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n) && color.isValid())
448 showKeyOn(d->m_keys.value(n), color, vel);
449}
450
456void PianoScene::showNoteOn( const int note, int vel )
457{
458 //qDebug() << Q_FUNC_INFO << note << vel;
459 int n = note - d->m_baseOctave*12 - d->m_transpose;
460 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
461 showKeyOn(d->m_keys.value(n), vel);
462 }
463}
464
470void PianoScene::showNoteOff( const int note, int vel )
471{
472 int n = note - d->m_baseOctave*12 - d->m_transpose;
473 if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) {
474 showKeyOff(d->m_keys.value(n), vel);
475 }
476}
477
483int PianoScene::baseOctave() const { return d->m_baseOctave; }
484
492void PianoScene::triggerNoteOn( const int note, const int vel )
493{
494 int n = d->m_baseOctave*12 + note + d->m_transpose;
495 if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
496 if (d->m_handler != nullptr) {
497 d->m_handler->noteOn(n, vel);
498 } else {
499 Q_EMIT noteOn(n, vel);
500 }
501 }
502}
503
511void PianoScene::triggerNoteOff( const int note, const int vel )
512{
513 int n = d->m_baseOctave*12 + note + d->m_transpose;
514 if ((n >= d->m_minNote) && (n <= d->m_maxNote)) {
515 if (d->m_handler != nullptr) {
516 d->m_handler->noteOff(n, vel);
517 } else {
518 Q_EMIT noteOff(n, vel);
519 }
520 }
521}
522
529void PianoScene::setHighlightColorFromPolicy(PianoKey* key, int vel)
530{
531 QColor c;
532 //qDebug() << Q_FUNC_INFO << key->getNote() << vel << d->m_velocityTint;
533 switch (d->m_hilightPalette.paletteId()) {
534 case PAL_SINGLE:
535 c = d->m_hilightPalette.getColor(0);
536 break;
537 case PAL_DOUBLE:
538 c = d->m_hilightPalette.getColor(key->getType());
539 break;
540 case PAL_CHANNELS:
541 c = d->m_hilightPalette.getColor(d->m_channel);
542 break;
543 case PAL_HISCALE:
544 c = d->m_hilightPalette.getColor(key->getDegree());
545 break;
546 default:
547 return;
548 }
549 if (c.isValid()) {
550 if (d->m_velocityTint && (vel >= 0) && (vel < 128)) {
551 QBrush h(c.lighter(200 - vel));
552 key->setPressedBrush(h);
553 } else {
554 key->setPressedBrush(c);
555 }
556 }
557}
558
563void PianoScene::keyOn( PianoKey* key )
564{
565 triggerNoteOn(key->getNote(), d->m_velocity);
566 showKeyOn(key, d->m_velocity);
567}
568
573void PianoScene::keyOff( PianoKey* key )
574{
575 triggerNoteOff(key->getNote(), 0);
576 showKeyOff(key, 0);
577}
578
584void PianoScene::keyOn( PianoKey* key, qreal pressure )
585{
586 int vel = d->m_velocity * pressure;
587 triggerNoteOn(key->getNote(), vel);
588 showKeyOn(key, vel);
589}
590
596void PianoScene::keyOff( PianoKey* key, qreal pressure )
597{
598 int vel = d->m_velocity * pressure;
599 triggerNoteOff(key->getNote(), vel);
600 showKeyOff(key, vel);
601}
602
607void PianoScene::keyOn(const int note)
608{
609 if (d->m_keys.contains(note))
610 keyOn(d->m_keys.value(note));
611 else
612 triggerNoteOn(note, d->m_velocity);
613}
614
619void PianoScene::keyOff(const int note)
620{
621 if (d->m_keys.contains(note))
622 keyOff(d->m_keys.value(note));
623 else
624 triggerNoteOff(note, d->m_velocity);
625}
626
631bool PianoScene::getRawKeyboardMode() const
632{
633 return d->m_rawkbd;
634}
635
641PianoKey* PianoScene::getKeyForPos( const QPointF& p ) const
642{
643 PianoKey* key = nullptr;
644 QList<QGraphicsItem *> ptitems = this->items(p, Qt::IntersectsItemShape, Qt::DescendingOrder);
645 foreach(QGraphicsItem *itm, ptitems) {
646 key = dynamic_cast<PianoKey*>(itm);
647 if (key != nullptr)
648 break;
649 }
650 return key;
651}
652
657void PianoScene::mouseMoveEvent ( QGraphicsSceneMouseEvent * mouseEvent )
658{
659 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
660 if (d->m_mousePressed) {
661 PianoKey* key = getKeyForPos(mouseEvent->scenePos());
662 PianoKey* lastkey = getKeyForPos(mouseEvent->lastScenePos());
663 if ((lastkey != nullptr) && (lastkey != key) && lastkey->isPressed()) {
664 keyOff(lastkey);
665 }
666 if ((key != nullptr) && !key->isPressed()) {
667 keyOn(key);
668 }
669 mouseEvent->accept();
670 return;
671 }
672 }
673}
674
679void PianoScene::mousePressEvent ( QGraphicsSceneMouseEvent * mouseEvent )
680{
681 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
682 PianoKey* key = getKeyForPos(mouseEvent->scenePos());
683 if (key != nullptr && !key->isPressed()) {
684 keyOn(key);
685 d->m_mousePressed = true;
686 mouseEvent->accept();
687 return;
688 }
689 }
690}
691
696void PianoScene::mouseReleaseEvent ( QGraphicsSceneMouseEvent * mouseEvent )
697{
698 if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) {
699 d->m_mousePressed = false;
700 PianoKey* key = getKeyForPos(mouseEvent->scenePos());
701 if (key != nullptr && key->isPressed()) {
702 keyOff(key);
703 mouseEvent->accept();
704 return;
705 }
706 }
707}
708
714int PianoScene::getNoteFromKey( const int key ) const
715{
716 if (d->m_keybdMap != nullptr) {
717 KeyboardMap::ConstIterator it = d->m_keybdMap->constFind(key);
718 if ((it != d->m_keybdMap->constEnd()) && (it.key() == key)) {
719 int note = it.value();
720 return note;
721 }
722 }
723 return -1;
724}
725
731PianoKey* PianoScene::getPianoKey( const int key ) const
732{
733 int note = getNoteFromKey(key);
734 if (d->m_keys.contains(note))
735 return d->m_keys.value(note);
736 return nullptr;
737}
738
743void PianoScene::keyPressEvent ( QKeyEvent * keyEvent )
744{
745 if ( d->m_keyboardEnabled &&
746 !d->m_usingNativeFilter &&
747 !keyEvent->isAutoRepeat() ) // ignore auto-repeats
748 {
749 int keyid = d->m_rawkbd ?
750#if defined(Q_OS_MACOS)
751 keyEvent->nativeVirtualKey()
752#else
753 keyEvent->nativeScanCode()
754#endif
755 : keyEvent->key();
756 int note = getNoteFromKey( keyid );
757 if (note > -1) {
758 keyOn(note);
759 keyEvent->accept();
760 return;
761 }
762 }
763 keyEvent->ignore();
764}
765
770void PianoScene::keyReleaseEvent ( QKeyEvent * keyEvent )
771{
772 if ( d->m_keyboardEnabled &&
773 !d->m_usingNativeFilter &&
774 !keyEvent->isAutoRepeat() ) // ignore auto-repeats
775 {
776 int keyid = d->m_rawkbd ?
777#if defined(Q_OS_MACOS)
778 keyEvent->nativeVirtualKey()
779#else
780 keyEvent->nativeScanCode()
781#endif
782 : keyEvent->key();
783 int note = getNoteFromKey( keyid );
784 if (note > -1) {
785 keyOff(note);
786 keyEvent->accept();
787 return;
788 }
789 }
790 keyEvent->ignore();
791}
792
799bool PianoScene::event(QEvent *event)
800{
801 return QGraphicsScene::event(event);
802}
803
807void PianoScene::allKeysOff()
808{
809 foreach(PianoKey* key, d->m_keys) {
810 key->setPressed(false);
811 }
812}
813
820void PianoScene::setKeyPressedColor(const QColor& color)
821{
822 if (color.isValid()) {
823 d->m_hilightPalette = PianoPalette(PAL_SINGLE);
824 d->m_hilightPalette.setColor(0, color);
825 QBrush hilightBrush(color);
826 for (PianoKey *key : std::as_const(d->m_keys)) {
827 key->setPressedBrush(hilightBrush);
828 }
829 }
830}
831
835void PianoScene::resetKeyPressedColor()
836{
837 d->m_hilightPalette.resetColors();
838 QBrush hilightBrush(getKeyPressedColor());
839 for (PianoKey *key : std::as_const(d->m_keys)) {
840 key->setPressedBrush(hilightBrush);
841 }
842}
843
848int PianoScene::getMinNote() const
849{
850 return d->m_minNote;
851}
852
856void PianoScene::hideOrShowKeys()
857{
858 for (PianoKey *key : std::as_const(d->m_keys)) {
859 int n = d->m_baseOctave*12 + key->getNote() + d->m_transpose;
860 bool b = !(n > d->m_maxNote) && !(n < d->m_minNote);
861 key->setVisible(b);
862 }
863}
864
869void PianoScene::setMinNote(const int note)
870{
871 if (d->m_minNote != note) {
872 d->m_minNote = note;
873 hideOrShowKeys();
874 }
875}
876
881int PianoScene::getMaxNote() const
882{
883 return d->m_maxNote;
884}
885
890void PianoScene::setMaxNote(const int note)
891{
892 if (d->m_maxNote != note) {
893 d->m_maxNote = note;
894 hideOrShowKeys();
895 }
896}
897
902int PianoScene::getTranspose() const
903{
904 return d->m_transpose;
905}
906
911void PianoScene::setBaseOctave(const int base)
912{
913 if (d->m_baseOctave != base) {
914 d->m_baseOctave = base;
915 hideOrShowKeys();
916 refreshLabels();
917 }
918}
919
924int PianoScene::numKeys() const
925{
926 return d->m_numKeys;
927}
928
933int PianoScene::startKey() const
934{
935 return d->m_startKey;
936}
937
943bool PianoScene::isOctaveStart(const int note)
944{
945 return (note + d->m_transpose + 12) % 12 == 0;
946}
947
953QString PianoScene::noteName( PianoKey* key )
954{
955 Q_ASSERT(key != nullptr);
956 return d->noteName(key, true);
957}
958
962void PianoScene::refreshLabels()
963{
964 for (KeyLabel *lbl : std::as_const(d->m_labels)) {
965 PianoKey* key = dynamic_cast<PianoKey*>(lbl->parentItem());
966 if (key != nullptr) {
967 lbl->setVisible(false);
968 lbl->setFont(font());
969 lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 1 : 0));
970 lbl->setOrientation(d->m_orientation);
971 lbl->setHtml(d->noteName(key, true));
972 lbl->adjust();
973 lbl->setVisible((d->m_showLabels == ShowAlways) ||
974 (d->m_showLabels == ShowMinimum && isOctaveStart(key->getNote())));
975 }
976 }
977}
978
982void PianoScene::refreshKeys()
983{
984 for (PianoKey *key : std::as_const(d->m_keys)) {
985 if (d->m_showColorScale && (d->m_backgroundPalette.paletteId() == PAL_SCALE)) {
986 int degree = key->getNote() % 12;
987 key->setBrush(d->m_backgroundPalette.getColor(degree));
988 } else {
989 key->setBrush(d->m_backgroundPalette.getColor(key->isBlack() ? 1 : 0));
990 }
991 key->setPressed(false);
992 }
993}
994
1000void PianoScene::setShowLabels(const LabelVisibility show)
1001{
1002 //qDebug() << Q_FUNC_INFO << show;
1003 if (d->m_showLabels != show) {
1004 d->m_showLabels = show;
1005 refreshLabels();
1006 }
1007}
1008
1014LabelAlteration PianoScene::alterations() const
1015{
1016 return d->m_alterations;
1017}
1018
1024void PianoScene::setAlterations(const LabelAlteration use)
1025{
1026 if (d->m_alterations != use) {
1027 d->m_alterations = use;
1028 refreshLabels();
1029 }
1030}
1031
1036LabelCentralOctave PianoScene::getOctave() const
1037{
1038 return d->m_octave;
1039}
1040
1045void PianoScene::setOrientation(const LabelOrientation orientation)
1046{
1047 if (d->m_orientation != orientation) {
1048 d->m_orientation = orientation;
1049 refreshLabels();
1050 }
1051}
1052
1053bool PianoScene::isKeyboardEnabled() const
1054{
1055 return d->m_keyboardEnabled;
1056}
1057
1058void PianoScene::setOctave(const LabelCentralOctave octave)
1059{
1060 if (d->m_octave != octave) {
1061 d->m_octave = octave;
1062 refreshLabels();
1063 }
1064}
1065
1066LabelOrientation PianoScene::getOrientation() const
1067{
1068 return d->m_orientation;
1069}
1070
1075void PianoScene::setTranspose(const int transpose)
1076{
1077 if (d->m_transpose != transpose && transpose > -12 && transpose < 12) {
1078 d->m_transpose = transpose;
1079 hideOrShowKeys();
1080 refreshLabels();
1081 }
1082}
1083
1089LabelVisibility PianoScene::showLabels() const
1090{
1091 return d->m_showLabels;
1092}
1093
1098void PianoScene::setRawKeyboardMode(bool b)
1099{
1100 if (d->m_rawkbd != b) {
1101 d->m_rawkbd = b;
1102 }
1103}
1104
1109QStringList PianoScene::customNoteNames() const
1110{
1111 return d->m_noteNames;
1112}
1113
1118QStringList PianoScene::standardNoteNames() const
1119{
1120 return d->m_names_s;
1121}
1122
1127int PianoScene::getVelocity()
1128{
1129 return d->m_velocity;
1130}
1131
1136void PianoScene::setVelocity(const int velocity)
1137{
1138 d->m_velocity = velocity;
1139}
1140
1146int PianoScene::getChannel() const
1147{
1148 return d->m_channel;
1149}
1150
1156void PianoScene::setChannel(const int channel)
1157{
1158 d->m_channel = channel;
1159}
1160
1165void PianoScene::useCustomNoteNames(const QStringList& names)
1166{
1167 //qDebug() << Q_FUNC_INFO << names;
1168 d->m_noteNames = names;
1169 refreshLabels();
1170}
1171
1175void PianoScene::useStandardNoteNames()
1176{
1177 //qDebug() << Q_FUNC_INFO;
1178 d->m_noteNames.clear();
1179 refreshLabels();
1180}
1181
1186void PianoScene::setKeyboardEnabled(const bool enable)
1187{
1188 if (enable != d->m_keyboardEnabled) {
1189 d->m_keyboardEnabled = enable;
1190 }
1191}
1192
1197bool PianoScene::isMouseEnabled() const
1198{
1199 return d->m_mouseEnabled;
1200}
1201
1206void PianoScene::setMouseEnabled(const bool enable)
1207{
1208 if (enable != d->m_mouseEnabled) {
1209 d->m_mouseEnabled = enable;
1210 }
1211}
1212
1217bool PianoScene::isTouchEnabled() const
1218{
1219 return d->m_touchEnabled;
1220}
1221
1226void PianoScene::setTouchEnabled(const bool enable)
1227{
1228 if (enable != d->m_touchEnabled) {
1229 d->m_touchEnabled = enable;
1230 }
1231}
1232
1237bool PianoScene::velocityTint() const
1238{
1239 return d->m_velocityTint;
1240}
1241
1246void PianoScene::setVelocityTint(const bool enable)
1247{
1248 //qDebug() << Q_FUNC_INFO << enable;
1249 d->m_velocityTint = enable;
1250}
1251
1255void PianoScene::retranslate()
1256{
1257 d->m_names_s = QStringList{
1258 tr("C"),
1259 tr("C♯"),
1260 tr("D"),
1261 tr("D♯"),
1262 tr("E"),
1263 tr("F"),
1264 tr("F♯"),
1265 tr("G"),
1266 tr("G♯"),
1267 tr("A"),
1268 tr("A♯"),
1269 tr("B")};
1270 d->m_names_f = QStringList{
1271 tr("C"),
1272 tr("D♭"),
1273 tr("D"),
1274 tr("E♭"),
1275 tr("E"),
1276 tr("F"),
1277 tr("G♭"),
1278 tr("G"),
1279 tr("A♭"),
1280 tr("A"),
1281 tr("B♭"),
1282 tr("B")};
1283 refreshLabels();
1284}
1285
1290void PianoScene::setShowColorScale(const bool show)
1291{
1292 if (d->m_showColorScale != show) {
1293 d->m_showColorScale = show;
1294 refreshKeys();
1295 invalidate();
1296 }
1297}
1298
1303QColor PianoScene::getKeyPressedColor() const
1304{
1305 return d->m_hilightPalette.getColor(0);
1306}
1307
1312void PianoScene::setHighlightPalette( const PianoPalette& p )
1313{
1314 if (d->m_hilightPalette != p) {
1315 d->m_hilightPalette = p;
1316 refreshKeys();
1317 invalidate();
1318 }
1319}
1320
1325PianoPalette PianoScene::getBackgroundPalette()
1326{
1327 return d->m_backgroundPalette;
1328}
1329
1334void PianoScene::setBackgroundPalette(const PianoPalette& p )
1335{
1336 if (d->m_backgroundPalette != p) {
1337 d->m_backgroundPalette = p;
1338 refreshKeys();
1339 invalidate();
1340 }
1341}
1342
1347PianoPalette PianoScene::getForegroundPalette()
1348{
1349 return d->m_foregroundPalette;
1350}
1351
1356void PianoScene::setForegroundPalette(const PianoPalette &p)
1357{
1358 if (d->m_foregroundPalette != p) {
1359 d->m_foregroundPalette = p;
1360 refreshLabels();
1361 invalidate();
1362 }
1363}
1364
1369bool PianoScene::showColorScale() const
1370{
1371 return d->m_showColorScale;
1372}
1373
1374void PianoScene::setKeyPicture(const bool natural, const QPixmap &pix)
1375{
1376 d->m_keyPix[int(natural)] = pix;
1377 for (PianoKey *key : std::as_const(d->m_keys)) {
1378 if (key->isBlack() == !natural) {
1379 key->setPixmap(pix);
1380 }
1381 }
1382}
1383
1384QPixmap PianoScene::getKeyPicture(const bool natural)
1385{
1386 return d->m_keyPix[int(natural)];
1387}
1388
1389void PianoScene::setUseKeyPictures(const bool enable)
1390{
1391 d->m_useKeyPix = enable;
1392 for (PianoKey *key : std::as_const(d->m_keys)) {
1393 key->setUsePixmap(enable);
1394 }
1395}
1396
1397bool PianoScene::getUseKeyPictures() const
1398{
1399 return d->m_useKeyPix;
1400}
1401
1402void PianoScene::saveData(QByteArray &ba)
1403{
1404 d->saveData(ba);
1405}
1406
1407void PianoScene::loadData(QByteArray &ba)
1408{
1409 d->loadData(ba);
1410}
1411
1417bool PianoScene::touchScreenEvent(QTouchEvent *touchEvent)
1418{
1419 switch(touchEvent->type()) {
1420 case QEvent::TouchEnd:
1421 case QEvent::TouchCancel:
1422 {
1423 foreach(PianoKey *key, d->m_touched) {
1424 //qDebug() << "key:" << key->getNote() << key->isPressed();
1425 if (key->isPressed()) {
1426 keyOff(key);
1427 }
1428 }
1429 d->m_touched.clear();
1430 touchEvent->accept();
1431 return true;
1432 } /* case (end and cancel touch events) */
1433 case QEvent::TouchBegin:
1434 case QEvent::TouchUpdate:
1435 {
1436 QList<QTouchEvent::TouchPoint> touchPoints =
1437 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1438 touchEvent->touchPoints();
1439 #else
1440 touchEvent->points();
1441 #endif
1442 bool hasPressure =
1443 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1444 touchEvent->device()->capabilities().testFlag(QTouchDevice::Pressure);
1445 #else
1446 touchEvent->device()->capabilities().testFlag(QInputDevice::Capability::Pressure);
1447 #endif
1448 foreach(const QTouchEvent::TouchPoint& touchPoint, touchPoints) {
1449 //qDebug() << touchPoint.id() << touchPoint.state();
1450 switch (touchPoint.state()) {
1451#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1452 case Qt::TouchPointReleased:
1453#else
1454 case QEventPoint::Released:
1455#endif
1456 {
1457 PianoKey* key = d->m_touched.value(touchPoint.id());
1458 if (key != nullptr) {
1459 //qDebug() << "key:" << key->getNote() << key->isPressed();
1460 if (key->isPressed()) {
1461 if (hasPressure) {
1462 keyOff(key, touchPoint.pressure());
1463 } else {
1464 keyOff(key);
1465 }
1466 }
1467 d->m_touched.remove(touchPoint.id());
1468 }
1469 break;
1470 } /* case released state */
1471#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1472 case Qt::TouchPointPressed:
1473#else
1474 case QEventPoint::Pressed:
1475#endif
1476 {
1477 PianoKey* key = getKeyForPos( d->m_view->mapToScene(
1478 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1479 touchPoint.pos().toPoint()
1480 #else
1481 touchPoint.position().toPoint()
1482 #endif
1483 ));
1484 if (key != nullptr) {
1485 //qDebug() << "key:" << key->getNote() << key->isPressed();
1486 if (!key->isPressed()) {
1487 if (hasPressure) {
1488 keyOn(key, touchPoint.pressure());
1489 } else {
1490 keyOn(key);
1491 }
1492 key->ensureVisible();
1493 }
1494 d->m_touched[touchPoint.id()] = key;
1495 }
1496 break;
1497 } /* case pressed state */
1498#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1499 case Qt::TouchPointMoved:
1500#else
1501 case QEventPoint::Updated:
1502#endif
1503 {
1504 PianoKey* key = getKeyForPos( d->m_view->mapToScene(
1505 #if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
1506 touchPoint.pos().toPoint()
1507 #else
1508 touchPoint.position().toPoint()
1509 #endif
1510 ));
1511 PianoKey* lastkey = d->m_touched.value(touchPoint.id());
1512 if ((lastkey != nullptr) && (lastkey != key)) {
1513 //qDebug() << "lastkey:" << lastkey->getNote() << lastkey->isPressed();
1514 if (lastkey->isPressed()) {
1515 if (hasPressure) {
1516 keyOff(lastkey, touchPoint.pressure());
1517 } else {
1518 keyOff(lastkey);
1519 }
1520 }
1521 d->m_touched.remove(touchPoint.id());
1522 }
1523 if (key != nullptr) {
1524 //qDebug() << "key:" << key->getNote() << key->isPressed();
1525 if (!key->isPressed()) {
1526 if (hasPressure) {
1527 keyOn(key, touchPoint.pressure());
1528 } else {
1529 keyOn(key);
1530 }
1531 }
1532 d->m_touched[touchPoint.id()] = key;
1533 }
1534 break;
1535 } /* case updated state */
1536 default:
1537 break;
1538 } /* switch touchpoint state */
1539 } /* foreach touchPoint */
1540 touchEvent->accept();
1541 return true;
1542 } /* case (begin and update touch events) */
1543 default:
1544 break;
1545 } /* switch touchEvent->type() */
1546 return false;
1547}
1548
1553void PianoScene::setUsingNativeFilter(const bool newState)
1554{
1555 if (newState != d->m_usingNativeFilter) {
1556 d->m_usingNativeFilter = newState;
1557 }
1558}
1559
1564bool PianoScene::isUsingNativeFilter() const
1565{
1566 return d->m_usingNativeFilter;
1567}
1568
1573void PianoScene::setOctaveSubscript(const bool enable)
1574{
1575 if (d->m_octaveSubscript != enable) {
1576 d->m_octaveSubscript = enable;
1577 refreshLabels();
1578 }
1579}
1580
1585bool PianoScene::octaveSubscript() const
1586{
1587 return d->m_octaveSubscript;
1588}
1589
1590} // namespace widgets
1591} // namespace drumstick
The QEvent class is the base class of all event classes.
The QGraphicsScene class provides a surface for managing a large number of 2D graphical items.
The QObject class is the base class of all Qt objects.
The PianoHandler class callbacks.
Definition pianokeybd.h:84
The PianoKeybd class.
Definition pianokeybd.h:176
The PianoPalette class.
PianoScene(const int baseOctave, const int numKeys, const int startKey, const QColor &keyPressedColor=QColor(), QObject *parent=nullptr)
Constructor.
int baseOctave() const
Returns the base octave number.
int numKeys() const
Returns the number of keys that will be displayed.
QColor getKeyPressedColor() const
Returns the single highlight palette color.
int startKey() const
Returns the first key number that will be displayed.
LabelAlteration
Labels for Alterations.
Definition pianokeybd.h:131
LabelCentralOctave
Labels Central Octave.
Definition pianokeybd.h:161
LabelVisibility
Labels Visibility.
Definition pianokeybd.h:120
LabelOrientation
Labels Orientation.
Definition pianokeybd.h:141
QHash< int, int > KeyboardMap
KeyboardMap.
Definition pianokeybd.h:108
@ ShowSharps
Show sharps on black keys.
Definition pianokeybd.h:132
@ ShowNothing
Do not show names on black keys.
Definition pianokeybd.h:134
@ ShowFlats
Show flats on black keys.
Definition pianokeybd.h:133
@ OctaveNothing
Don't show octave numbers.
Definition pianokeybd.h:162
@ OctaveC4
Central C, MIDI note #60 is C4.
Definition pianokeybd.h:164
@ ShowAlways
Show always note names.
Definition pianokeybd.h:124
@ ShowMinimum
Show only note C names.
Definition pianokeybd.h:122
@ ShowActivated
Show names when notes are activated.
Definition pianokeybd.h:123
@ ShowNever
Don't show note names.
Definition pianokeybd.h:121
@ HorizontalOrientation
Show horizontal names.
Definition pianokeybd.h:142
@ PAL_SCALE
Background colors for each chromatic scale note.
@ PAL_SINGLE
Single highlihgting color for all keys.
@ PAL_HISCALE
Highlighting colors for each chromatic scale note.
@ PAL_CHANNELS
Different highlihgting colors for each channel.
@ PAL_KEYS
Two background colors (naturals/alterations)
@ PAL_DOUBLE
Two highlihgting colors (naturals/alterations)
@ PAL_FONT
Foreground font colors for names.
Drumstick common.
Piano Keyboard Widget.
PianoScene class declaration.