diff --git a/src/activities/note_names/NoteNames.qml b/src/activities/note_names/NoteNames.qml index 914ca18e2..0fce31dc4 100755 --- a/src/activities/note_names/NoteNames.qml +++ b/src/activities/note_names/NoteNames.qml @@ -1,457 +1,463 @@ /* GCompris - NoteNames.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 GCompris 1.0 import "../../core" import "../playpiano" import "note_names.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Rectangle { id: background anchors.fill: parent color: "#ABCDEF" property bool keyboardMode: false property bool horizontalLayout: background.width > background.height ? true : false signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // 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 staff: staff property alias bar: bar property alias bonus: bonus property alias gridRepeater: gridRepeater property alias bottomNotesRepeater: bottomNotesRepeater property alias okButton: okButton property alias score: score + property string staffLength: "long" readonly property string clef: items.bar.level > 10 ? "bass" : "treble" } onStart: { Activity.start(items) } onStop: { Activity.stop() } Rectangle { id: instructionBox radius: 10 - width: background.width / 1.9 - height: horizontalLayout ? background.height / 5 : background.height / 4 + width: background.width / 1.7 + height: horizontalLayout ? background.height / 5 : background.height / 5 anchors.horizontalCenter: parent.horizontalCenter opacity: 0.8 border.width: 6 color: "white" border.color: "#87A6DD" GCText { id: instructionText visible: !staffText.visible 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: [2, 3, 4, 12, 13, 14].indexOf(bar.level) !== -1 ? qsTr("Click on the note name to match the pitch. Then click OK to check.") : [5, 6, 7, 15, 16, 17].indexOf(bar.level) !== -1 ? qsTr("Now there are sharp notes. These pitches are raised a half step.") : // [8, 9, 10, 18, 19, 20] qsTr("Now there are flat notes. These pitches are lowered a half step.") } GCText { id: staffText visible: bar.level == 1 || bar.level == 11 height: background.height / 5 width: background.width / 1.5 z: 3 color: "black" fontSizeMode: Text.Fit anchors.fill: parent anchors.rightMargin: parent.width * 0.1 anchors.leftMargin: parent.width * 0.1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap text: bar.level == 1 ? qsTr("These are the eight basic notes in treble clef. They form the C Major Scale.") : qsTr("These are the eight basic notes in bass clef. They also form the C Major Scale. Notice that the note positions are different than in treble clef.") } } Rectangle { id: playScale width: horizontalLayout ? parent.width * 0.3 : parent.width * 0.35 height: 30 * ApplicationInfo.ratio color: "#d8ffffff" border.color: "#2a2a2a" border.width: 3 radius: 8 z: 5 anchors.top: instructionBox.bottom anchors.topMargin: 15 anchors.left: background.left anchors.leftMargin: background.width * 0.15 visible: bar.level == 1 || bar.level == 11 GCText { id: playScaleText anchors.centerIn: parent text: qsTr("Play scale") fontSizeMode: Text.Fit wrapMode: Text.Wrap } MouseArea { id: playScaleArea anchors.fill: parent onClicked: { // items.staff.eraseAllNotes() // for(var i = 0; i < Activity.bottomNotes.length; i++) { // var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + Activity.bottomNotes[i].note + '.wav'; // items.staff.addNote(noteToPlay, 4, "", true); // items.audioEffects.append(noteToPlay) // } items.staff.play() } } states: [ State { name: "notclicked" PropertyChanges { target: playScale scale: 1.0 } }, State { name: "clicked" when: playScaleArea.pressed PropertyChanges { target: playScale scale: 0.9 } }, State { name: "hover" when: playScaleArea.containsMouse PropertyChanges { target: playScale scale: 1.1 } } ] Behavior on scale { NumberAnimation { duration: 70 } } } Rectangle { id: playButton width: horizontalLayout ? parent.width * 0.3 : parent.width * 0.35 height: 30 * ApplicationInfo.ratio color: "#d8ffffff" border.color: "#2a2a2a" border.width: 3 radius: 8 z: 5 anchors.top: instructionBox.bottom anchors.topMargin: 15 anchors.leftMargin: 30 anchors.left: playScale.right visible: bar.level == 1 || bar.level == 11 GCText { id: playButtonText anchors.centerIn: parent text: qsTr("Start levels") fontSizeMode: Text.Fit wrapMode: Text.Wrap } MouseArea { id: playButtonArea anchors.fill: parent onClicked: { Activity.nextLevel() } } states: [ State { name: "notclicked" PropertyChanges { target: playButton scale: 1.0 } }, State { name: "clicked" when: playButtonArea.pressed PropertyChanges { target: playButton scale: 0.9 } }, State { name: "hover" when: playButtonArea.containsMouse PropertyChanges { target: playButton scale: 1.1 } } ] Behavior on scale { NumberAnimation { duration: 70 } } } MultipleStaff { id: staff nbStaves: 1 clef: bar.level <= 10 ? "treble" : "bass" height: background.height / 4 width: bar.level == 1 || bar.level == 11 ? background.width * 0.8 : background.width / 2 - anchors.bottom: parent.bottom - anchors.bottomMargin: bar.level != 11 ? parent.height * 0.35 : parent.height * 0.25 - anchors.horizontalCenter: bar.level == 1 || bar.level == 11 ? parent.horizontalCenter : undefined - anchors.left: parent.left - anchors.leftMargin: horizontalLayout ? parent.width * 0.1 : parent.width * 0.05 + anchors { + bottom: parent.bottom + bottomMargin: bar.level != 11 ? parent.height * 0.35 : parent.height * 0.25 + horizontalCenter: bar.level == 1 || bar.level == 11 ? parent.horizontalCenter : undefined + left: parent.left + leftMargin: horizontalLayout ? parent.width * 0.1 : parent.width * 0.05 + } nbMaxNotesPerStaff: bar.level == 1 || bar.level == 11 ? 8 : 1 firstNoteX: bar.level == 1 || bar.level == 11 ? width / 5 : width / 2 } Grid { id: bottomNotesGrid rows: 1 spacing: horizontalLayout ? background.width * 0.03 : background.width * 0.01 anchors.bottom: background.bottom anchors.bottomMargin: background.height * 0.01 anchors.horizontalCenter: parent.horizontalCenter height: staff.height visible: (bar.level == 1 || bar.level == 11) ? true : false property int itemWidth: horizontalLayout ? background.width * 0.05 : background.width * 0.1 property int itemHeight: itemWidth Repeater { id: bottomNotesRepeater model: Activity.bottomNotes Rectangle { id: notes color: dummyNote.noteColorMap[Activity.bottomNotes[index].note] width: bottomNotesGrid.itemWidth height: bottomNotesGrid.itemHeight radius: width / 5 border.color: "black" GCText { id: bottomNotesText text: parseInt(Activity.bottomNotes[0].note) > 0 ? dummyNote.whiteNoteName[Activity.bottomNotes[index].note] : dummyNote.blackNoteName[Activity.bottomNotes[index].note] anchors.centerIn: parent fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter } MouseArea { id: buttonClick anchors.fill: parent onClicked: { select() } } function select() { grid.currentIndex = index var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + Activity.bottomNotes[index].note + '.wav' - items.staff.play(noteToPlay) + items.audioEffects.play(noteToPlay) } } } } DialogHelp { id: dialogHelp onClose: home() } Keys.onPressed: { if(event.key === Qt.Key_Space) { grid.currentItem.select() } } Keys.onReleased: { keyboardMode = true event.accepted = false } Keys.onEnterPressed: { grid.currentItem.select(); Activity.checkAnswer(okButton.currentAnswer); } Keys.onReturnPressed: { grid.currentItem.select(); Activity.checkAnswer(okButton.currentAnswer); } Keys.onRightPressed: grid.moveCurrentIndexRight(); Keys.onLeftPressed: grid.moveCurrentIndexLeft(); Keys.onDownPressed: grid.moveCurrentIndexDown(); Keys.onUpPressed: grid.moveCurrentIndexUp(); ListModel { id: gridRepeater } GridView { id: grid visible: instructionText.visible anchors { left: staff.right right: background.right leftMargin: 15 * ApplicationInfo.ratio top: instructionBox.bottom topMargin: horizontalLayout ? 15 * ApplicationInfo.ratio : 15 * ApplicationInfo.ratio bottom: background.bottom } keyNavigationWraps: true interactive: false model: gridRepeater cellWidth: itemWidth + 10 cellHeight: itemHeight + 10 height: staff.height property int itemWidth: 60 property int itemHeight: itemWidth delegate: Rectangle { id: highlightRectangle width: grid.itemWidth * 1.15 height: grid.itemHeight * 1.15 radius: width / 5 color: index === grid.currentIndex ? "red" : "transparent" border.color: index === grid.currentIndex ? "white" : "transparent" Rectangle { id: noteRectangle color: staff.noteIsColored ? dummyNote.noteColorMap[note] : "white" width: grid.itemWidth height: grid.itemHeight radius: width / 5 border.color: "black" anchors.centerIn: parent visible: true GCText { id: noteText text: parseInt(note) > 0 ? dummyNote.whiteNoteName[note] : dummyNote.blackNoteName[note] anchors.centerIn: parent fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter } MouseArea { id: buttonClick anchors.fill: parent onClicked: select() } } function select() { grid.currentIndex = index var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + note + '.wav' items.audioEffects.play(noteToPlay); okButton.currentAnswer = note } } } // Never visible, only used to access the note names, colors Note { id: dummyNote visible: false value: "1" blackType: [8, 9, 10, 18, 19, 20].indexOf(bar.level) === -1 ? "sharp" : "flat" } Image { id: okButton visible: instructionText.visible source:"qrc:/gcompris/src/core/resource/bar_ok.svg" width: Math.min(parent.width,parent.height) * 0.15 height: Math.min(parent.width,parent.height) * 0.15 anchors { - left: background.left - bottom: score.top + left: parent.left + bottom: parent.bottom topMargin: horizontalLayout ? 0.1 * parent.height : 0.01 * parent.height - bottomMargin: 20 + bottomMargin: parent.height * 0.2 leftMargin: 20 } property string currentAnswer: "" MouseArea { anchors.fill: parent onClicked: { if(okButton.currentAnswer !== "") Activity.checkAnswer(okButton.currentAnswer); okButton.currentAnswer = "" } } } Bar { id: bar content: BarEnumContent { value: (bar.level == 1 || bar.level == 11) ? (help | home | level ) : (help | home | level | reload) } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.reloadLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } Score { id: score - anchors.bottom: background.bottom - anchors.right: background.right + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: parent.height * 0.2 + } visible: bar.level !== 1 && bar.level !== 11 } } } diff --git a/src/activities/playpiano/MultipleStaff.qml b/src/activities/playpiano/MultipleStaff.qml index dfc4e0db8..1ed2f177f 100644 --- a/src/activities/playpiano/MultipleStaff.qml +++ b/src/activities/playpiano/MultipleStaff.qml @@ -1,156 +1,157 @@ /* GCompris - MultipleStaff.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: multipleStaff property int nbStaves property string clef property int distanceBetweenStaff: 20 property int currentStaff: 0 property int nbMaxNotesPerStaff: 6 property int firstNoteX: width / 5 property bool noteIsColored property bool isMetronomeDisplayed: false Column { + spacing: parent.height * 0.05 Repeater { id: staves model: nbStaves Staff { id: staff clef: multipleStaff.clef height: (multipleStaff.height - distanceBetweenStaff * (nbStaves - 1)) / nbStaves width: multipleStaff.width y: index * (height + distanceBetweenStaff) lastPartition: index == nbStaves - 1 nbMaxNotesPerStaff: multipleStaff.nbMaxNotesPerStaff noteIsColored: multipleStaff.noteIsColored isMetronomeDisplayed: multipleStaff.isMetronomeDisplayed firstNoteX: multipleStaff.firstNoteX } } } function addNote(newValue_, newType_, newBlackType_, highlightWhenPlayed_) { if(staves.itemAt(currentStaff).notes.count > nbMaxNotesPerStaff) { if(currentStaff + 1 >= nbStaves) { return } else currentStaff++ } staves.itemAt(currentStaff).addNote(newValue_, newType_, newBlackType_, highlightWhenPlayed_); } function play() { musicTimer.currentPlayedStaff = 0; musicTimer.currentNote = 0; musicTimer.interval = 500; for(var v = 1 ; v < currentStaff ; ++ v) staves.itemAt(v).showMetronome = false; // Only display metronome if we want to staves.itemAt(0).showMetronome = isMetronomeDisplayed; musicTimer.start(); } function eraseAllNotes() { for(var v = 0 ; v <= currentStaff ; ++ v) staves.itemAt(v).eraseAllNotes(); currentStaff = 0; } property var whiteNotes: ["C", "D", "E", "F", "G", "A", "B", "2C", "2D", "2E", "2F"] property var blackNotesSharp: ["C#", "D#", "F#", "G#", "A#", "2C#"] property var blackNotesFlat: ["DB", "EB", "GB", "AB", "BB"] function loadFromData(data) { eraseAllNotes() var melody = data.split(" "); multipleStaff.clef = melody[0]; for(var i = 1 ; i < melody.length ; ++ i) { var noteLength = melody[i].length; var type = parseInt(melody[i][noteLength-1]); var noteStr = melody[i].substr(0, noteLength-1).toUpperCase(); if(whiteNotes.indexOf(noteStr) != -1) addNote(""+(whiteNotes.indexOf(noteStr)+1), type, "", false); else if (blackNotesSharp.indexOf(melody[i][0]) != -1) { addNote(""+(-1*blackNotesSharp.indexOf(noteStr)-1), type, "sharp", false); } else { addNote(""+(-1*blackNotesFlat.indexOf(noteStr)-1), type, "flat", false); } print(melody[i]); } } Timer { id: musicTimer property int currentPlayedStaff: 0 property int currentNote: 0 onRunningChanged: { if(!running && staves.itemAt(currentPlayedStaff).notes.get(currentNote) !== undefined) { var currentType = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mType var note = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mValue // TODO some notes does not play if they are played in the rcc directly... var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + multipleStaff.clef + '_pitches/' + currentType + '/' + note + '.wav'; items.audioEffects.play(noteToPlay); staves.itemAt(currentPlayedStaff).notesRepeater.itemAt(currentNote).play() if(currentNote == 0) { staves.itemAt(currentPlayedStaff).initMetronome(); } musicTimer.interval = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mDuration; currentNote ++; if(currentNote > nbMaxNotesPerStaff) { currentNote = 0; currentPlayedStaff ++; if(currentPlayedStaff < nbStaves && currentNote < staves.itemAt(currentPlayedStaff).notes.count) { print("play next staff"); staves.itemAt(currentPlayedStaff).showMetronome = isMetronomeDisplayed; if(currentPlayedStaff > 0) staves.itemAt(currentPlayedStaff - 1).showMetronome = false; staves.itemAt(currentPlayedStaff).playNote(currentNote); musicTimer.start(); } } else if(staves.itemAt(currentPlayedStaff).notes.get(currentNote) !== undefined) { print("will play next " + JSON.stringify(staves.itemAt(currentPlayedStaff).notes.get(currentNote))); staves.itemAt(currentPlayedStaff).notesRepeater.itemAt(currentNote).play() musicTimer.start(); } } } } } diff --git a/src/activities/playpiano/Piano.qml b/src/activities/playpiano/Piano.qml index bb0787311..1488639b8 100644 --- a/src/activities/playpiano/Piano.qml +++ b/src/activities/playpiano/Piano.qml @@ -1,136 +1,136 @@ /* GCompris - Piano.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: piano //source: "qrc:/gcompris/src/activities/playpiano/resource/piano.svg" //sourceSize.height: 200 * ApplicationInfo.ratio z: 3 width: numberOfWhite * 23 // 23 is default width height: 120 property int numberOfWhite: 8 property var blacks: [14.33, 41.67, 82.25, 108.25, 134.75] // / 8*23 property int whiteWidth: width / numberOfWhite // 23 property int whiteHeight: height // 120 - property int blackWidth: (whiteWidth+1)/2 // 13 - property int blackHeight: 2*height/3 // 80 + property int blackWidth: (whiteWidth + 1) / 2 // 13 + property int blackHeight: 2 * height / 3 // 80 property var whiteNotes: ["C", "D", "E", "F", "G", "A", "B", "C"] property var blackNotesSharp: ["C#", "D#", "F#", "G#", "A#"] property var blackNotesFlat: ["Db", "Eb", "Gb", "Ab", "Bb"] // black note labels used, can be sharp or flat property var blackNotes: useSharpNotation ? blackNotesSharp : blackNotesFlat property var colorWhiteNotes: ["#FF0000", "#FF7F00", "#FFFF00", "#32CD32", "#6495ED", "#D02090", "#FF1493", "#FF0000", "#FF7F00", "#FFFF00", "#32CD32"] property var colorBlackNotes: ["#FF6347", "#FFD700", "#20B2AA", "#8A2BE2", "#FF00FF", "#FF6347"] signal noteClicked(string note) property bool blackLabelsVisible: true property bool whiteLabelsVisible: true property bool useSharpNotation: true property int labelSquareSize: whiteWidth - 5 Repeater { id: whiteRepeater model: whiteNotes.length Rectangle { id: note color: "white" border.color: "black" width: whiteWidth height: whiteHeight x: index * whiteWidth Rectangle { width: labelSquareSize height: width anchors.bottom: note.bottom anchors.horizontalCenter: note.horizontalCenter color: colorWhiteNotes[index] anchors.margins: 4 border.color: "black" border.width: 2 radius: 5 visible: whiteLabelsVisible GCText { anchors.fill: parent text: whiteNotes[index] fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } MultiPointTouchArea { anchors.fill: parent onPressed: { - noteClicked(index+1); + noteClicked(index + 1); } } } } Repeater { id: blackNotesLabels model: blackNotes.length Rectangle { id: note color: "black" border.color: "black" width: blackWidth height: blackHeight x: blacks[index] * piano.width / 184 Rectangle { width: height height: labelSquareSize y: parent.height - height - 5 - x: (blackWidth-labelSquareSize)/2 + x: (blackWidth - labelSquareSize)/2 color: colorBlackNotes[index] border.color: "black" border.width: 2 radius: 5 visible: blackLabelsVisible GCText { anchors.fill: parent text: blackNotes[index] fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } MultiPointTouchArea { anchors.fill: parent onPressed: { - noteClicked(-index-1); + noteClicked(-index - 1); } } } } } diff --git a/src/activities/playpiano/Playpiano.qml b/src/activities/playpiano/Playpiano.qml index eb7f9c56e..c8c624444 100644 --- a/src/activities/playpiano/Playpiano.qml +++ b/src/activities/playpiano/Playpiano.qml @@ -1,204 +1,249 @@ /* 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) } // 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 - MultipleStaff { - id: staff2 - width: parent.width * 0.35 - height: parent.height * 0.5 - nbStaves: 2 - clef: "bass" - nbMaxNotesPerStaff: 10 - noteIsColored: true - isMetronomeDisplayed: true - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: 100 - anchors.rightMargin: 20 + MultipleStaff { + id: staff2 + width: horizontalLayout ? parent.width * 0.50 : parent.height * 0.5 + height: parent.height * 0.5 + nbStaves: 3 + clef: "bass" + 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.6 : parent.width * 0.55 - height: horizontalLayout ? parent.height * 0.65 : parent.width * 0.45 - anchors.right: staff2.left - anchors.rightMargin: 30 - anchors.top: parent.top - anchors.topMargin: 100 + 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: 70 + 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'; items.audioEffects.play(noteToPlay); } Note { id: onlyNote value: "1" type: currentType visible: false } } Row { id: optionsRow - anchors.top: staff2.bottom + anchors.bottom: parent.bottom anchors.right: parent.right anchors.rightMargin: 50 + spacing: 30 + 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{ 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{ 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{ 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{ anchors.fill: parent onClicked: currentType = onlyNote.eighthNote } } Image { + id: playButton source: "qrc:/gcompris/src/activities/playpiano/resource/play.svg" sourceSize.width: 75 MouseArea{ anchors.fill: parent onClicked: staff2.play() } } Image { + id: clearButton source: "qrc:/gcompris/src/activities/playpiano/resource/edit-clear.svg" sourceSize.width: 75 MouseArea{ anchors.fill: parent onClicked: staff2.eraseAllNotes() } } Image { + id: openButton source: "qrc:/gcompris/src/activities/playpiano/resource/open.svg" sourceSize.width: 50 MouseArea{ anchors.fill: parent onClicked: loadMelody() } } } // GCDialogCheckBox { // id: button +// anchors.top: parent.top // onClicked: piano.useSharpNotation = !piano.useSharpNotation // } // GCText { // text: qsTr("Change accidental style") // } -// +// // Image { // source: piano.useSharpNotation ? "qrc:/gcompris/src/activities/playpiano/resource/blacksharp.svg" : "qrc:/gcompris/src/activities/playpiano/resource/blackflat.svg" // } 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 0f4b32248..8476f4676 100644 --- a/src/activities/playpiano/Staff.qml +++ b/src/activities/playpiano/Staff.qml @@ -1,188 +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 + 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 ? Math.min(main.width,main.height) * 0.1 : noteWidth + 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 } 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/playpiano.js b/src/activities/playpiano/playpiano.js index 8bcac08db..b35cb9baa 100644 --- a/src/activities/playpiano/playpiano.js +++ b/src/activities/playpiano/playpiano.js @@ -1,54 +1,56 @@ /* GCompris - playpiano.js * * 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 . */ .pragma library .import QtQuick 2.0 as Quick var currentLevel = 0 -var numberOfLevel = 4 +var numberOfLevel = 7 var items +var instructions = ["This is the treble cleff staff for high pitched notes", "This is the bass cleff staff for low pitched notes", "Click on the note symbols to write different length notes such as quarter notes, half notes and whole notes", "The black keys are sharp and flat keys, have a # sign.", "Each black key has two names: flat and sharp. Flat notes have b sign","Now you can load music", "Now you can compose your own music"] function start(items_) { items = items_ currentLevel = 0 initLevel() } function stop() { } function initLevel() { items.bar.level = currentLevel + 1 } function nextLevel() { + items.staff2.eraseAllNotes() if(numberOfLevel <= ++currentLevel ) { currentLevel = 0 } initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); }