diff --git a/src/activities/piano_composition/MultipleStaff.qml b/src/activities/piano_composition/MultipleStaff.qml index 9ea33c297..571421b40 100644 --- a/src/activities/piano_composition/MultipleStaff.qml +++ b/src/activities/piano_composition/MultipleStaff.qml @@ -1,170 +1,170 @@ /* 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 QtQuick 2.6 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; } function getAllNotes() { var melody = [] for(var i = 0; i < nbStaves; i ++) { var staveNotes = staves.itemAt(i).notes for(var j = 0; j < staveNotes.count; j++) { melody.push({ "type": staveNotes.get(j).type, "note": staveNotes.get(j).mValue }) } } return melody } 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/piano_composition/resource/' + multipleStaff.clef + '_pitches/' + currentType + '/' + note + '.wav'; if(currentNote == 0) { staves.itemAt(currentPlayedStaff).initMetronome(); } // musicTimer.interval = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mDuration; if(staves.itemAt(currentPlayedStaff).notes.get(currentNote) !== undefined) { musicTimer.interval = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mDuration; items.audioEffects.play(noteToPlay); print("will play next " + JSON.stringify(staves.itemAt(currentPlayedStaff).notes.get(currentNote))); staves.itemAt(currentPlayedStaff).notesRepeater.itemAt(currentNote).play() 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() } } } } diff --git a/src/activities/piano_composition/Note.qml b/src/activities/piano_composition/Note.qml index a7346c7eb..ce6ea0d1e 100644 --- a/src/activities/piano_composition/Note.qml +++ b/src/activities/piano_composition/Note.qml @@ -1,122 +1,122 @@ /* GCompris - Note.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 2.6 import QtGraphicalEffects 1.0 import GCompris 1.0 import "../../core" Item { id: note property string value property bool noteIsColored: true property int type: wholeNote property string noteType: type == wholeNote ? "whole" : type == halfNote ? "half" : type == quarterNote ? "quarter" : type == eighthNote ? "eighth" : "" property string blackType: "" // empty, "flat" or "sharp" //width: noteImage.width //height: noteImage.height readonly property int wholeNote: 1 readonly property int halfNote: 2 readonly property int quarterNote: 4 readonly property int eighthNote: 8 readonly property int noteDuration: 2000 / type property var noteColorMap: { "1": "#FF0000", "2": "#FF7F00", "3": "#FFFF00", "4": "#32CD32", "5": "#6495ED", "6": "#D02090", "7": "#FF1493", "8": "#FF0000", "9": "#FF7F00", "10": "#FFFF00", "11": "#32CD32", "-1": "#FF6347", "-2": "#FFD700", "-3": "#20B2AA", "-4": "#8A2BE2", "-5": "#FF00FF" } property var whiteNoteName: { "1": qsTr("C"), "2": qsTr("D"), "3": qsTr("E"), "4": qsTr("F"), "5": qsTr("G"), "6": qsTr("A"), "7": qsTr("B"), "8": qsTr("C") } property var blackNoteName: blackType == "flat" ? flatNoteName : sharpNoteName property var sharpNoteName: { "-1": qsTr("C#"), "-2": qsTr("D#"), "-3": qsTr("F#"), "-4": qsTr("G#"), "-5": qsTr("A#")} property var flatNoteName: { "-1": qsTr("Db"), "-2": qsTr("Eb"), "-3": qsTr("Gb"), "-4": qsTr("Ab"), "-5": qsTr("Bb")} property bool highlightWhenPlayed: false property alias highlightTimer: highlightTimer Image { id: blackTypeImage source: blackType !== "" ? "qrc:/gcompris/src/activities/piano_composition/resource/black" + blackType + ".svg" : "" visible: value[0] === '-' sourceSize.width: noteImage.width/2.5 anchors.right: noteImage.left anchors.rightMargin: -width/2 anchors.bottom: noteImage.bottom anchors.bottomMargin: -height/2 fillMode: Image.PreserveAspectFit } Image { id: highlightImage source: "qrc:/gcompris/src/activities/piano_composition/resource/note_highlight.png" visible: false sourceSize.width: noteImage.width height: noteImage.height / 2 anchors.bottom: noteImage.bottom } Rectangle { id: highlightRectangle width: noteImage.width * 0.9 height: noteImage.height * 0.9 color: "red" opacity: 0.6 border.color: "white" radius: width / 6 visible: false } Image { id: noteImage source: "qrc:/gcompris/src/activities/piano_composition/resource/" + noteType + "-note.svg" sourceSize.width: 200 width: note.width height: note.height } // If the result is not good enough maybe have a rectangle and use opacity mask with a note ColorOverlay { anchors.fill: noteImage source: noteImage color: noteColorMap[value] // make image like it lays under red glass visible: noteIsColored } Timer { id: highlightTimer interval: noteDuration onRunningChanged: { highlightRectangle.visible = running // highlightImage.visible = running } } } diff --git a/src/activities/piano_composition/Piano.qml b/src/activities/piano_composition/Piano.qml index 8c2596be4..901f85e01 100644 --- a/src/activities/piano_composition/Piano.qml +++ b/src/activities/piano_composition/Piano.qml @@ -1,140 +1,92 @@ /* 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 QtQuick 2.6 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 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 + id: whiteKeyRepeater model: whiteNotes.length - Rectangle { - id: whiteKey + PianoKey { color: "white" - border.color: "black" width: whiteWidth height: whiteHeight x: index * whiteWidth - Rectangle { - width: labelSquareSize - height: width - anchors.bottom: whiteKey.bottom - anchors.horizontalCenter: whiteKey.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) - whiteKey.scale = 0.95 - } - onReleased: whiteKey.scale = 1 - } + labelSquareSize: piano.labelSquareSize + noteColor: colorWhiteNotes[index] + keyName: whiteNotes[index] + labelsVisible: whiteLabelsVisible + onKeyPressed: noteClicked(index + 1) } } Repeater { - id: blackNotesLabels + id: blackKeyRepeater model: blackNotes.length - Rectangle { - id: blackKey + PianoKey { 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 - 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) - blackKey.scale = 0.95 - } - onReleased: blackKey.scale = 1 - } + labelSquareSize: piano.labelSquareSize + noteColor: colorBlackNotes[index] + keyName: blackNotes[index] + labelsVisible: blackLabelsVisible + onKeyPressed: noteClicked(-index - 1) } } } diff --git a/src/activities/piano_composition/PianoKey.qml b/src/activities/piano_composition/PianoKey.qml new file mode 100644 index 000000000..39e644746 --- /dev/null +++ b/src/activities/piano_composition/PianoKey.qml @@ -0,0 +1,67 @@ +/* GCompris - PianoKey.qml +* +* Copyright (C) 2018 Aman Kumar Gupta +* +* Authors: +* Beth Hadley (GTK+ version) +* Johnny Jazeix (Qt Quick port) +* Aman Kumar Gupta (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.6 +import GCompris 1.0 + +import "../../core" + +Rectangle { + id: pianoKey + + property string noteColor + property string keyName + property real labelSquareSize + property bool labelsVisible + + signal keyPressed + + border.color: "black" + Rectangle { + width: labelSquareSize + height: width + anchors.bottom: pianoKey.bottom + anchors.horizontalCenter: pianoKey.horizontalCenter + color: noteColor + anchors.margins: 4 + border.color: "black" + border.width: 2 + radius: 5 + visible: labelsVisible + GCText { + anchors.fill: parent + text: keyName + fontSizeMode: Text.Fit + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + MultiPointTouchArea { + anchors.fill: parent + onPressed: { + keyPressed() + pianoKey.scale = 0.95 + } + onReleased: pianoKey.scale = 1 + } +} diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml index a827edf74..e3f81ae9e 100644 --- a/src/activities/piano_composition/Piano_composition.qml +++ b/src/activities/piano_composition/Piano_composition.qml @@ -1,352 +1,352 @@ /* GCompris - Piano_composition.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 2.6 import QtQuick.Controls 1.0 import GCompris 1.0 import "../../core" import "piano_composition.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/piano_composition/resource/' + 'bass' + '_pitches/' + currentType + '/' + note + '.wav'; 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 GCSfx audioEffects: activity.audioEffects property alias bar: bar property alias bonus: bonus property alias staff2: staff2 property string staffLength: "short" property alias melodyList: melodyList property alias file: file } onStart: { Activity.start(items) } onStop: { Activity.stop() } property int currentType: 1 property string clefType: bar.level == 2 ? "bass" : "treble" File { id: file onError: console.error("File error: " + msg) } MelodyList { id: melodyList onClose: { visible = false piano.enabled = true bar.visible = true } } MultipleStaff { id: staff2 width: horizontalLayout ? parent.width * 0.50 : parent.height * 0.8 height: horizontalLayout ? parent.height * 0.5 : parent.height * 0.3 nbStaves: 3 clef: clefType nbMaxNotesPerStaff: 8 noteIsColored: true isMetronomeDisplayed: true anchors.right: parent.right anchors.top: horizontalLayout ? instructionBox.bottom : piano.bottom anchors.topMargin: horizontalLayout ? parent.height * 0.13 : parent.height * 0.05 anchors.rightMargin: 20 } Rectangle { id: instructionBox radius: 10 width: horizontalLayout ? background.width / 1.9 : background.width * 0.95 height: horizontalLayout ? background.height / 5 : background.height / 9 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].text } } Piano { id: piano width: horizontalLayout ? parent.width * 0.4 : parent.width * 0.8 height: horizontalLayout ? parent.height * 0.45 : parent.width * 0.3 anchors.horizontalCenter: horizontalLayout ? undefined : parent.horizontalCenter anchors.left: horizontalLayout ? parent.left : undefined anchors.leftMargin: horizontalLayout ? parent.width * 0.06 : parent.height * 0.01 anchors.top: optionsRow.bottom 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/piano_composition/resource/' + clefType + '_pitches/' + currentType + '/' + note + '.wav'; items.audioEffects.play(noteToPlay) } Note { id: onlyNote value: "1" type: currentType visible: false } } Row { id: optionsRow anchors.top: instructionBox.bottom anchors.topMargin: 10 spacing: 15 anchors.horizontalCenter: parent.horizontalCenter Image { id: wholeNote source: "qrc:/gcompris/src/activities/piano_composition/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/piano_composition/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/piano_composition/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/piano_composition/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/piano_composition/resource/play.svg" sourceSize.width: 50 MouseArea { anchors.fill: parent onClicked: staff2.play() } } Image { id: clefButton source: clefType == "bass" ? "qrc:/gcompris/src/activities/piano_composition/resource/bassClefButton.svg" : "qrc:/gcompris/src/activities/piano_composition/resource/trebbleClefButton.svg" sourceSize.width: 50 visible: bar.level > 2 MouseArea { anchors.fill: parent onClicked: { staff2.eraseAllNotes() clefType = (clefType == "bass") ? "treble" : "bass" } } } Image { id: clearButton source: "qrc:/gcompris/src/activities/piano_composition/resource/edit-clear.svg" sourceSize.width: 50 MouseArea { anchors.fill: parent onClicked: staff2.eraseAllNotes() } } Image { id: openButton source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" sourceSize.width: 50 visible: bar.level == 6 || bar.level == 7 MouseArea { anchors.fill: parent onClicked: { var dataset = Dataset.get() for(var i = 0; i < dataset.length; i++) { melodyList.melodiesModel.append(dataset[i]) } piano.enabled = false bar.visible = false melodyList.visible = true } } } Image { id: saveButton source: "qrc:/gcompris/src/activities/piano_composition/resource/save.svg" sourceSize.width: 50 visible: bar.level == 6 || bar.level == 7 MouseArea { anchors.fill: parent onClicked: { Activity.saveMelody() } } } Image { id: changeAccidentalStyleButton source: piano.useSharpNotation ? "qrc:/gcompris/src/activities/piano_composition/resource/blacksharp.svg" : "qrc:/gcompris/src/activities/piano_composition/resource/blackflat.svg" visible: bar.level >= 4 MouseArea { anchors.fill: parent onClicked: piano.useSharpNotation = !piano.useSharpNotation } } } 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/piano_composition/Staff.qml b/src/activities/piano_composition/Staff.qml index a3f8ae1ac..3f964e4d1 100644 --- a/src/activities/piano_composition/Staff.qml +++ b/src/activities/piano_composition/Staff.qml @@ -1,205 +1,205 @@ /* 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 QtQuick 2.6 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/piano_composition/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_) { var duration if(newType_ == 1) duration = 2000/newType_ else if(newType_ == 2) duration = 3000/newType_ else if(newType_ == 4) duration = 4000/newType_ else duration = 6500/newType_ notes.append({"mValue": newValue_, "mType": newType_, "mBlackType": newBlackType_, "mDuration": duration, "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 - 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/piano_composition/piano_composition.js b/src/activities/piano_composition/piano_composition.js index 9db6b700b..fe85ba0e4 100644 --- a/src/activities/piano_composition/piano_composition.js +++ b/src/activities/piano_composition/piano_composition.js @@ -1,105 +1,105 @@ /* GCompris - piano_composition.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 +.import QtQuick 2.6 as Quick .import GCompris 1.0 as GCompris .import "qrc:/gcompris/src/core/core.js" as Core var currentLevel = 0 var numberOfLevel = 7 var items var userDir = "file://" + GCompris.ApplicationInfo.getSharedWritablePath() + "/" + "piano_composition" var userFile = userDir + "/melodies.json" var instructions = [{ "text": qsTr("This is the treble cleff staff for high pitched notes") }, { "text": qsTr("This is the bass cleff staff for low pitched notes") }, { "text": qsTr("Click on the note symbols to write different length notes such as quarter notes, half notes and whole notes") }, { "text": qsTr("The black keys are sharp and flat keys, have a # sign.") }, { "text": qsTr("Each black key has two names: flat and sharp. Flat notes have b sign") }, { "text": qsTr("Now you can load music") }, { "text": qsTr("Now you can compose your own music") } ] function start(items_) { items = items_ currentLevel = 0 initLevel() } function saveMelody() { var notes = items.staff2.getAllNotes() if (!items.file.exists(userDir)) { if (!items.file.mkpath(userDir)) console.error("Could not create directory " + userDir); else console.debug("Created directory " + userDir); } var data = items.file.read(userFile) if (!items.file.append(JSON.stringify(notes), userFile)) { Core.showMessageDialog(items.background, qsTr("Error saving melody to your file (%1)") .arg(userFile), "", null, "", null, null); } else { Core.showMessageDialog(items.background, qsTr("Saved melody to your file (%1)") .arg(userFile), "", null, "", null, null); } } function stop() {} function initLevel() { items.bar.level = currentLevel + 1 } function nextLevel() { items.staff2.eraseAllNotes() if(numberOfLevel <= ++currentLevel) { currentLevel = 0 } initLevel(); } function previousLevel() { items.staff2.eraseAllNotes() if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); }