Skip to content
Snippets Groups Projects
Commit 6c643a69 authored by SeeLook's avatar SeeLook
Browse files

Implemented piano keyboard, clicking keys adds note to the score

parent 4998fc7c
No related branches found
No related tags found
No related merge requests found
picts/pianokey.png

6.46 KiB

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
width="52.44693"
height="161.68961"
id="svg4626"
sodipodi:version="0.32"
inkscape:version="0.92.1 r"
sodipodi:docname="pianokey.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:export-filename="../../picts/pianokey.png"
inkscape:export-xdpi="234.52342"
inkscape:export-ydpi="234.52342">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
<dc:title></dc:title>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
<cc:requires
rdf:resource="http://web.resource.org/cc/SourceCode" />
</cc:License>
</rdf:RDF>
</metadata>
<sodipodi:namedview
inkscape:window-height="1039"
inkscape:window-width="1920"
inkscape:pageshadow="2"
inkscape:pageopacity="1"
guidetolerance="0.4"
gridtolerance="0.4"
objecttolerance="0.4"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base"
showborder="true"
inkscape:showpageshadow="false"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="-367.46148"
inkscape:cy="127.28471"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:current-layer="svg4626"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
id="GridFromPre046Settings"
type="xygrid"
originx="-1.024"
originy="-0.95999368"
spacingx="0.10666667"
spacingy="0.10666667"
color="#3f3fff"
empcolor="#3f3fff"
opacity="0.15"
empopacity="0.38"
empspacing="5" />
</sodipodi:namedview>
<defs
id="defs4628">
<linearGradient
id="linearGradient5164">
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop5166" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop5168" />
</linearGradient>
<linearGradient
id="linearGradient3230">
<stop
style="stop-color:#000000;stop-opacity:0"
offset="0"
id="stop3232" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3234" />
</linearGradient>
<linearGradient
id="linearGradient3916">
<stop
style="stop-color:#000000;stop-opacity:0"
offset="0"
id="stop3918" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop3920" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3916"
id="linearGradient5141"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.200802,0,0,1.196659,-269.30906,-322.81977)"
x1="277.40338"
y1="361.15414"
x2="213.72539"
y2="295.93903" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3230"
id="linearGradient5143"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.6939249,0,0,0.92080192,-390.48322,-217.42706)"
x1="261.73914"
y1="416.65976"
x2="236.34105"
y2="392.0029" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5164"
id="linearGradient5145"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0545831,0,0,1.0653024,-410.68998,-310.06594)"
x1="380.41821"
y1="374.1217"
x2="419.6817"
y2="367.00864" />
</defs>
<g
id="g5135"
transform="matrix(0.9800699,0,0,0.9908495,-0.58556755,-0.07976399)">
<rect
id="rect4280"
style="fill:url(#linearGradient5141);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.88976371;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1"
y="0.093986385"
x="8.5333338"
ry="0"
rx="0"
height="142.36931"
width="37.439754" />
<path
sodipodi:nodetypes="ccccccc"
id="path4282"
style="fill:url(#linearGradient5143);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.88976371;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-opacity:1"
d="m 45.421134,143.6376 7.462077,18.02772 c 0,0.88527 -0.688754,1.59799 -1.544224,1.59799 H 3.6775574 c -0.8555013,0 -1.5442241,-0.71272 -1.5442241,-1.59799 l 7.5719281,-18.13251 c 14.6279896,-0.0205 21.4195706,0.0755 35.7158726,0.10475 z"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="cccccc"
id="path4284"
style="fill:url(#linearGradient5145);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.87287581;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1"
d="M 0.73128165,0.15525114 0.59747529,159.39954 c 0,0.84667 0.29061143,1.63388 0.75798171,2.26376 L 8.012514,143.54781 C 7.9745649,95.506362 8.0807462,48.198642 7.9795583,0.15871742 5.5404702,0.16514302 3.1700124,0.15422462 0.73128165,0.15525114 Z"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="cccccccc"
id="path4286"
style="fill:#ffffff;fill-opacity:0.07843137;fill-rule:evenodd;stroke:none;stroke-width:1.88976371;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1"
d="m 46.717221,0.08050061 c -0.01094,0.17468052 -0.03301,0.35330778 -0.03301,0.53437776 V 143.32742 l 6.964617,18.33588 c 0.290119,-0.54256 0.462107,-1.14251 0.462107,-1.80353 V 0.16803924 c 1.1e-5,0.0469058 -0.181876,-0.0205219 -0.177416,-0.0205113 z"
inkscape:connector-curvature="0" />
</g>
</svg>
...@@ -40,6 +40,7 @@ set(LIB_NOOTKACORE_SRC ...@@ -40,6 +40,7 @@ set(LIB_NOOTKACORE_SRC
score/tbeamobject.cpp score/tbeamobject.cpp
instruments/tguitarbg.cpp instruments/tguitarbg.cpp
instruments/tpianobg.cpp
# exam/tqatype.cpp # exam/tqatype.cpp
# exam/tqaunit.cpp # exam/tqaunit.cpp
......
/***************************************************************************
* Copyright (C) 2017 by Tomasz Bojczuk *
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "tpianobg.h"
#include "tpath.h"
#include "music/tnote.h"
#include <QtGui/qpainter.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qscreen.h>
#include <QtCore/qmath.h>
#include <QtCore/qdebug.h>
#include "checktime.h"
TpianoBg::TpianoBg(QQuickItem* parent) :
QQuickPaintedItem(parent),
m_keyWidth(32.0),
m_firstOctave(-2)
{
setAcceptHoverEvents(true);
setRenderTarget(QQuickPaintedItem::FramebufferObject);
// setPerformanceHint(QQuickPaintedItem::FastFBOResizing);
setAntialiasing(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
TpianoBg::~TpianoBg()
{
}
void TpianoBg::setKeyWidth(qreal kw) {
if (m_keyWidth != kw) {
m_keyWidth = kw;
calculateMetrics(width());
update();
}
}
void TpianoBg::setNote(const Tnote& n) {
}
void TpianoBg::setFirstOctave(int firstO) {
auto fo = static_cast<char>(firstO);
if (fo != m_firstOctave) {
}
}
void TpianoBg::setReadOnly(bool ro) {
if (ro != m_readOnly) {
m_readOnly = ro;
setAcceptedMouseButtons(m_readOnly ? Qt::NoButton : Qt::LeftButton);
}
}
void TpianoBg::paint(QPainter* painter) {
CHECKTIME (
int kw = qFloor(m_keyWidth);
auto keyPix = QPixmap(Tpath::img("pianokey")).scaled(qRound(m_keyWidth * 0.8), height() / 2, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
painter->setPen(QPen(Qt::black, m_keyWidth / 16, Qt::SolidLine));
painter->setBrush(Qt::white);
for (int k = 0; k < m_keysNumber; ++k) {
painter->drawRoundedRect(m_margin + k * kw, 1, kw, height() - 2, m_keyWidth / 5, m_keyWidth / 5);
if (k % 7 != 0 && k % 7 != 3)
painter->drawPixmap(m_margin + k * kw - qRound(m_keyWidth * 0.4), kw, keyPix);
}
painter->setBrush(Qt::black);
painter->drawRect(0, 0, width(), kw);
if (m_margin > 0) {
painter->drawRect(0, kw, m_margin, height() - kw);
painter->drawRect(width() - m_margin, kw, m_margin, height() - kw);
}
)
}
//#################################################################################################
//################### PROTECTED ############################################
//#################################################################################################
void TpianoBg::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) {
if (oldGeometry.width() != newGeometry.width() || oldGeometry.height() != newGeometry.height()) {
calculateMetrics(qFloor(newGeometry.width()));
update();
}
}
void TpianoBg::hoverEnterEvent(QHoverEvent*) {
m_active = true;
emit activeChanged();
}
void TpianoBg::hoverLeaveEvent(QHoverEvent*) {
m_active = false;
m_keyRect = QRectF();
emit activeChanged();
emit keyRectChanged();
}
void TpianoBg::hoverMoveEvent(QHoverEvent* event) {
if (event->pos().y() > m_keyWidth) {
int kw = qFloor(m_keyWidth);
int keyNr = qFloor((event->pos().x() - m_margin) / kw);
Tnote newNote(static_cast<char>(keyNr % 7 + 1), m_firstOctave + static_cast<char>(keyNr / 7), 0);
bool isWhite = true;
if (event->pos().y() < height() / 2) { // black keys
if (keyNr % 7 != 0 && keyNr % 7 != 3) {
--newNote.note;
newNote.alter = Tnote::e_Sharp;
isWhite = false;
}
}
if (m_activeNote != newNote) {
m_activeNote.note = newNote.note; m_activeNote.octave = newNote.octave; m_activeNote.alter = newNote.alter;
if (isWhite)
m_keyRect.setRect(m_margin + keyNr * kw, m_keyWidth, m_keyWidth, height() - m_keyWidth);
else
m_keyRect.setRect(m_margin + keyNr * kw - m_keyWidth * 0.4, m_keyWidth, m_keyWidth * 0.8, height() / 2.0);
emit keyRectChanged();
}
}
}
void TpianoBg::mousePressEvent(QMouseEvent* event) {
if (event->buttons() & Qt::LeftButton) {
m_note = m_activeNote;
emit noteChanged();
}
}
void TpianoBg::calculateMetrics(int newWidth) {
m_keysNumber = newWidth / qRound(m_keyWidth);
m_margin = (newWidth - m_keysNumber * qFloor(m_keyWidth)) / 2;
}
/***************************************************************************
* Copyright (C) 2017 by Tomasz Bojczuk *
* seelook@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef TPIANOBG_H
#define TPIANOBG_H
#include <QtQuick/qquickpainteditem.h>
#include "music/tnote.h"
/**
*
*/
class TpianoBg : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(qreal keyWidth READ keyWidth WRITE setKeyWidth)
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
Q_PROPERTY(QRectF keyRect READ keyRect NOTIFY keyRectChanged)
Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly)
Q_PROPERTY(Tnote note READ note WRITE setNote NOTIFY noteChanged)
Q_PROPERTY(int firstOctave READ firstOctave WRITE setFirstOctave)
public:
explicit TpianoBg(QQuickItem* parent = nullptr);
~TpianoBg();
qreal keyWidth() const { return m_keyWidth; }
void setKeyWidth(qreal kw);
QRectF keyRect() const { return m_keyRect; }
Tnote note() const { return m_note; }
void setNote(const Tnote& n);
int firstOctave() const { return static_cast<int>(m_firstOctave); }
void setFirstOctave(int firstO);
/**
* @p TRUE when mouse cursor is over
*/
bool active() const { return m_active; }
bool readOnly() const { return m_readOnly; }
void setReadOnly(bool ro);
void paint(QPainter* painter) override;
signals:
void activeChanged();
void keyRectChanged();
void noteChanged();
protected:
void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry) override;
void hoverEnterEvent(QHoverEvent*) override;
void hoverLeaveEvent(QHoverEvent*) override;
void hoverMoveEvent(QHoverEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
private:
void calculateMetrics(int newWidth);
private:
int m_keysNumber;
qreal m_keyWidth;
bool m_active = false;
QRectF m_keyRect;
int m_margin;
Tnote m_note, m_activeNote;
char m_firstOctave;
bool m_readOnly = false;
};
#endif // TPIANOBG_H
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "score/tstaffobject.h" #include "score/tstaffobject.h"
#include "score/tnoteobject.h" #include "score/tnoteobject.h"
#include "instruments/tguitarbg.h" #include "instruments/tguitarbg.h"
#include "instruments/tpianobg.h"
#include "taction.h" #include "taction.h"
#include "music/tinstrument.h" #include "music/tinstrument.h"
#include "music/ttune.h" #include "music/ttune.h"
...@@ -62,6 +63,7 @@ TnootkaQML::TnootkaQML(QObject* parent) : ...@@ -62,6 +63,7 @@ TnootkaQML::TnootkaQML(QObject* parent) :
qmlRegisterType<TtickColors>("Nootka", 1, 0, "TtickColors"); qmlRegisterType<TtickColors>("Nootka", 1, 0, "TtickColors");
qmlRegisterType<TguitarBg>("Nootka", 1, 0, "TguitarBg"); qmlRegisterType<TguitarBg>("Nootka", 1, 0, "TguitarBg");
qmlRegisterType<TpianoBg>("Nootka", 1, 0, "TpianoBg");
qmlRegisterType<Taction>("Nootka", 1, 0, "Taction"); qmlRegisterType<Taction>("Nootka", 1, 0, "Taction");
qmlRegisterUncreatableType<TnootkaQML>("Nootka", 1, 0, "Nootka", QStringLiteral("You cannot create an instance of the TnootkaQML.")); qmlRegisterUncreatableType<TnootkaQML>("Nootka", 1, 0, "Nootka", QStringLiteral("You cannot create an instance of the TnootkaQML."));
...@@ -100,6 +102,11 @@ Tnote TnootkaQML::note(int pitch, int octave, int alter, int rhythm, bool rest, ...@@ -100,6 +102,11 @@ Tnote TnootkaQML::note(int pitch, int octave, int alter, int rhythm, bool rest,
} }
Tnote TnootkaQML::note(const Tnote& n, int rhythm, bool rest, bool dot) {
return Tnote(n, Trhythm(static_cast<Trhythm::Erhythm>(rhythm), rest, dot, false));
}
QString TnootkaQML::noteName(const Tnote& n, int style, bool showOctave) { QString TnootkaQML::noteName(const Tnote& n, int style, bool showOctave) {
// Tnote::toText() method returns only names in user preferred according to settings // Tnote::toText() method returns only names in user preferred according to settings
// To cheat it and force note name in any given style we are resetting pointer of is7th_B // To cheat it and force note name in any given style we are resetting pointer of is7th_B
......
...@@ -65,6 +65,7 @@ public: ...@@ -65,6 +65,7 @@ public:
Q_INVOKABLE Tclef clef(int type); Q_INVOKABLE Tclef clef(int type);
Q_INVOKABLE Tmeter meter(int m); Q_INVOKABLE Tmeter meter(int m);
Q_INVOKABLE Tnote note(int pitch, int octave, int alter, int rhythm = 3, bool rest = false, bool dot = false); Q_INVOKABLE Tnote note(int pitch, int octave, int alter, int rhythm = 3, bool rest = false, bool dot = false);
Q_INVOKABLE Tnote note(const Tnote& n, int rhythm = 3, bool rest = false, bool dot = false);
Q_INVOKABLE QString noteName(const Tnote& n, int style, bool showOctave = true); Q_INVOKABLE QString noteName(const Tnote& n, int style, bool showOctave = true);
Q_INVOKABLE QString majorKeyName(int key); Q_INVOKABLE QString majorKeyName(int key);
Q_INVOKABLE QString minorKeyName(int key); Q_INVOKABLE QString minorKeyName(int key);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
<file alias="Instrument.qml">qml/instruments/Instrument.qml</file> <file alias="Instrument.qml">qml/instruments/Instrument.qml</file>
<file alias="Guitar.qml">qml/instruments/Guitar.qml</file> <file alias="Guitar.qml">qml/instruments/Guitar.qml</file>
<file alias="Piano.qml">qml/instruments/Piano.qml</file>
<file alias="TaboutNootka.qml">qml/about/TaboutNootka.qml</file> <file alias="TaboutNootka.qml">qml/about/TaboutNootka.qml</file>
<file alias="AboutPage.qml">qml/about/AboutPage.qml</file> <file alias="AboutPage.qml">qml/about/AboutPage.qml</file>
......
...@@ -97,9 +97,10 @@ ApplicationWindow { ...@@ -97,9 +97,10 @@ ApplicationWindow {
Instrument { Instrument {
id: instrument id: instrument
height: nootkaWindow.height / 4
width: nootkaWindow.width
z: 1 z: 1
onNote: {
score.addNote(Noo.note(n, 2 + Math.random() * 4))
}
} }
} }
...@@ -112,26 +113,7 @@ ApplicationWindow { ...@@ -112,26 +113,7 @@ ApplicationWindow {
// randNotes() // randNotes()
// } // }
// Timer {
// interval: 5000
// running: true
// repeat: true
// onTriggered: {
// score.addNote(Noo.note(1 + Math.random() * 7, -2 + Math.random() * 5, Math.min(Math.max(-2, -3 + Math.random() * 6), 2),
// 2 + Math.random() * 4))
// var noteNr = Math.random() * 7
// var rest = Math.floor((Math.random() * 100) % 2)
// var accid = rest ? 0 : Math.min(Math.max(-2, -3 + Math.random() * 6), 2)
// var note = Noo.note(1 + Math.random() * 7, -3 + Math.random() * 7, accid, 1 + Math.random() * 5, rest)
// score.setNote(0, noteNr, note)
// score.enableKeySign = !score.enableKeySign
// if (score.enableKeySign)
// score.setKeySignature(-7 + Math.random() * 15)
// var m = Math.pow(2, Math.floor(1 + Math.random() * 11))
// console.log("meter " + m)
// score.meter = m
// }
// }
function randNotes() { function randNotes() {
var rest = (Math.random() * 100) > 90 var rest = (Math.random() * 100) > 90
// var accid = rest ? 0 : Math.min(Math.max(-2, -3 + Math.random() * 6), 2) // var accid = rest ? 0 : Math.min(Math.max(-2, -3 + Math.random() * 6), 2)
......
...@@ -5,8 +5,19 @@ ...@@ -5,8 +5,19 @@
import QtQuick 2.7 import QtQuick 2.7
Item { Item {
id: root
property Item instrument: null
Guitar { signal note(var n)
anchors.fill: parent
height: nootkaWindow.height / 5 //4
width: nootkaWindow.width
Component.onCompleted: {
var c = Qt.createComponent("qrc:/Piano.qml")
instrument = c.createObject(root, {"anchors.fill": root})
instrument.onNoteChanged.connect(callNote)
} }
function callNote() { root.note(instrument.note) }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment