diff --git a/src/activities/playpiano/Playpiano.qml b/src/activities/playpiano/Playpiano.qml index 402525681..b36c5eed3 100644 --- a/src/activities/playpiano/Playpiano.qml +++ b/src/activities/playpiano/Playpiano.qml @@ -1,299 +1,320 @@ /* GCompris - playpiano.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 1.0 import "../../core" import "playpiano.js" as Activity import "melodies.js" as Dataset ActivityBase { id: activity onStart: focus = true onStop: {} property bool horizontalLayout: background.width > background.height ? true : false pageComponent: Rectangle { id: background anchors.fill: parent color: "#ABCDEF" signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } Keys.onPressed: { if(event.key === Qt.Key_1) { playNote('1') } if(event.key === Qt.Key_2) { playNote('2') } if(event.key === Qt.Key_3) { playNote('3') } if(event.key === Qt.Key_4) { playNote('4') } if(event.key === Qt.Key_5) { playNote('5') } if(event.key === Qt.Key_6) { playNote('6') } if(event.key === Qt.Key_7) { playNote('7') } if(event.key === Qt.Key_8) { playNote('8') } if(event.key === Qt.Key_F1 && bar.level >= 4) { playNote('-1') } if(event.key === Qt.Key_F2 && bar.level >= 4) { playNote('-2') } if(event.key === Qt.Key_F3 && bar.level >= 4) { playNote('-3') } if(event.key === Qt.Key_F4 && bar.level >= 4) { playNote('-4') } if(event.key === Qt.Key_F5 && bar.level >= 4) { playNote('-5') } if(event.key === Qt.Key_Delete) { staff2.eraseAllNotes() } if(event.key === Qt.Key_Space) { staff2.play() } } function playNote(note) { staff2.addNote(note, currentType, piano.useSharpNotation ? "sharp" : "flat", false) var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + 'bass' + '_pitches/' + currentType + '/' + note + '.wav'; - print("in",noteToPlay) - items.audioEffects.play(noteToPlay); + items.audioEffects.play(noteToPlay) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property GCAudio audioEffects: activity.audioEffects property alias bar: bar property alias bonus: bonus property alias staff2: staff2 property string staffLength: "short" } onStart: { Activity.start(items) } onStop: { Activity.stop() } property int currentType: 1 + property string cleffType: bar.level == 2 ? "bass" : "treble" MultipleStaff { id: staff2 width: horizontalLayout ? parent.width * 0.50 : parent.height * 0.4 height: parent.height * 0.5 nbStaves: 3 - clef: "bass" + clef: cleffType == "bass" ? "bass" : "treble" nbMaxNotesPerStaff: 8 noteIsColored: true isMetronomeDisplayed: true anchors.right: parent.right anchors.top: instructionBox.bottom anchors.topMargin: parent.height * 0.13 anchors.rightMargin: 20 } Rectangle { id: instructionBox radius: 10 width: background.width / 1.9 height: horizontalLayout ? background.height / 5 : background.height / 4 anchors.horizontalCenter: parent.horizontalCenter opacity: 0.8 border.width: 6 color: "white" border.color: "#87A6DD" GCText { id: instructionText color: "black" z: 3 anchors.fill: parent anchors.rightMargin: parent.width * 0.1 anchors.leftMargin: parent.width * 0.1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit wrapMode: Text.WordWrap text: Activity.instructions[bar.level - 1] } } Piano { id: piano width: horizontalLayout ? parent.width * 0.4 : parent.width * 0.45 height: horizontalLayout ? parent.height * 0.45 : parent.width * 0.4 anchors.left: parent.left anchors.leftMargin: horizontalLayout ? parent.width * 0.06 : parent.height * 0.01 anchors.top: instructionBox.bottom anchors.topMargin: parent.height * 0.15 blackLabelsVisible: [4, 5, 6, 7, 8].indexOf(items.bar.level) == -1 ? false : true useSharpNotation: bar.level == 5 ? false : true onNoteClicked: { + onlyNote.value = note staff2.addNote(note, currentType, piano.useSharpNotation ? "sharp" : "flat", false) - var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + 'bass' + '_pitches/' + currentType + '/' + note + '.wav'; - print(noteToPlay) - items.audioEffects.play(noteToPlay); + var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + cleffType + '_pitches/' + currentType + '/' + note + '.wav'; + items.audioEffects.play(noteToPlay) } + Note { id: onlyNote value: "1" type: currentType visible: false } } Row { id: optionsRow anchors.bottom: staff2.top spacing: 15 - anchors.leftMargin: 15 + anchors.horizontalCenter: parent.horizontalCenter Image { id: wholeNote source: "qrc:/gcompris/src/activities/playpiano/resource/whole-note.svg" sourceSize.width: 50 visible: bar.level == 1 || bar.level == 2 ? false : true - MouseArea{ + MouseArea { anchors.fill: parent onClicked: currentType = onlyNote.wholeNote } } + Image { id: halfNote source: "qrc:/gcompris/src/activities/playpiano/resource/half-note.svg" sourceSize.width: 50 visible: wholeNote.visible - MouseArea{ + MouseArea { anchors.fill: parent onClicked: currentType = onlyNote.halfNote } } + Image { id: quarterNote source: "qrc:/gcompris/src/activities/playpiano/resource/quarter-note.svg" sourceSize.width: 50 visible: wholeNote.visible - MouseArea{ + MouseArea { anchors.fill: parent onClicked: currentType = onlyNote.quarterNote } } + Image { id: eighthNote source: "qrc:/gcompris/src/activities/playpiano/resource/eighth-note.svg" sourceSize.width: 50 visible: wholeNote.visible - MouseArea{ + MouseArea { anchors.fill: parent onClicked: currentType = onlyNote.eighthNote } } + Image { id: playButton source: "qrc:/gcompris/src/activities/playpiano/resource/play.svg" - sourceSize.width: 75 - MouseArea{ + sourceSize.width: 50 + MouseArea { anchors.fill: parent onClicked: staff2.play() } } + + Image { + id: cleffButton + source: cleffType == "bass" ? "qrc:/gcompris/src/activities/playpiano/resource/bassClefButton.svg" : "qrc:/gcompris/src/activities/playpiano/resource/trebbleClefButton.svg" + sourceSize.width: 50 + visible: bar.level > 2 + MouseArea { + anchors.fill: parent + onClicked: { + staff2.eraseAllNotes() + cleffType = (cleffType == "bass") ? "treble" : "bass" + } + } + } + Image { id: clearButton source: "qrc:/gcompris/src/activities/playpiano/resource/edit-clear.svg" - sourceSize.width: 75 - MouseArea{ + sourceSize.width: 50 + MouseArea { anchors.fill: parent onClicked: staff2.eraseAllNotes() } } Image { id: openButton source: "qrc:/gcompris/src/activities/playpiano/resource/open.svg" sourceSize.width: 50 + visible: bar.level == 6 || bar.level == 7 MouseArea { anchors.fill: parent onClicked: loadMelody() } } Image { id: changeAccidentalStyleButton source: piano.useSharpNotation ? "qrc:/gcompris/src/activities/playpiano/resource/blacksharp.svg" : "qrc:/gcompris/src/activities/playpiano/resource/blackflat.svg" visible: bar.level >= 4 MouseArea { anchors.fill: parent onClicked: piano.useSharpNotation = !piano.useSharpNotation } } } function loadMelody() { var data = Dataset.get(); var selectedMusic = data.filter(function(item) { return item.title === 'Frère jacques'; }); staff2.loadFromData(selectedMusic[0]["melody"]); } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/playpiano/Staff.qml b/src/activities/playpiano/Staff.qml index 8476f4676..228afb7a7 100644 --- a/src/activities/playpiano/Staff.qml +++ b/src/activities/playpiano/Staff.qml @@ -1,196 +1,196 @@ /* GCompris - Staff.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * 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 . */ import QtQuick 2.1 import GCompris 1.0 import "../../core" Item { id: staff property Item main: activity.main property int verticalDistanceBetweenLines: height / nbLines // todo width/nbLines * smth property string clef width: 400 height: 100 // Stave property int nbLines: 5 property bool lastPartition: false property bool isMetronomeDisplayed: false property bool showMetronome: false property alias notes: notes property int firstNoteX: defaultFirstNoteX property int defaultFirstNoteX: clefImage.width property int nbMaxNotesPerStaff property bool noteIsColored property alias notesRepeater: notesRepeater Image { id: clefImage source: clef ? "qrc:/gcompris/src/activities/playpiano/resource/" + clef + "Clef.svg" : "" sourceSize.width: (nbLines-2)*verticalDistanceBetweenLines } Repeater { model: nbLines Rectangle { width: staff.width height: 5 border.width: 5 color: "black" x: 0 y: index * verticalDistanceBetweenLines } } Rectangle { width: 5 border.width: 5 height: (nbLines - 1) * verticalDistanceBetweenLines + 5 color: "black" x: staff.width y: 0 } // end of partition line Rectangle { width: 5 border.width: 5 height: (nbLines - 1) * verticalDistanceBetweenLines + 5 visible: lastPartition color: "black" x: staff.width - 10 y: 0 } ListModel { id: notes } Rectangle { id: metronome width: 5 border.width: 10 height: (nbLines - 1) * verticalDistanceBetweenLines + 5 visible: isMetronomeDisplayed && showMetronome color: "red" x: firstNoteX - width/2 y: 0 Behavior on x { SmoothedAnimation { id: metronomeAnimation duration: -1 } } } property var mDuration function initMetronome() { var staffDuration = 0; for(var v = 0 ; v < notes.count ; ++ v) { staffDuration += notes.get(v).mDuration; } metronomeAnimation.velocity = 1; mDuration = staffDuration; metronome.x = staff.width; print("total duration " + staffDuration) print("total distance " + metronome.x) } function addNote(newValue_, newType_, newBlackType_, highlightWhenPlayed_) { notes.append({"mValue": newValue_, "mType": newType_, "mBlackType": newBlackType_, "mDuration": 2000/newType_, "mHighlightWhenPlayed": highlightWhenPlayed_}); } function playNote(noteId) { metronomeAnimation.velocity = staff.width * 1000 / (notes.get(noteId).mDuration * notes.count); print("velocity " + metronomeAnimation.velocity) } function eraseAllNotes() { notes.clear(); } property int noteWidth: (staff.width - 10 - clefImage.width) / 10 Row { id: notesRow x: firstNoteX - noteWidth/2 Repeater { id: notesRepeater model: notes Note { value: mValue type: mType blackType: mBlackType highlightWhenPlayed: mHighlightWhenPlayed noteIsColored: staff.noteIsColored width: (notes.count == 1 && items.staffLength === "long") ? Math.min(items.background.width,items.background.height) * 0.1 : noteWidth height: staff.height MouseArea { anchors.fill: parent onClicked: { print(items.staffLength) print(items.background.width,items.background.height) } } function play() { if(highlightWhenPlayed) { highlightTimer.start(); } } y: { var shift = 0; if(clef === "bass") { shift = -3 * verticalDistanceBetweenLines } if(blackType !== "") { if(blackType === "flat") { shift += - verticalDistanceBetweenLines } else { shift += - verticalDistanceBetweenLines / 2 } } if(mValue > 0) { - return (nbLines - 3) * verticalDistanceBetweenLines - (parseInt(mValue) - 1) * verticalDistanceBetweenLines/2 + shift + return (nbLines - 2) * verticalDistanceBetweenLines - (parseInt(mValue) - 1) * verticalDistanceBetweenLines/2 + shift } else if(mValue >= -2) return (nbLines - 3) * verticalDistanceBetweenLines - (Math.abs(parseInt(mValue)) - 1) * verticalDistanceBetweenLines/2 + shift else return (nbLines - 3) * verticalDistanceBetweenLines - (Math.abs(parseInt(mValue))) * verticalDistanceBetweenLines/2 + shift } } } spacing: 0 } } diff --git a/src/activities/playpiano/resource/bassClefButton.svg b/src/activities/playpiano/resource/bassClefButton.svg new file mode 100644 index 000000000..977ced271 --- /dev/null +++ b/src/activities/playpiano/resource/bassClefButton.svg @@ -0,0 +1,295 @@ + + + + + + + + + + + + + webpage + button + shape + + + + + Benji Park + + + + + Benji Park + + + + + Benji Park + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/playpiano/resource/trebbleClefButton.svg b/src/activities/playpiano/resource/trebbleClefButton.svg new file mode 100644 index 000000000..9a2a699f7 --- /dev/null +++ b/src/activities/playpiano/resource/trebbleClefButton.svg @@ -0,0 +1,275 @@ + + + + + + + + + + + + + webpage + button + shape + + + + + Benji Park + + + + + Benji Park + + + + + Benji Park + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +