diff --git a/src/activities/piano_composition/ActivityInfo.qml b/src/activities/piano_composition/ActivityInfo.qml --- a/src/activities/piano_composition/ActivityInfo.qml +++ b/src/activities/piano_composition/ActivityInfo.qml @@ -42,20 +42,8 @@ - enter/return: OK button - space bar: play - number keys: - - 1: C - - 2: D - - 3: E - - 4: F - - 5: G - - 6: A - - 7: B - - 8: C (higher octave) - - etc. - - F1: C# / Db - - F2: D# / Eb - - F3: F# / Gb - - F4: G# / Ab - - F5: A# / Bb + - 1 to 8: Corresponding white keys in the order on the displayed octave. + - F1 to F5: Corresponding black keys in the order on the displayed octave. ") credit: "" section: "discovery sound_group" diff --git a/src/activities/piano_composition/LyricsArea.qml b/src/activities/piano_composition/LyricsArea.qml --- a/src/activities/piano_composition/LyricsArea.qml +++ b/src/activities/piano_composition/LyricsArea.qml @@ -112,7 +112,7 @@ lyricsArea.title = "" lyricsArea.origin = "" lyricsArea.lyrics = "" - lyricsOrPianoModeOption.currentIndex = 0 + optionsRow.lyricsOrPianoModeIndex = 0 } function setLyrics(title, origin, lyrics) { @@ -120,6 +120,6 @@ lyricsArea.title = title lyricsArea.origin = origin lyricsArea.lyrics = lyrics - lyricsOrPianoModeOption.currentIndex = 1 + optionsRow.lyricsOrPianoModeIndex = 1 } } diff --git a/src/activities/piano_composition/MultipleStaff.qml b/src/activities/piano_composition/MultipleStaff.qml --- a/src/activities/piano_composition/MultipleStaff.qml +++ b/src/activities/piano_composition/MultipleStaff.qml @@ -23,7 +23,7 @@ import GCompris 1.0 import "../../core" -import "piano_composition.js" as Activity +import "qrc:/gcompris/src/activities/piano_composition/NoteNotations.js" as NoteNotations Item { id: multipleStaff @@ -39,13 +39,32 @@ // Stores the note number which is to be replaced. property int noteToReplace: -1 property bool noteIsColored + property bool noteHoverEnabled: true + property bool centerNotesPosition: false property bool isMetronomeDisplayed: false + readonly property bool isMusicPlaying: musicTimer.running property alias flickableStaves: flickableStaves property real flickableTopMargin: multipleStaff.height / 14 + distanceBetweenStaff / 2 + property bool isFlickable: true + /** + * Emitted when a note is clicked. + * + * It is used for selecting note to play, replace and edit it. + */ signal noteClicked(string noteName, string noteType, int noteIndex) + + /** + * Emitted when a change in any note is made to push it to the undo stack. + */ signal pushToUndoStack(int noteIndex, string oldNoteName, string oldNoteType) + + /** + * Emitted for the notes while a melody is playing. + * + * It is used to indicate the corresponding piano key. + */ signal notePlayed(string noteName) ListModel { @@ -54,6 +73,7 @@ Flickable { id: flickableStaves + interactive: multipleStaff.isFlickable flickableDirection: Flickable.VerticalFlick contentWidth: staffColumn.width contentHeight: staffColumn.height + 1.5 * distanceBetweenStaff @@ -78,7 +98,7 @@ } } - property real newNoteWidth: (multipleStaff.width - 15 - staves.itemAt(0).clefImageWidth) / 10 + property real noteWidth: (multipleStaff.width - 15 - staves.itemAt(0).clefImageWidth) / 10 Repeater { id: notesRepeater model: notesModel @@ -87,14 +107,16 @@ noteType: noteType_ highlightWhenPlayed: highlightWhenPlayed noteIsColored: multipleStaff.noteIsColored - width: flickableStaves.newNoteWidth + width: flickableStaves.noteWidth height: multipleStaff.height / 5 readonly property int currentStaffNb: index / nbMaxNotesPerStaff + readonly property real defaultX: staves.itemAt(0).clefImageWidth + ((index % nbMaxNotesPerStaff) * flickableStaves.noteWidth) + readonly property real centeredPosition: (multipleStaff.width / 2.5 - (flickableStaves.noteWidth * notesModel.count / 2) + defaultX) - x: staves.itemAt(0).clefImageWidth + ((index % nbMaxNotesPerStaff) * flickableStaves.newNoteWidth) + x: multipleStaff.centerNotesPosition ? centeredPosition : defaultX - noteDetails: Activity.getNoteDetails(noteName, noteType) + noteDetails: multipleStaff.getNoteDetails(noteName, noteType) MouseArea { id: noteMouseArea @@ -126,6 +148,28 @@ } } + /** + * Gets all the details of any note like note image, position on staff etc. from NoteNotations. + */ + function getNoteDetails(noteName, noteType) { + var notesDetails = NoteNotations.get() + var clef = background.clefType + var noteNotation + if(noteType === "Rest") + noteNotation = noteName + noteType + else + noteNotation = clef + noteName + + for(var i = 0; i < notesDetails.length; i++) { + if(noteNotation === notesDetails[i].noteName) { + return notesDetails[i] + } + } + } + + /** + * Calculates and assign the timer interval for a note. + */ function calculateTimerDuration(noteType) { noteType = noteType.toLowerCase() if(noteType === "whole") @@ -138,6 +182,9 @@ return 812.5 } + /** + * Adds a note to the staff. + */ function addNote(noteName, noteType, highlightWhenPlayed, playAudio, isReplacing) { var duration if(noteType === "Rest") @@ -168,6 +215,12 @@ playNoteAudio(noteName, noteType) } + /** + * Replaces the selected note with a new note. + * + * @param noteName: new note name. + * @param noteType: new note type. + */ function replaceNote(noteName, noteType) { if(noteToReplace != -1) { addNote(noteName, noteType, false, true, true) @@ -175,6 +228,11 @@ noteToReplace = -1 } + /** + * Erases the selected note. + * + * @param noteIndex: index of the note to be replaced + */ function eraseNote(noteIndex) { var noteLength = notesModel.get(noteIndex).mDuration var restName @@ -192,11 +250,17 @@ notesModel.set(noteIndex, { "noteName_": restName, "noteType_": "Rest" }) } + /** + * Erases all the notes. + */ function eraseAllNotes() { notesModel.clear() noteToReplace = -1 } + /** + * Undo the change made to the last note. + */ function undoChange(undoNoteDetails) { if(undoNoteDetails.oldNoteName_ === "none") notesModel.remove(undoNoteDetails.noteIndex_) @@ -207,6 +271,12 @@ noteToReplace = -1 } + /** + * Plays audio for a note. + * + * @param noteName: name of the note to be played. + * @param noteType: note type to be played. + */ function playNoteAudio(noteName, noteType) { if(noteType != "Rest") { var audioPitchType @@ -240,11 +310,13 @@ else audioPitchType = "bass" var noteToPlay = "qrc:/gcompris/src/activities/piano_composition/resource/" + audioPitchType + "_pitches/" + noteName + ".wav" - console.log(noteToPlay) items.audioEffects.play(noteToPlay) } } + /** + * Get all the notes from the notesModel and returns the melody. + */ function getAllNotes() { var melody = "" + multipleStaff.clef for(var i = 0; i < notesModel.count; i ++) @@ -252,6 +324,11 @@ return melody } + /** + * Loads melody from the provided data, to the staffs. + * + * @rparam data: melody to be loaded + */ function loadFromData(data) { eraseAllNotes() var melody = data.split(" ") @@ -277,6 +354,25 @@ } } + /** + * Used in the activity play_piano. + * + * Checks if the answered note is correct + */ + function indicateAnsweredNote(isCorrectAnswer, noteIndexAnswered) { + notesRepeater.itemAt(noteIndexAnswered).noteAnswered = true + notesRepeater.itemAt(noteIndexAnswered).isCorrectlyAnswered = isCorrectAnswer + } + + /** + * Used in the activity play_piano. + * + * Reverts the previous answer. + */ + function revertAnswer(noteIndexReverting) { + notesRepeater.itemAt(noteIndexReverting).noteAnswered = false + } + function play() { musicTimer.currentNote = 0 musicTimer.interval = 500 @@ -288,6 +384,9 @@ musicTimer.start() } + /** + * Stops the audios playing. + */ function stopAudios() { notesModel.clear() musicTimer.stop() diff --git a/src/activities/piano_composition/Note.qml b/src/activities/piano_composition/Note.qml --- a/src/activities/piano_composition/Note.qml +++ b/src/activities/piano_composition/Note.qml @@ -57,6 +57,9 @@ property var noteDetails readonly property int notesPositionBelowStaffWithBars: 6 + property bool noteAnswered: false + property bool isCorrectlyAnswered: false + rotation: { if((noteDetails === undefined) || ((noteDetails.positionOnStaff < 0) && (noteType === "Whole"))) return 0 @@ -69,12 +72,12 @@ Image { id: blackTypeImage source: blackType !== "" ? "qrc:/gcompris/src/activities/piano_composition/resource/black" + blackType + ".svg" : "" - sourceSize.width: noteImage.width / 2.5 + sourceSize.width: noteImage.width / 2 anchors.right: parent.rotation === 180 ? undefined : noteImage.left anchors.left: parent.rotation === 180 ? noteImage.right : undefined rotation: parent.rotation === 180 ? 180 : 0 - anchors.rightMargin: -12 - anchors.leftMargin: -18 + anchors.rightMargin: -noteImage.width / 4 + anchors.leftMargin: -noteImage.width / 2.5 anchors.bottom: noteImage.bottom anchors.bottomMargin: parent.height / 6 fillMode: Image.PreserveAspectFit @@ -97,7 +100,7 @@ opacity: 0.6 border.color: "white" radius: width / 6 - visible: noteMouseArea.containsMouse || highlightTimer.running + visible: (multipleStaff.noteHoverEnabled && noteMouseArea.containsMouse) || highlightTimer.running } Rectangle { @@ -121,6 +124,22 @@ height: note.height } + Image { + id: correctOrWrongAnswerIndicator + visible: noteAnswered + source: isCorrectlyAnswered ? "qrc:/gcompris/src/activities/piano_composition/resource/passed.svg" + : "qrc:/gcompris/src/activities/piano_composition/resource/failed.svg" + sourceSize.width: noteImage.width / 2.5 + anchors.right: parent.rotation === 180 ? undefined : noteImage.right + anchors.left: parent.rotation === 180 ? noteImage.left : undefined + rotation: parent.rotation === 180 ? 180 : 0 + anchors.rightMargin: 12 + anchors.bottom: noteImage.bottom + anchors.bottomMargin: parent.height / 6 + fillMode: Image.PreserveAspectFit + z: 3 + } + // If the result is not good enough maybe have a rectangle and use opacity mask with a note ColorOverlay { anchors.fill: noteImage diff --git a/src/activities/piano_composition/OptionsRow.qml b/src/activities/piano_composition/OptionsRow.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/OptionsRow.qml @@ -0,0 +1,204 @@ +/* GCompris - OptionsRow.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" + +Row { + id: optionsRow + spacing: 15 + + readonly property var noteLengthName: ["Whole", "Half", "Quarter", "Eighth"] + readonly property var staffModes: ["add", "replace", "erase"] + readonly property var lyricsOrPianoModes: ["piano", "lyrics"] + + property real iconsWidth: Math.min(50, (background.width - optionsRow.spacing * 12) / 14) + property alias noteOptionsIndex: noteOptions.currentIndex + property alias staffModeIndex: staffModesOptions.currentIndex + property alias lyricsOrPianoModeIndex: lyricsOrPianoModeOption.currentIndex + property alias restOptionIndex: restOptions.currentIndex + + property bool noteOptionsVisible: false + property bool playButtonVisible: false + property bool clefButtonVisible: false + property bool staffModesOptionsVisible: false + property bool clearButtonVisible: false + property bool undoButtonVisible: false + property bool openButtonVisible: false + property bool saveButtonVisible: false + property bool changeAccidentalStyleButtonVisible: false + property bool lyricsOrPianoModeOptionVisible: false + property bool restOptionsVisible: false + + signal undoButtonClicked + signal clearButtonClicked + signal openButtonClicked + signal saveButtonClicked + + SwitchableOptions { + id: noteOptions + source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex]) + nbOptions: optionsRow.noteLengthName.length + onClicked: background.currentType = optionsRow.noteLengthName[currentIndex] + visible: noteOptionsVisible + } + + Image { + id: playButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/play.svg" + sourceSize.width: optionsRow.iconsWidth + visible: playButtonVisible + MouseArea { + anchors.fill: parent + onClicked: multipleStaff.play() + } + } + + Image { + id: clefButton + source: background.clefType == "Bass" ? "qrc:/gcompris/src/activities/piano_composition/resource/bassClefButton.svg" : "qrc:/gcompris/src/activities/piano_composition/resource/trebbleClefButton.svg" + sourceSize.width: optionsRow.iconsWidth + visible: clefButtonVisible + MouseArea { + anchors.fill: parent + onClicked: { + multipleStaff.eraseAllNotes() + background.clefType = (background.clefType == "Bass") ? "Treble" : "Bass" + lyricsArea.resetLyricsArea() + } + } + } + + SwitchableOptions { + id:staffModesOptions + nbOptions: optionsRow.staffModes.length + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1.svg".arg(optionsRow.staffModes[currentIndex]) + anchors.top: parent.top + anchors.topMargin: 4 + visible: staffModesOptionsVisible + onClicked: { + background.staffMode = optionsRow.staffModes[currentIndex] + if(background.staffMode != "replace") { + multipleStaff.noteToReplace = -1 + } + } + } + + Image { + id: clearButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/edit-clear.svg" + sourceSize.width: optionsRow.iconsWidth + visible: clearButtonVisible + MouseArea { + anchors.fill: parent + onClicked: clearButtonClicked() + } + } + + Image { + id: undoButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/undo.svg" + sourceSize.width: optionsRow.iconsWidth + visible: undoButtonVisible + MouseArea { + anchors.fill: parent + onClicked: undoButtonClicked() + } + } + + Image { + id: openButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" + sourceSize.width: optionsRow.iconsWidth + visible: openButtonVisible + MouseArea { + anchors.fill: parent + onClicked: openButtonClicked() + } + } + + Image { + id: saveButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/save.svg" + sourceSize.width: optionsRow.iconsWidth + visible: saveButtonVisible + MouseArea { + anchors.fill: parent + onClicked: saveButtonClicked() + } + } + + Image { + id: changeAccidentalStyleButton + source: piano.useSharpNotation ? "qrc:/gcompris/src/activities/piano_composition/resource/blacksharp.svg" : "qrc:/gcompris/src/activities/piano_composition/resource/blackflat.svg" + sourceSize.width: optionsRow.iconsWidth + visible: changeAccidentalStyleButtonVisible + MouseArea { + anchors.fill: parent + onClicked: piano.useSharpNotation = !piano.useSharpNotation + } + } + + SwitchableOptions { + id: lyricsOrPianoModeOption + nbOptions: optionsRow.lyricsOrPianoModes.length + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1-icon.svg".arg(optionsRow.lyricsOrPianoModes[currentIndex]) + anchors.top: parent.top + anchors.topMargin: 4 + visible: lyricsOrPianoModeOptionVisible + } + + // Since the half rest image is just the rotated image of whole rest image, we check if the current rest type is half, we assign the source as whole rest and rotate it by 180 degrees. + SwitchableOptions { + id: restOptions + + readonly property string restTypeImage: ((optionsRow.noteLengthName[currentIndex] === "Half") ? "Whole" : optionsRow.noteLengthName[currentIndex]).toLowerCase() + + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1Rest.svg".arg(restTypeImage) + nbOptions: optionsRow.noteLengthName.length + onClicked: background.restType = optionsRow.noteLengthName[currentIndex] + rotation: optionsRow.noteLengthName[currentIndex] === "Half" ? 180 : 0 + visible: restOptionsVisible + } + + Image { + id: addRestButton + sourceSize.width: optionsRow.iconsWidth + source: "qrc:/gcompris/src/core/resource/apply.svg" + visible: restOptions.visible + anchors.top: parent.top + anchors.topMargin: 4 + MouseArea { + anchors.fill: parent + onPressed: parent.scale = 0.8 + onReleased: { + parent.scale = 1 + if(background.staffMode === "add") + multipleStaff.addNote(restType.toLowerCase(), "Rest", false, false) + else + multipleStaff.replaceNote(restType.toLowerCase(), "Rest") + } + } + } +} diff --git a/src/activities/piano_composition/Piano.qml b/src/activities/piano_composition/Piano.qml --- a/src/activities/piano_composition/Piano.qml +++ b/src/activities/piano_composition/Piano.qml @@ -36,7 +36,7 @@ property int numberOfWhite: 8 property int currentOctaveNb: defaultOctaveNb - readonly property int defaultOctaveNb: background.clefType === "treble" ? 1 : 0 + readonly property int defaultOctaveNb: background.clefType === "Treble" ? 1 : 0 readonly property int maxNbOctaves: whiteNotes.length onDefaultOctaveNbChanged: currentOctaveNb = defaultOctaveNb @@ -59,7 +59,7 @@ [["C4", qsTr("C%1").arg(4)], ["D4", qsTr("D%1").arg(4)], ["E4", qsTr("E%1").arg(4)], ["F4", qsTr("F%1").arg(4)], ["G4", qsTr("G%1").arg(4)], ["A4", qsTr("A%1").arg(4)], ["B4", qsTr("B%1").arg(4)], ["C5", qsTr("C%1").arg(5)]], [["C5", qsTr("C%1").arg(5)], ["D5", qsTr("D%1").arg(5)], ["E5", qsTr("E%1").arg(5)], ["F5", qsTr("F%1").arg(5)], ["G5", qsTr("G%1").arg(5)], ["A5", qsTr("A%1").arg(5)], ["B5", qsTr("B%1").arg(5)], ["C6", qsTr("C%1").arg(6)]] ] - readonly property var whiteNotes: background.clefType === "treble" ? whiteNotesTreble : whiteNotesBass + readonly property var whiteNotes: background.clefType === "Treble" ? whiteNotesTreble : whiteNotesBass // Sharp black key notes when the clef is bass. //: Translators, C#, D#, F#, G#, and A# are the note notations in English musical notation system. The numbers in the arguments represents the octave number of the note. For instance, C#4 is a C# note in the 4th octave. @@ -75,7 +75,7 @@ [["C#4", qsTr("C#%1").arg(4)], ["D#4", qsTr("D#%1").arg(4)], ["F#4", qsTr("F#%1").arg(4)], ["G#4", qsTr("G#%1").arg(4)], ["A#4", qsTr("A#%1").arg(4)]], [["C#5", qsTr("C#%1").arg(5)], ["D#5", qsTr("D#%1").arg(5)], ["F#5", qsTr("F#%1").arg(5)], ["G#5", qsTr("G#%1").arg(5)], ["A#5", qsTr("A#%1").arg(5)]] ] - readonly property var blackNotesSharp: background.clefType === "treble" ? blackNotesSharpTreble : blackNotesSharpBass + readonly property var blackNotesSharp: background.clefType === "Treble" ? blackNotesSharpTreble : blackNotesSharpBass // Flat black key notes when the clef is bass. //: Translators, Db, Eb, Gb, Ab, Bb are the note notations in English musical notation system. The numbers in the arguments represents the octave number of the note. For instance, Db4 is a Db note in the 4th octave. @@ -91,7 +91,7 @@ [["Db4", qsTr("Db%1").arg(4)], ["Eb4", qsTr("Eb%1").arg(4)], ["Gb4", qsTr("Gb%1").arg(4)], ["Ab4", qsTr("Ab%1").arg(4)], ["Bb4", qsTr("Bb%1").arg(4)]], [["Db5", qsTr("Db%1").arg(5)], ["Eb5", qsTr("Eb%1").arg(5)], ["Gb5", qsTr("Gb%1").arg(5)], ["Ab5", qsTr("Ab%1").arg(5)], ["Bb5", qsTr("Bb%1").arg(5)]] ] - readonly property var blackNotesFlat: background.clefType === "treble" ? blackNotesFlatTreble : blackNotesFlatBass + readonly property var blackNotesFlat: background.clefType === "Treble" ? blackNotesFlatTreble : blackNotesFlatBass // Black note labels used, can be sharp or flat readonly property var blackNotes: useSharpNotation ? blackNotesSharp : blackNotesFlat @@ -107,7 +107,7 @@ [14.33, 41.67, 82.25, 108.25, 134.75], [14.33, 41.67, 82.25, 108.25, 154], ] - readonly property var blacks: background.clefType === "treble" ? blacksTreble : blacksBass + readonly property var blacks: background.clefType === "Treble" ? blacksTreble : blacksBass // Color of white key labels when the clef is bass readonly property var colorWhiteNotesBass: [ @@ -120,7 +120,7 @@ ["#FF0000", "#FF7F00", "#FFFF00", "#32CD32", "#6495ED", "#D02090", "#FF1493", "#FF0000"], ["#FF0000", "#FF7F00", "#FFFF00", "#32CD32", "#6495ED", "#D02090", "#FF1493", "#FF0000"] ] - readonly property var colorWhiteNotes: background.clefType === "treble" ? colorWhiteNotesTreble : colorWhiteNotesBass + readonly property var colorWhiteNotes: background.clefType === "Treble" ? colorWhiteNotesTreble : colorWhiteNotesBass // Color of black key labels when the clef is bass readonly property var colorBlackNotesBass: [ @@ -133,13 +133,14 @@ ["#FF6347", "#FFD700", "#20B2AA", "#8A2BE2", "#FF00FF"], ["#FF6347", "#FFD700", "#20B2AA", "#8A2BE2", "#FF00FF"] ] - readonly property var colorBlackNotes: background.clefType === "treble" ? colorBlackNotesTreble : colorBlackNotesBass + readonly property var colorBlackNotes: background.clefType === "Treble" ? colorBlackNotesTreble : colorBlackNotesBass signal noteClicked(string note) property bool blackLabelsVisible: true property bool whiteLabelsVisible: true property bool blackKeysEnabled: true + property bool whiteKeysEnabled: true property bool useSharpNotation: true property int labelSquareSize: whiteWidth - 5 @@ -155,13 +156,14 @@ noteColor: colorWhiteNotes[currentOctaveNb][index] keyName: whiteNotes[currentOctaveNb][index][1] labelsVisible: whiteLabelsVisible + isKeyEnabled: piano.whiteKeysEnabled onKeyPressed: noteClicked(whiteNotes[currentOctaveNb][index][0]) } } Repeater { id: blackKeyRepeater - model: Object.keys(blackNotes[currentOctaveNb]).length + model: blackNotes[currentOctaveNb].length PianoKey { color: "black" width: blackWidth diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml --- a/src/activities/piano_composition/Piano_composition.qml +++ b/src/activities/piano_composition/Piano_composition.qml @@ -72,19 +72,19 @@ if(event.key === Qt.Key_8) { piano.whiteKeyRepeater.itemAt(7).keyPressed() } - if(event.key === Qt.Key_F1 && bar.level >= 4) { + if(event.key === Qt.Key_F1 && piano.blackKeysEnabled) { piano.blackKeyRepeater.itemAt(0).keyPressed() } - if(event.key === Qt.Key_F2 && bar.level >= 4) { + if(event.key === Qt.Key_F2 && piano.blackKeysEnabled) { piano.blackKeyRepeater.itemAt(1).keyPressed() } - if(event.key === Qt.Key_F3 && bar.level >= 4) { + if(event.key === Qt.Key_F3 && piano.blackKeysEnabled) { piano.blackKeyRepeater.itemAt(2).keyPressed() } - if(event.key === Qt.Key_F4 && bar.level >= 4) { + if(event.key === Qt.Key_F4 && piano.blackKeysEnabled) { piano.blackKeyRepeater.itemAt(3).keyPressed() } - if(event.key === Qt.Key_F5 && bar.level >= 4) { + if(event.key === Qt.Key_F5 && piano.blackKeysEnabled) { piano.blackKeyRepeater.itemAt(4).keyPressed() } if(event.key === Qt.Key_Delete) { @@ -108,7 +108,7 @@ property alias melodyList: melodyList property alias file: file property alias piano: piano - property alias staffModesOptions: staffModesOptions + property alias optionsRow: optionsRow property alias lyricsArea: lyricsArea } @@ -120,7 +120,7 @@ property string clefType: bar.level == 2 ? "bass" : "treble" property string staffMode: "add" property bool undidChange: false - property bool isLyricsMode: (lyricsOrPianoModeOption.currentIndex === 1) && lyricsOrPianoModeOption.visible + property bool isLyricsMode: (optionsRow.lyricsOrPianoModeIndex === 1) && optionsRow.lyricsOrPianoModeOptionVisible File { id: file @@ -269,179 +269,46 @@ id: lyricsArea } - Row { + OptionsRow { id: optionsRow anchors.top: instructionBox.bottom anchors.topMargin: 10 - spacing: 15 anchors.horizontalCenter: parent.horizontalCenter - readonly property var noteLengthName: ["Whole", "Half", "Quarter", "Eighth"] - readonly property var staffModes: ["add", "replace", "erase"] - readonly property var lyricsOrPianoModes: ["piano", "lyrics"] - readonly property real iconsWidth: Math.min(50, (background.width - optionsRow.spacing *12) / 14) - - SwitchableOptions { - id: noteOptions - source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex]) - nbOptions: optionsRow.noteLengthName.length - onClicked: currentType = optionsRow.noteLengthName[currentIndex] - visible: bar.level > 4 - } - - Image { - id: playButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/play.svg" - sourceSize.width: optionsRow.iconsWidth - MouseArea { - anchors.fill: parent - onClicked: multipleStaff.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: optionsRow.iconsWidth - visible: bar.level > 2 - MouseArea { - anchors.fill: parent - onClicked: { - multipleStaff.eraseAllNotes() - clefType = (clefType == "bass") ? "treble" : "bass" - lyricsArea.resetLyricsArea() - } - } - } - - SwitchableOptions { - id:staffModesOptions - nbOptions: optionsRow.staffModes.length - source: "qrc:/gcompris/src/activities/piano_composition/resource/%1.svg".arg(optionsRow.staffModes[currentIndex]) - anchors.top: parent.top - anchors.topMargin: 4 - onClicked: { - background.staffMode = optionsRow.staffModes[currentIndex] - if(background.staffMode != "replace") { - multipleStaff.noteToReplace = -1 - } - } - visible: true - } - - Image { - id: clearButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/edit-clear.svg" - sourceSize.width: optionsRow.iconsWidth - MouseArea { - anchors.fill: parent - onClicked: { - lyricsArea.resetLyricsArea() - Activity.undoStack = [] - multipleStaff.eraseAllNotes() - } - } - } - - Image { - id: undoButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/undo.svg" - sourceSize.width: optionsRow.iconsWidth - MouseArea { - anchors.fill: parent - onClicked: { - background.undidChange = true - Activity.undoChange() - background.undidChange = false - } - } - } - - Image { - id: openButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" - sourceSize.width: optionsRow.iconsWidth - visible: bar.level > 6 - MouseArea { - anchors.fill: parent - onClicked: { - melodyList.melodiesModel.clear() - 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 - melodyList.forceActiveFocus() - } - } - } - - Image { - id: saveButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/save.svg" - sourceSize.width: optionsRow.iconsWidth - visible: 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 - } - } - - SwitchableOptions { - id: lyricsOrPianoModeOption - nbOptions: optionsRow.lyricsOrPianoModes.length - source: "qrc:/gcompris/src/activities/piano_composition/resource/%1-icon.svg".arg(optionsRow.lyricsOrPianoModes[currentIndex]) - anchors.top: parent.top - anchors.topMargin: 4 - visible: bar.level > 6 - } - - // Since the half rest image is just the rotated image of whole rest image, we check if the current rest type is half, we assign the source as whole rest and rotate it by 180 degrees. - SwitchableOptions { - id: restOptions - - readonly property string restTypeImage: ((optionsRow.noteLengthName[currentIndex] === "Half") ? "Whole" : optionsRow.noteLengthName[currentIndex]).toLowerCase() - - source: "qrc:/gcompris/src/activities/piano_composition/resource/%1Rest.svg".arg(restTypeImage) - nbOptions: optionsRow.noteLengthName.length - onClicked: restType = optionsRow.noteLengthName[currentIndex] - rotation: optionsRow.noteLengthName[currentIndex] === "Half" ? 180 : 0 - visible: bar.level > 5 + noteOptionsVisible: bar.level > 4 + playButtonVisible: true + clefButtonVisible: bar.level > 2 + staffModesOptionsVisible: true + clearButtonVisible: true + undoButtonVisible: true + openButtonVisible: bar.level > 6 + saveButtonVisible: bar.level > 6 + changeAccidentalStyleButtonVisible: bar.level >= 4 + lyricsOrPianoModeOptionVisible: bar.level > 6 + restOptionsVisible: bar.level > 5 + + onUndoButtonClicked: { + background.undidChange = true + Activity.undoChange() + background.undidChange = false + } + onClearButtonClicked: { + lyricsArea.resetLyricsArea() + Activity.undoStack = [] + multipleStaff.eraseAllNotes() } - - Image { - id: addRestButton - sourceSize.width: optionsRow.iconsWidth - source: "qrc:/gcompris/src/core/resource/apply.svg" - visible: restOptions.visible - anchors.top: parent.top - anchors.topMargin: 4 - MouseArea { - anchors.fill: parent - onPressed: parent.scale = 0.8 - onReleased: { - parent.scale = 1 - if(background.staffMode === "add") - multipleStaff.addNote(restType.toLowerCase(), "Rest", false, false) - else - multipleStaff.replaceNote(restType.toLowerCase(), "Rest") - } + onOpenButtonClicked: { + melodyList.melodiesModel.clear() + 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 + melodyList.forceActiveFocus() } + onSaveButtonClicked: Activity.saveMelody() } DialogHelp { diff --git a/src/activities/piano_composition/Staff.qml b/src/activities/piano_composition/Staff.qml --- a/src/activities/piano_composition/Staff.qml +++ b/src/activities/piano_composition/Staff.qml @@ -23,7 +23,6 @@ import GCompris 1.0 import "../../core" -import "piano_composition.js" as Activity Item { id: staff @@ -47,7 +46,7 @@ Image { id: clefImage - source: clef ? "qrc:/gcompris/src/activities/piano_composition/resource/" + clef + "Clef.svg" : "" + source: clef ? "qrc:/gcompris/src/activities/piano_composition/resource/" + clef.toLowerCase() + "Clef.svg" : "" sourceSize.width: (nbLines - 2) * verticalDistanceBetweenLines } diff --git a/src/activities/piano_composition/melodies.js b/src/activities/piano_composition/melodies.js --- a/src/activities/piano_composition/melodies.js +++ b/src/activities/piano_composition/melodies.js @@ -26,205 +26,205 @@ "title": "Twinkle Twinkle Little Star", "_origin": qsTr("America: English Lullaby"), "lyrics": "Twinkle, twinkle, little star, how I wonder what you are; Up above the world so high, like a diamond in the sky. Twinkle, twinkle, little star, how I wonder what you are.", - "melody": "treble C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Whole" + "melody": "Treble C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Whole" }, { "title": "Yankee Doodle", "_origin": qsTr("America: Patriotic"), "lyrics": "Yankee Doodle went to town, Riding on a pony; He stuck a feather in his hat, And called it macaroni", - "melody": "treble G4Eighth G4Eighth A4Eighth B4Eighth G4Eighth B4Eighth A4Quarter G4Eighth G4Eighth A4Eighth B4Eighth G4Quarter F#4Quarter G4Eighth G4Eighth A4Eighth B4Eighth C5Eighth B4Eighth A4Eighth G4Eighth F#4Eighth D4Eighth E4Eighth F#4Eighth G4Quarter G4Quarter" + "melody": "Treble G4Eighth G4Eighth A4Eighth B4Eighth G4Eighth B4Eighth A4Quarter G4Eighth G4Eighth A4Eighth B4Eighth G4Quarter F#4Quarter G4Eighth G4Eighth A4Eighth B4Eighth C5Eighth B4Eighth A4Eighth G4Eighth F#4Eighth D4Eighth E4Eighth F#4Eighth G4Quarter G4Quarter" }, { "title": "Simple Gifts", "_origin": qsTr("America: Shaker Tune"), "lyrics": "Tis the gift to be simple, 'tis the gift to be free, 'tis the gift to come down where we ought to be. And when we find ourselves in the place just right, 'Twill be in the valley of love and delight.", - "melody": "treble C4Eighth C4Eighth F4Quarter F4Eighth G4Eighth A4Eighth F4Eighth A4Eighth Bb4Eighth C5Quarter C5Eighth Bb4Eighth A4Quarter G4Eighth F4Eighth G4Quarter C4Quarter G4Quarter F4Quarter G4Eighth A4Eighth G4Eighth E4Eighth C4Quarter C4Quarter F4Eighth E4Eighth F4Eighth G4Eighth A4Quarter G4Eighth G4Eighth A4Quarter Bb4Quarter C5Quarter A4Quarter G4Quarter G4Eighth G4Eighth A4Quarter A4Eighth G4Eighth F4Quarter F4Eighth F4Eighth F4Half" + "melody": "Treble C4Eighth C4Eighth F4Quarter F4Eighth G4Eighth A4Eighth F4Eighth A4Eighth Bb4Eighth C5Quarter C5Eighth Bb4Eighth A4Quarter G4Eighth F4Eighth G4Quarter C4Quarter G4Quarter F4Quarter G4Eighth A4Eighth G4Eighth E4Eighth C4Quarter C4Quarter F4Eighth E4Eighth F4Eighth G4Eighth A4Quarter G4Eighth G4Eighth A4Quarter Bb4Quarter C5Quarter A4Quarter G4Quarter G4Eighth G4Eighth A4Quarter A4Eighth G4Eighth F4Quarter F4Eighth F4Eighth F4Half" }, { "title": "Old MacDonald Had a Farm", "_origin": qsTr("America: Nursery Rhyme"), "lyrics": "Old MacDonald had a farm, EE-I-EE-I-O, And on that farm he had a [animal name], EE-I-EE-I-O", - "melody": "bass G4Quarter G4Quarter G4Quarter D4Quarter E4Quarter E4Quarter D4Half B4Quarter B4Quarter A4Quarter A4Quarter G4Half D4Half G4Quarter G4Quarter G4Quarter D4Quarter E4Quarter E4Quarter D4Half B4Quarter B4Quarter A4Quarter A4Quarter G4Whole" + "melody": "Bass G3Quarter G3Quarter G3Quarter D3Quarter E3Quarter E3Quarter D3Half B3Quarter B3Quarter A3Quarter A3Quarter G3Half D3Half G3Quarter G3Quarter G3Quarter D3Quarter E3Quarter E3Quarter D3Half B3Quarter B3Quarter A3Quarter A3Quarter G3Whole" }, { "title": "Un elefante se balanceaba", "_origin": qsTr("Mexico"), "lyrics": "Un elefante se columpiaba sobre la tela de una araña. Como veia que resistía fue a llamar a otro elefante", - "melody": "treble G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth A4Eighth G4Eighth F4Eighth E4Eighth F4Half D4Half F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth G4Eighth F4Eighth E4Eighth D4Eighth E4Half C4Half" + "melody": "Treble G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth A4Eighth G4Eighth F4Eighth E4Eighth F4Half D4Half F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth G4Eighth F4Eighth E4Eighth D4Eighth E4Half C4Half" }, { "title": "Piove, Piove", "_origin": qsTr("Italy"), "lyrics": "Piove, piove. La gatta non si muove. La fiamma traballa. La mucca è nella stalla. La mucca ha il vitello. La pecora ha l’agnello. [La chioccia ha il pulcino.] Ognuno ha il suo bambino. Ognuno ha la sua mamma. E tutti fanno nanna!", - "melody": "treble E4Eighth G4Quarter G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Half E4Eighth G4Half E4Eighth G4Eighth A4Eighth G4Eighth E4Eighth G4Half" + "melody": "Treble E4Eighth G4Quarter G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Half E4Eighth G4Half E4Eighth G4Eighth A4Eighth G4Eighth E4Eighth G4Half" }, { "title": "Que llueva, que llueva", "_origin": qsTr("Spain"), "lyrics": "Que llueva, que llueva. La Virgen de la Cueva. Los pajaritos cantan, Las nubes se levantan. ¡Que sí, que no, que caiga un chaparrón! Que siga lloviendo, Los pájaros corriendo. Florezca la pradera. Al sol de la primavera....", - "melody": "treble E4Eighth G4Quarter G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Half E4Eighth G4Half E4Eighth G4Eighth A4Eighth G4Eighth E4Eighth G4Half" + "melody": "Treble E4Eighth G4Quarter G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Eighth G4Eighth G4Eighth A4Eighth G4Quarter G4Eighth E4Eighth G4Half E4Eighth G4Half E4Eighth G4Eighth A4Eighth G4Eighth E4Eighth G4Half" }, { "title": "Backe, backe Kuchen", "_origin": qsTr("German Kid's Song"), "lyrics": "Backe, backe, Kuchen Der Bäcker hat gerufen! Wer will guten Kuchen backen Der muß haben sieben Sachen: Eier und Schmalz Butter und Salz Milch und Mehl/Safran macht den Kuchen gehl!Schieb, schieb in´n Ofen ´nein.", - "melody": "treble A4Quarter A4Quarter B4Quarter B4Quarter A4Half F#4Quarter D4Quarter A4Quarter A4Quarter B4Quarter B4Quarter A4Half F#4Half A4Quarter A4Quarter B4Quarter B4Quarter A4Quarter A4Quarter F#4Quarter D4Quarter A4Quarter A4Quarter B4Quarter B4Quarter A4Quarter A4Quarter F#4Quarter F#4Quarter A4Eighth A4Eighth A4Quarter F#4Half A4Eighth A4Eighth A4Quarter F#4Half A4Quarter A4Quarter F#4Half" + "melody": "Treble A4Quarter A4Quarter B4Quarter B4Quarter A4Half F#4Quarter D4Quarter A4Quarter A4Quarter B4Quarter B4Quarter A4Half F#4Half A4Quarter A4Quarter B4Quarter B4Quarter A4Quarter A4Quarter F#4Quarter D4Quarter A4Quarter A4Quarter B4Quarter B4Quarter A4Quarter A4Quarter F#4Quarter F#4Quarter A4Eighth A4Eighth A4Quarter F#4Half A4Eighth A4Eighth A4Quarter F#4Half A4Quarter A4Quarter F#4Half" }, { "title": "Se essa rua fosse minha", "_origin": qsTr("Children's Song from Brazil"), "lyrics": "Se essa rua Se essa rua fosse minha Eu mandava Eu mandava ladrilhar Com pedrinhas Com pedrinhas de brilhante Só pra ver Só pra ver meu bem passar Nessa rua Nessa rua tem um bosque Que se chama Que se chama solidão...", - "melody": "treble E4Eighth E4Eighth A4Eighth A4Eighth E4Eighth C4Eighth A4Eighth C4Eighth F4Eighth E4Eighth E4Quarter D4Quarter E4Eighth E4Eighth B4Eighth B4Eighth F4Eighth E4Eighth G#4Eighth E4Eighth C4Eighth B4Eighth A4Half A4Eighth A4Eighth Bb4Eighth A4Eighth E4Eighth C#4Eighth A4Eighth G4Eighth F4Eighth E4Eighth G4Eighth F4Eighth E4Eighth D4Eighth E4Quarter B4Eighth G#4Eighth E4Eighth D4Eighth C4Eighth B4Eighth A4Half" + "melody": "Treble E4Eighth E4Eighth A4Eighth A4Eighth E4Eighth C4Eighth A4Eighth C4Eighth F4Eighth E4Eighth E4Quarter D4Quarter E4Eighth E4Eighth B4Eighth B4Eighth F4Eighth E4Eighth G#4Eighth E4Eighth C4Eighth B4Eighth A4Half A4Eighth A4Eighth Bb4Eighth A4Eighth E4Eighth C#4Eighth A4Eighth G4Eighth F4Eighth E4Eighth G4Eighth F4Eighth E4Eighth D4Eighth E4Quarter B4Eighth G#4Eighth E4Eighth D4Eighth C4Eighth B4Eighth A4Half" }, { "title": "Fuchs du hast die Gans gestohlen", "_origin": qsTr("German"), "lyrics": "Fuchs, du hast die Gans gestohlen, gib sie wieder her, gib sie wieder her! Sonst wird dich der Jäger holen, mit dem Schießgewehr! Sonst wird dich der Jäger holen, mit dem Schießgewehr!", - "melody": "treble D4Quarter E4Quarter F#4Quarter G4Quarter A4Quarter A4Quarter A4Quarter A4Quarter B4Quarter G4Quarter D5Quarter B4Quarter A4Whole B4Quarter G4Quarter D5Quarter B4Quarter A4Whole A4Quarter G4Quarter G4Quarter G4Quarter G4Quarter F#4Quarter F#4Quarter F#4Quarter F#4Quarter E4Quarter F#4Quarter E4Quarter D4Quarter F#4Quarter A4Half" + "melody": "Treble D4Quarter E4Quarter F#4Quarter G4Quarter A4Quarter A4Quarter A4Quarter A4Quarter B4Quarter G4Quarter D5Quarter B4Quarter A4Whole B4Quarter G4Quarter D5Quarter B4Quarter A4Whole A4Quarter G4Quarter G4Quarter G4Quarter G4Quarter F#4Quarter F#4Quarter F#4Quarter F#4Quarter E4Quarter F#4Quarter E4Quarter D4Quarter F#4Quarter A4Half" }, { "title": "Un éléphant qui se balançait", "_origin": qsTr("France"), "lyrics": "Un éléphant qui se balançait\nSur une toile toile toile\nToile d'araignée\nIl trouvait ça tellement amusant\nQu'il alla chercher un\nDeuxième éléphant.", - "melody": "treble G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth A4Eighth G4Eighth F4Eighth E4Eighth F4Half D4Half F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth G4Eighth F4Eighth E4Eighth D4Eighth E4Half C4Half" + "melody": "Treble G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth E4Quarter E4Quarter G4Quarter G4Eighth G4Eighth A4Eighth G4Eighth F4Eighth E4Eighth F4Half D4Half F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth D4Quarter D4Quarter F4Quarter F4Eighth F4Eighth G4Eighth F4Eighth E4Eighth D4Eighth E4Half C4Half" }, { "title": "Alle Meine Entchen", "_origin": qsTr("Germany"), "lyrics": "Alle meine Entchen schwimmen auf dem See, schwimmen auf dem See, Köpfchen in das Wasser, Schwänzchen in die Höh'.", - "melody": "treble C4Eighth D4Eighth E4Eighth F4Eighth G4Quarter G4Quarter A4Eighth A4Eighth A4Eighth A4Eighth G4Half A4Eighth A4Eighth A4Eighth A4Eighth G4Half F4Eighth F4Eighth F4Eighth F4Eighth E4Quarter E4Quarter D4Eighth D4Eighth D4Eighth G4Eighth C4Half" + "melody": "Treble C4Eighth D4Eighth E4Eighth F4Eighth G4Quarter G4Quarter A4Eighth A4Eighth A4Eighth A4Eighth G4Half A4Eighth A4Eighth A4Eighth A4Eighth G4Half F4Eighth F4Eighth F4Eighth F4Eighth E4Quarter E4Quarter D4Eighth D4Eighth D4Eighth G4Eighth C4Half" }, { "title": "À la claire fontaine", "_origin": qsTr("France"), "lyrics": "À la claire fontaine\nM'en allant promener\nJ'ai trouvé l'eau si belle\nQue je m'y suis baigné\nIl y a longtemps que je t'aime\nJamais je ne t'oublierai.", - "melody": "treble G4Quarter G4Eighth B4Eighth B4Eighth A4Eighth B4Eighth B4Eighth G4Quarter G4Eighth B4Eighth B4Eighth A4Eighth B4Quarter B4Quarter B4Eighth A4Eighth G4Eighth B4Eighth D5Eighth B4Eighth D5Quarter D5Eighth B4Eighth G4Eighth B4Eighth A4Half G4Half G4Quarter B4Quarter B4Quarter A4Eighth G4Eighth B4Quarter G4Quarter B4Half B4Quarter A4Eighth G4Eighth B4Quarter A4Quarter G4Whole" + "melody": "Treble G4Quarter G4Eighth B4Eighth B4Eighth A4Eighth B4Eighth B4Eighth G4Quarter G4Eighth B4Eighth B4Eighth A4Eighth B4Quarter B4Quarter B4Eighth A4Eighth G4Eighth B4Eighth D5Eighth B4Eighth D5Quarter D5Eighth B4Eighth G4Eighth B4Eighth A4Half G4Half G4Quarter B4Quarter B4Quarter A4Eighth G4Eighth B4Quarter G4Quarter B4Half B4Quarter A4Eighth G4Eighth B4Quarter A4Quarter G4Whole" }, { "title": "O Cravo e a Rosa", "_origin": qsTr("Brazil"), "lyrics": "O Cravo brigou com a Rosa Debaixo de uma sacada O Cravo ficou ferido E a Rosa despedaçada O Cravo ficou doente A Rosa foi visitar O Cravo teve um desmaio A Rosa pôs-se a chorar", - "melody": "treble G4Quarter G4Quarter E4Quarter C5Eighth B4Eighth A4Eighth G4Quarter F4Half A4Quarter A4Quarter F4Quarter C5Eighth B4Eighth A4Eighth G4Quarter G4Half G4Quarter C5Quarter C5Quarter C5Eighth D5Eighth C5Eighth B4Quarter A4Half A4Quarter G4Quarter B4Half A4Eighth F4Eighth D4Eighth C4Quarter" + "melody": "Treble G4Quarter G4Quarter E4Quarter C5Eighth B4Eighth A4Eighth G4Quarter F4Half A4Quarter A4Quarter F4Quarter C5Eighth B4Eighth A4Eighth G4Quarter G4Half G4Quarter C5Quarter C5Quarter C5Eighth D5Eighth C5Eighth B4Quarter A4Half A4Quarter G4Quarter B4Half A4Eighth F4Eighth D4Eighth C4Quarter" }, { "title": "Marcha Soldado", "_origin": qsTr("Brazil"), "lyrics": "Marcha soldado Cabeça de papel Se não marchar direito Vai preso pro quartel O quartel pegou fogo A polícia deu sinal Acode acode acode A bandeira nacional", - "melody": "treble G4Quarter G4Eighth E4Eighth C4Quarter C4Eighth E4Eighth G4Eighth G4Eighth G4Eighth E4Eighth D4Half E4Eighth F4Eighth F4Eighth F4Eighth D4Eighth G4Quarter G4Eighth A4Eighth G4Eighth F4Eighth E4Eighth D4Eighth C4Half" + "melody": "Treble G4Quarter G4Eighth E4Eighth C4Quarter C4Eighth E4Eighth G4Eighth G4Eighth G4Eighth E4Eighth D4Half E4Eighth F4Eighth F4Eighth F4Eighth D4Eighth G4Quarter G4Eighth A4Eighth G4Eighth F4Eighth E4Eighth D4Eighth C4Half" }, { "title": "Frère jacques", "_origin": qsTr("France"), "lyrics": "Frère Jacques\nFrère Jacques\nDormez-vous ?\nDormez-vous ?\nSonnez les matines\nSonnez les matines\nDing ding dong\nDing ding dong", - "melody": "treble F4Quarter G4Quarter A4Quarter F4Quarter F4Quarter G4Quarter A4Quarter F4Quarter A4Quarter Bb4Quarter C5Half A4Quarter Bb4Quarter C5Half C5Eighth D5Eighth C5Eighth Bb4Eighth A4Quarter F4Quarter C5Eighth D5Eighth C5Eighth Bb4Eighth A4Quarter F4Quarter F4Quarter C4Quarter F4Half F4Quarter C4Quarter F4Half" + "melody": "Treble F4Quarter G4Quarter A4Quarter F4Quarter F4Quarter G4Quarter A4Quarter F4Quarter A4Quarter Bb4Quarter C5Half A4Quarter Bb4Quarter C5Half C5Eighth D5Eighth C5Eighth Bb4Eighth A4Quarter F4Quarter C5Eighth D5Eighth C5Eighth Bb4Eighth A4Quarter F4Quarter F4Quarter C4Quarter F4Half F4Quarter C4Quarter F4Half" }, { "title": "Au clair de la lune", "_origin": qsTr("France"), "lyrics": "Au clair de la lune\nMon ami Pierrot\nPrête-moi ta plume\nPour écrire un mot\nMa chandelle est morte\nJe n'ai plus de feu\nOuvre-moi ta porte\nPour l'amour de Dieu.", - "melody": "treble G4Quarter G4Quarter G4Quarter A4Quarter B4Half A4Half G4Quarter B4Quarter A4Quarter A4Quarter G4Whole G4Quarter G4Quarter G4Quarter A4Quarter B4Half A4Half G4Quarter B4Quarter A4Quarter A4Quarter G4Whole A4Quarter A4Quarter A4Quarter A4Quarter E4Half E4Half A4Quarter G4Quarter F#4Quarter E4Quarter D4Whole G4Quarter G4Quarter G4Quarter A4Quarter B4Half A4Half G4Quarter B4Quarter A4Quarter A4Quarter G4Whole" + "melody": "Treble G4Quarter G4Quarter G4Quarter A4Quarter B4Half A4Half G4Quarter B4Quarter A4Quarter A4Quarter G4Whole G4Quarter G4Quarter G4Quarter A4Quarter B4Half A4Half G4Quarter B4Quarter A4Quarter A4Quarter G4Whole A4Quarter A4Quarter A4Quarter A4Quarter E4Half E4Half A4Quarter G4Quarter F#4Quarter E4Quarter D4Whole G4Quarter G4Quarter G4Quarter A4Quarter B4Half A4Half G4Quarter B4Quarter A4Quarter A4Quarter G4Whole" }, { "title": "Boci, boci tarka", "_origin": qsTr("Hungary, Nursery Rhyme"), "lyrics": "Boci, boci tarka, Se füle, se farka, Oda megyünk lakni, Ahol tejet kapni.", - "melody": "treble D4Eighth F#4Eighth D4Eighth F#4Eighth A4Quarter A4Quarter D4Eighth F#4Eighth D4Eighth F#4Eighth A4Quarter A4Quarter D5Eighth C#5Eighth B4Eighth A4Eighth G4Quarter B4Quarter A4Eighth G4Eighth F#4Eighth E4Eighth D4Quarter D4Quarter" + "melody": "Treble D4Eighth F#4Eighth D4Eighth F#4Eighth A4Quarter A4Quarter D4Eighth F#4Eighth D4Eighth F#4Eighth A4Quarter A4Quarter D5Eighth C#5Eighth B4Eighth A4Eighth G4Quarter B4Quarter A4Eighth G4Eighth F#4Eighth E4Eighth D4Quarter D4Quarter" }, { "title": "Nyuszi ül a fűben", "_origin": qsTr("Hungary, Nursery Rhyme"), "lyrics": "Nyuszi ül a fűben, ülve szundikálva. Nyuszi talán beteg vagy, hogy már nem is ugorhatsz? Nyuszi hopp! Nyuszi hopp! Máris egyet elkapott.", - "melody": "treble A4Eighth A4Eighth A4Eighth B4Eighth A4Quarter F#4Quarter A4Eighth A4Eighth A4Eighth B4Eighth A4Eighth G4Eighth F#4Quarter F#4Eighth E4Eighth F#4Eighth E4Eighth D4Eighth D4Eighth D4Quarter A4Eighth A4Eighth D5Quarter A4Eighth A4Eighth D5Quarter F#4Eighth E4Eighth F#4Eighth E4Eighth D4Eighth D4Eighth D4Quarter" + "melody": "Treble A4Eighth A4Eighth A4Eighth B4Eighth A4Quarter F#4Quarter A4Eighth A4Eighth A4Eighth B4Eighth A4Eighth G4Eighth F#4Quarter F#4Eighth E4Eighth F#4Eighth E4Eighth D4Eighth D4Eighth D4Quarter A4Eighth A4Eighth D5Quarter A4Eighth A4Eighth D5Quarter F#4Eighth E4Eighth F#4Eighth E4Eighth D4Eighth D4Eighth D4Quarter" }, { "title": "Lánc, lánc, eszterlánc", "_origin": qsTr("Hungary, Children's Song"), "lyrics": "Lánc, lánc, eszterlánc,/ eszterlánci cérna,/ cérna volna, selyem volna,/ mégis kifordulna. / Pénz volna karika, karika,/ forduljon ki Marika,/ Marikának lánca.", - "melody": "treble A4Quarter B4Quarter A4Eighth A4Eighth F#4Quarter A4Eighth A4Eighth A4Eighth B4Eighth A4Quarter F#4Quarter A4Eighth A4Eighth A4Eighth B4Eighth A4Eighth G4Eighth F#4Eighth E4Eighth F#4Eighth E4Eighth F#4Eighth E4Eighth D4Quarter D4Quarter A4Quarter A4Eighth A4Eighth A4Eighth D5Eighth A4Quarter A4Eighth G4Eighth F#4Eighth E4Eighth D4Eighth D5Eighth A4Quarter A4Eighth G4Eighth F#4Eighth E4Eighth D4Quarter D4Quarter" + "melody": "Treble A4Quarter B4Quarter A4Eighth A4Eighth F#4Quarter A4Eighth A4Eighth A4Eighth B4Eighth A4Quarter F#4Quarter A4Eighth A4Eighth A4Eighth B4Eighth A4Eighth G4Eighth F#4Eighth E4Eighth F#4Eighth E4Eighth F#4Eighth E4Eighth D4Quarter D4Quarter A4Quarter A4Eighth A4Eighth A4Eighth D5Eighth A4Quarter A4Eighth G4Eighth F#4Eighth E4Eighth D4Eighth D5Eighth A4Quarter A4Eighth G4Eighth F#4Eighth E4Eighth D4Quarter D4Quarter" }, { "title": "Tavaszi szél vizet áraszt", "_origin": qsTr("Hungary, Children's Song"), "lyrics": "Tavaszi szél vizet áraszt, virágom, virágom. / Minden madár társat választ virágom, virágom. / Hát én immár kit válasszak? Virágom, virágom. / Te engemet, én tégedet, virágom, virágom.", - "melody": "treble F4Quarter G4Quarter A4Quarter A4Quarter G4Quarter G4Eighth A4Eighth F4Quarter G4Quarter A4Eighth A4Quarter G4Quarter G4Eighth A4Eighth F4Half C4Half F4Quarter G4Quarter A4Eighth A4Quarter G4Quarter G4Eighth A4Eighth F4Eighth E4Eighth D4Quarter G4Eighth G4Quarter A4Eighth F4Quarter E4Quarter D4Half D4Half" + "melody": "Treble F4Quarter G4Quarter A4Quarter A4Quarter G4Quarter G4Eighth A4Eighth F4Quarter G4Quarter A4Eighth A4Quarter G4Quarter G4Eighth A4Eighth F4Half C4Half F4Quarter G4Quarter A4Eighth A4Quarter G4Quarter G4Eighth A4Eighth F4Eighth E4Eighth D4Quarter G4Eighth G4Quarter A4Eighth F4Quarter E4Quarter D4Half D4Half" }, { "title": "Zec kopa repu", "_origin": qsTr("Serbia"), "lyrics": "Zec kopa repu, a lisica cveklu / Vuk im se prikrade, zeca da ukrade / Zec spazi repu, a lisica cveklu / Vuka nije bilo, u snu im se snilo", - "melody": "treble A4Half A4Quarter F#4Quarter G4Half E4Half D4Quarter E4Quarter E4Quarter E4Quarter E4Half D4Half F4Quarter F4Quarter E4Quarter F4Quarter G4Half E4Half F4Quarter F4Quarter E4Quarter D4Quarter E4Half D4Half" + "melody": "Treble A4Half A4Quarter F#4Quarter G4Half E4Half D4Quarter E4Quarter E4Quarter E4Quarter E4Half D4Half F4Quarter F4Quarter E4Quarter F4Quarter G4Half E4Half F4Quarter F4Quarter E4Quarter D4Quarter E4Half D4Half" }, { "title": "Jack and Jill", "_origin": qsTr("Britain"), "lyrics": "Jack and Jill went up the hill / To fetch a pail of water. / Jack fell down and broke his crown,/ And Jill came tumbling after", - "melody": "treble C4Quarter C4Eighth D4Quarter D4Eighth E4Quarter E4Eighth F4Quarter F4Eighth G4Quarter G4Eighth A4Quarter A4Eighth B4Half C5Half C5Quarter C5Eighth B4Quarter B4Eighth A4Quarter A4Eighth G4Quarter G4Eighth F4Quarter F4Eighth E4Quarter E4Eighth D4Half C4Half" + "melody": "Treble C4Quarter C4Eighth D4Quarter D4Eighth E4Quarter E4Eighth F4Quarter F4Eighth G4Quarter G4Eighth A4Quarter A4Eighth B4Half C5Half C5Quarter C5Eighth B4Quarter B4Eighth A4Quarter A4Eighth G4Quarter G4Eighth F4Quarter F4Eighth E4Quarter E4Eighth D4Half C4Half" }, { "title": "Wlazł kotek na płotek", "_origin": qsTr("Poland"), "lyrics": "Wlazł kotek na płotek i mruga,/ Ładna to piosenka nie długa. / Nie długa, nie krótka, lecz w sam raz,/ Zaśpiewaj koteczku jeszcze raz.", - "melody": "treble G4Quarter E4Quarter E4Quarter F4Quarter D4Quarter D4Quarter C4Eighth E4Eighth G4Half G4Quarter E4Quarter E4Quarter F4Quarter D4Quarter D4Quarter C4Eighth E4Eighth C4Half" + "melody": "Treble G4Quarter E4Quarter E4Quarter F4Quarter D4Quarter D4Quarter C4Eighth E4Eighth G4Half G4Quarter E4Quarter E4Quarter F4Quarter D4Quarter D4Quarter C4Eighth E4Eighth C4Half" }, { "title": "Φεγγαράκι μου λαμπρό", "_origin": qsTr("Greece"), "lyrics": "Φεγγαράκι μου λαμπρό,/ Φέγγε μου να περπατώ,/ Να πηγαίνω στο σχολειό / Να μαθαίνω γράμματα,/ Γράμματα σπουδάματα / Του Θεού τα πράματα.", - "melody": "treble C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Whole" + "melody": "Treble C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half G4Quarter G4Quarter F4Quarter F4Quarter E4Quarter E4Quarter D4Half C4Quarter C4Quarter G4Quarter G4Quarter A4Quarter A4Quarter G4Half F4Quarter F4Quarter E4Quarter E4Quarter D4Quarter D4Quarter C4Whole" }, { "title": "Βρέχει, χιονίζει", "_origin": qsTr("Greece"), "lyrics": "Βρέχει, χιονίζει,/ Τα μάρμαρα ποτίζει,/ Κι' ο γέρος πα στα ξύλα / Κ' η γριά του μαγειρεύει / Κουρκούτι με το μέλι. / \"'Ελα γέρο μου να φας / Πού να φας την / κουτσουλιά,/ Και του σκύλλου την οριά.\"", - "melody": "treble D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth" + "melody": "Treble D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth D4Eighth G4Eighth G4Eighth G4Eighth A4Eighth A4Eighth G4Eighth G4Eighth" }, { "title": "Котику сіренький", "_origin": qsTr("Ukraine"), "lyrics": "Котику сіренький,/ Котику біленький,/ Котку волохатий,/ Не ходи по хаті! / Не ходи по хаті,/ Не буди дитяти! / Дитя буде спати,/ Котик воркотати. / Ой на кота воркота,/ На дитину дрімота. / А-а, люлі!", - "melody": "treble C4Quarter F4Quarter G4Quarter Bb4Eighth Ab4Eighth G4Quarter F4Quarter C4Quarter F4Quarter G4Quarter Bb4Eighth Ab4Eighth G4Half F4Half Bb4Quarter Bb4Quarter Bb4Quarter Ab4Quarter G4Half F4Half Bb4Quarter Bb4Quarter Bb4Quarter Ab4Quarter G4Half F4Half C4Quarter F4Quarter G4Quarter Bb4Eighth Ab4Eighth G4Eighth F4Eighth G4Quarter F4Half" + "melody": "Treble C4Quarter F4Quarter G4Quarter Bb4Eighth Ab4Eighth G4Quarter F4Quarter C4Quarter F4Quarter G4Quarter Bb4Eighth Ab4Eighth G4Half F4Half Bb4Quarter Bb4Quarter Bb4Quarter Ab4Quarter G4Half F4Half Bb4Quarter Bb4Quarter Bb4Quarter Ab4Quarter G4Half F4Half C4Quarter F4Quarter G4Quarter Bb4Eighth Ab4Eighth G4Eighth F4Eighth G4Quarter F4Half" }, { "title": "Baa, Baa, Blacksheep", "_origin": qsTr("Britain"), "lyrics": "Baa, baa, black sheep, / Have you any wool? / Yes, sir, yes, sir, / Three bags full; / One for the master, / And one for the dame, / And one for the little boy / Who lives down the lane", - "melody": "treble G4Quarter G4Quarter D5Quarter D5Quarter E5Eighth E5Eighth E5Eighth E5Eighth D5Half C5Quarter C5Quarter B4Quarter B4Quarter A4Quarter A4Quarter G4Half D5Quarter D5Eighth D5Eighth C5Quarter C5Quarter B4Quarter B4Eighth B4Eighth A4Half D5Quarter D5Eighth D5Eighth C5Eighth C5Eighth C5Eighth C5Eighth B4Quarter B4Eighth B4Eighth A4Half" + "melody": "Treble G4Quarter G4Quarter D5Quarter D5Quarter E5Eighth E5Eighth E5Eighth E5Eighth D5Half C5Quarter C5Quarter B4Quarter B4Quarter A4Quarter A4Quarter G4Half D5Quarter D5Eighth D5Eighth C5Quarter C5Quarter B4Quarter B4Eighth B4Eighth A4Half D5Quarter D5Eighth D5Eighth C5Eighth C5Eighth C5Eighth C5Eighth B4Quarter B4Eighth B4Eighth A4Half" }, { "title": "A la rueda de San Miguel", "_origin": qsTr("Mexico"), "lyrics": "A la rueda, a la rueda de San Miguel / Todos traen su caja de miel. / A lo maduro, a lo maduro / Que se voltee (nombre del niño) de burro.", - "melody": "treble G4Quarter G4Quarter C5Quarter G4Quarter C5Quarter G4Eighth G4Eighth C5Quarter D5Quarter E5Half C5Quarter D5Quarter E5Half C5Quarter C5Quarter D5Quarter D5Eighth D5Eighth G4Quarter G4Eighth G4Eighth C5Half" + "melody": "Treble G4Quarter G4Quarter C5Quarter G4Quarter C5Quarter G4Eighth G4Eighth C5Quarter D5Quarter E5Half C5Quarter D5Quarter E5Half C5Quarter C5Quarter D5Quarter D5Eighth D5Eighth G4Quarter G4Eighth G4Eighth C5Half" }, { "title": "Arroz con leche", "_origin": qsTr("Mexico"), "lyrics": "Arroz con leche / me quiero casar / con una mexicana / de la capital.", - "melody": "treble C4Eighth F4Half A4Eighth F4Quarter F4Eighth C4Eighth F4Quarter F4Eighth A4Eighth G4Half A4Eighth Bb4Eighth A4Eighth G4Eighth F4Eighth E4Quarter E4Eighth C4Eighth D4Quarter E4Eighth E4Eighth F4Quarter" + "melody": "Treble C4Eighth F4Half A4Eighth F4Quarter F4Eighth C4Eighth F4Quarter F4Eighth A4Eighth G4Half A4Eighth Bb4Eighth A4Eighth G4Eighth F4Eighth E4Quarter E4Eighth C4Eighth D4Quarter E4Eighth E4Eighth F4Quarter" }, { "title": "Los pollitos dicen", "_origin": qsTr("Mexico"), "lyrics": "Los pollitos dicen / pío pío pío / cuando tienen hambre / cuando tienen frío", - "melody": "treble A4Eighth E4Eighth A4Eighth B4Eighth C#5Quarter C#5Quarter B4Eighth D5Eighth C#5Eighth B4Eighth A4Quarter A4Quarter A4Eighth G#4Eighth F#4Eighth E4Eighth B4Quarter B4Quarter E4Eighth E4Eighth F#4Eighth G#4Eighth A4Quarter A4Quarter" + "melody": "Treble A4Eighth E4Eighth A4Eighth B4Eighth C#5Quarter C#5Quarter B4Eighth D5Eighth C#5Eighth B4Eighth A4Quarter A4Quarter A4Eighth G#4Eighth F#4Eighth E4Eighth B4Quarter B4Quarter E4Eighth E4Eighth F#4Eighth G#4Eighth A4Quarter A4Quarter" }, { "title": "Dale dale dale", "_origin": qsTr("Mexican song to break a piñata"), "lyrics": "Dale dale dale / no pierdas el tino / porque si lo pierdes / pierdes el camino", - "melody": "treble G4Eighth G4Eighth G4Eighth E4Eighth A4Quarter A4Quarter B4Eighth B4Eighth B4Eighth G4Eighth C5Quarter C5Quarter G4Eighth G4Eighth G4Eighth E4Eighth A4Quarter A4Quarter B4Eighth B4Eighth B4Eighth G4Eighth C5Half" + "melody": "Treble G4Eighth G4Eighth G4Eighth E4Eighth A4Quarter A4Quarter B4Eighth B4Eighth B4Eighth G4Eighth C5Quarter C5Quarter G4Eighth G4Eighth G4Eighth E4Eighth A4Quarter A4Quarter B4Eighth B4Eighth B4Eighth G4Eighth C5Half" }, { "title": "Kolme varista", "_origin": qsTr("Finland"), "lyrics": "Kolme varista istui aidalla. Silivati seilaa, silivati seilaa, yksi lensi pois. Kaksi varista istui aidalla. Silivati seilaa, silivati seilaa, yksi lensi pois. Yksi varis vain istui aidalla. Silivati seilaa, silivati seilaa, sekin lensi pois.", - "melody": "treble A4Quarter Bb4Quarter A4Eighth F4Half D4Whole A4Quarter D4Quarter C5Quarter Bb4Quarter G4Whole A4Eighth A4Eighth A4Eighth G4Eighth E4Quarter G4Quarter A4Eighth A4Eighth A4Eighth G4Eighth E4Quarter G4Quarter A4Half G4Eighth F4Quarter E4Quarter D4Quarter" + "melody": "Treble A4Quarter Bb4Quarter A4Eighth F4Half D4Whole A4Quarter D4Quarter C5Quarter Bb4Quarter G4Whole A4Eighth A4Eighth A4Eighth G4Eighth E4Quarter G4Quarter A4Eighth A4Eighth A4Eighth G4Eighth E4Quarter G4Quarter A4Half G4Eighth F4Quarter E4Quarter D4Quarter" }, { "title": "Humpty Dumpty", "_origin": qsTr("Britain"), "lyrics": "Humpty Dumpty sat on a wall, / Humpty Dumpty had a great fall. / All the king's horses and all the king's men / Couldn't put Humpty together again", - "melody": "treble E4Quarter G4Eighth F4Quarter A4Eighth G4Eighth A4Eighth B4Eighth C5Half E4Quarter G4Eighth F4Quarter A4Eighth G4Eighth F4Eighth E4Eighth D4Half E4Eighth E4Eighth G4Eighth F4Eighth F4Eighth A4Eighth G4Eighth A4Eighth B4Eighth C5Quarter C5Eighth E5Eighth E5Eighth C5Eighth F5Quarter E5Eighth D5Eighth C5Eighth B4Eighth C5Half" + "melody": "Treble E4Quarter G4Eighth F4Quarter A4Eighth G4Eighth A4Eighth B4Eighth C5Half E4Quarter G4Eighth F4Quarter A4Eighth G4Eighth F4Eighth E4Eighth D4Half E4Eighth E4Eighth G4Eighth F4Eighth F4Eighth A4Eighth G4Eighth A4Eighth B4Eighth C5Quarter C5Eighth E5Eighth E5Eighth C5Eighth F5Quarter E5Eighth D5Eighth C5Eighth B4Eighth C5Half" } ]; } diff --git a/src/activities/piano_composition/piano_composition.js b/src/activities/piano_composition/piano_composition.js --- a/src/activities/piano_composition/piano_composition.js +++ b/src/activities/piano_composition/piano_composition.js @@ -23,12 +23,10 @@ .import QtQuick 2.6 as Quick .import GCompris 1.0 as GCompris .import "qrc:/gcompris/src/core/core.js" as Core -.import "qrc:/gcompris/src/activities/piano_composition/NoteNotations.js" as NoteNotations var currentLevel = 0 var numberOfLevel = 7 var items -var notesDetails = NoteNotations.get() var userDir = "file://" + GCompris.ApplicationInfo.getSharedWritablePath() + "/" + "piano_composition" var userFile = userDir + "/melodies.json" var undoStack = [] @@ -92,9 +90,9 @@ items.bar.level = currentLevel + 1 if(items.bar.level === 2) - items.background.clefType = "bass" + items.background.clefType = "Bass" else - items.background.clefType = "treble" + items.background.clefType = "Treble" if(items.bar.level === 4) items.piano.useSharpNotation = false @@ -105,7 +103,7 @@ items.multipleStaff.nbStaves = 2 items.background.staffMode = "add" items.multipleStaff.noteToReplace = -1 - items.staffModesOptions.currentIndex = 0 + items.optionsRow.staffModeIndex = 0 items.lyricsArea.resetLyricsArea() undoStack = [] } @@ -125,21 +123,6 @@ } } -function getNoteDetails(noteName, noteType) { - var clef = items.background.clefType === 'treble' ? "Treble" : "Bass" - var noteNotation - if(noteType === "Rest") - noteNotation = noteName + noteType - else - noteNotation = clef + noteName - console.log(noteNotation) - for(var i = 0; i < notesDetails.length; i++) { - if(noteNotation === notesDetails[i].noteName) { - return notesDetails[i] - } - } -} - function nextLevel() { items.multipleStaff.eraseAllNotes() if(numberOfLevel <= ++currentLevel) { diff --git a/src/activities/piano_composition/resource/background/1.jpg b/src/activities/piano_composition/resource/background/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@Each level has 5 sublevels. Levels 1-5 will offer treble clef to practice and levels 6-10 will offer bass clef.") + manual: qsTr("The notes you see will be played to you. Click on the corresponding keys on the keyboard that match the notes you hear and see.
Each level has 5 sublevels. Levels 1-6 will offer treble clef to practice and levels 7-11 will offer bass clef.") credit: "" section: "discovery sound_group" createdInVersion: 9500 diff --git a/src/activities/play_piano/PlayPiano.qml b/src/activities/play_piano/PlayPiano.qml --- a/src/activities/play_piano/PlayPiano.qml +++ b/src/activities/play_piano/PlayPiano.qml @@ -20,8 +20,11 @@ * along with this program; if not, see . */ import QtQuick 2.6 +import QtQuick.Controls 1.5 +import GCompris 1.0 import "../../core" +import "../piano_composition" import "play_piano.js" as Activity ActivityBase { @@ -30,34 +33,310 @@ onStart: focus = true onStop: {} - pageComponent: Rectangle { + property bool horizontalLayout: width > height + + pageComponent: Image { id: background anchors.fill: parent - color: "#ABCDEF" + fillMode: Image.PreserveAspectCrop signal start signal stop + property string backgroundImagesUrl: ":/gcompris/src/activities/piano_composition/resource/background/" + property var backgroundImages: directory.getFiles(backgroundImagesUrl) + + Directory { + id: directory + } + + source:{ + if(items.bar.level === 0) + return "qrc" + backgroundImagesUrl + backgroundImages[0] + else + return "qrc" + backgroundImagesUrl + backgroundImages[(items.bar.level - 1) % backgroundImages.length] + } + Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } + Keys.onPressed: { + if(event.key === Qt.Key_1) { + piano.whiteKeyRepeater.itemAt(0).keyPressed() + } + if(event.key === Qt.Key_2) { + piano.whiteKeyRepeater.itemAt(1).keyPressed() + } + if(event.key === Qt.Key_3) { + piano.whiteKeyRepeater.itemAt(2).keyPressed() + } + if(event.key === Qt.Key_4) { + piano.whiteKeyRepeater.itemAt(3).keyPressed() + } + if(event.key === Qt.Key_5) { + piano.whiteKeyRepeater.itemAt(4).keyPressed() + } + if(event.key === Qt.Key_6) { + piano.whiteKeyRepeater.itemAt(5).keyPressed() + } + if(event.key === Qt.Key_7) { + piano.whiteKeyRepeater.itemAt(6).keyPressed() + } + if(event.key === Qt.Key_8) { + piano.whiteKeyRepeater.itemAt(7).keyPressed() + } + if(event.key === Qt.Key_F1 && piano.blackKeysEnabled) { + piano.blackKeyRepeater.itemAt(0).keyPressed() + } + if(event.key === Qt.Key_F2 && piano.blackKeysEnabled) { + piano.blackKeyRepeater.itemAt(1).keyPressed() + } + if(event.key === Qt.Key_F3 && piano.blackKeysEnabled) { + piano.blackKeyRepeater.itemAt(2).keyPressed() + } + if(event.key === Qt.Key_F4 && piano.blackKeysEnabled) { + piano.blackKeyRepeater.itemAt(3).keyPressed() + } + if(event.key === Qt.Key_F5 && piano.blackKeysEnabled) { + piano.blackKeyRepeater.itemAt(4).keyPressed() + } + if(event.key === Qt.Key_Space) { + multipleStaff.play() + } + if(event.key === Qt.Key_Backspace || event.key === Qt.Key_Delete) { + Activity.undoPreviousAnswer() + } + } + // 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 multipleStaff: multipleStaff + property alias piano: piano property alias bar: bar property alias bonus: bonus + property alias score: score + property alias iAmReady: iAmReady + property string mode: "coloredNotes" } - onStart: { Activity.start(items) } + onStart: { + dialogActivityConfig.getInitialConfiguration() + Activity.start(items) + } onStop: { Activity.stop() } - GCText { - anchors.centerIn: parent - text: "play_piano activity" - fontSize: largeSize + property string clefType: (items.bar.level <= 6) ? "Treble" : "Bass" + readonly property bool shiftButtonsVisible: [4, 5, 6, 9, 10, 11].indexOf(items.bar.level) != -1 + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.3 + visible: iAmReady.visible + z: 10 + MouseArea { + anchors.fill: parent + } + } + + ReadyButton { + id: iAmReady + focus: true + z: 10 + onClicked: { + Activity.initLevel() + } + } + + Score { + id: score + anchors.top: background.top + anchors.bottom: undefined + numberOfSubLevels: 5 + width: horizontalLayout ? parent.width / 10 : (parent.width - instruction.x - instruction.width - 1.5 * anchors.rightMargin) + } + + Rectangle { + id: instruction + radius: 10 + width: background.width * 0.6 + height: background.height / 9 + anchors.horizontalCenter: parent.horizontalCenter + opacity: 0.8 + border.width: 6 + color: "white" + border.color: "#87A6DD" + + GCText { + color: "black" + z: 3 + anchors.fill: parent + anchors.rightMargin: parent.width * 0.02 + anchors.leftMargin: parent.width * 0.02 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + fontSizeMode: Text.Fit + wrapMode: Text.WordWrap + text: qsTr("Click on the piano keys that match the given notes.") + } + } + + MultipleStaff { + id: multipleStaff + width: horizontalLayout ? parent.width * 0.4 : parent.width * 0.8 + height: horizontalLayout ? parent.height * 0.7 : parent.height * 0.58 + nbStaves: 1 + clef: clefType + nbMaxNotesPerStaff: 10 + noteIsColored: items.mode === "coloredNotes" + isMetronomeDisplayed: false + isFlickable: false + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: instruction.bottom + anchors.topMargin: horizontalLayout ? 0 : parent.height * 0.09 + onNoteClicked: playNoteAudio(noteName, noteType) + noteHoverEnabled: false + centerNotesPosition: true + } + + Piano { + id: piano + width: horizontalLayout ? parent.width * 0.4 : parent.width * 0.7 + height: horizontalLayout ? parent.height * 0.3 : parent.width * 0.26 + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: bar.top + anchors.bottomMargin: 20 + blackLabelsVisible: ([4, 5, 6, 9, 10, 11].indexOf(items.bar.level) != -1) + useSharpNotation: (bar.level != 5) && (bar.level != 10) + blackKeysEnabled: blackLabelsVisible && !multipleStaff.isMusicPlaying + whiteKeysEnabled: !multipleStaff.isMusicPlaying + onNoteClicked: Activity.checkAnswer(note) + currentOctaveNb: 0 + } + + Image { + id: shiftKeyboardLeft + source: "qrc:/gcompris/src/core/resource/bar_next.svg" + sourceSize.width: piano.width / 7 + width: sourceSize.width + height: width + fillMode: Image.PreserveAspectFit + rotation: 180 + visible: (piano.currentOctaveNb > 0) && shiftButtonsVisible + anchors { + verticalCenter: piano.verticalCenter + right: piano.left + } + MouseArea { + anchors.fill: parent + onClicked: piano.currentOctaveNb-- + } + } + + Image { + id: shiftKeyboardRight + source: "qrc:/gcompris/src/core/resource/bar_next.svg" + sourceSize.width: piano.width / 7 + width: sourceSize.width + height: width + fillMode: Image.PreserveAspectFit + visible: (piano.currentOctaveNb < piano.maxNbOctaves - 1) && shiftButtonsVisible + anchors { + verticalCenter: piano.verticalCenter + left: piano.right + } + MouseArea { + anchors.fill: parent + onClicked: piano.currentOctaveNb++ + } + } + + Rectangle { + id: optionDeck + width: optionsRow.changeAccidentalStyleButtonVisible ? optionsRow.iconsWidth * 5 : optionsRow.iconsWidth * 4 + height: optionsRow.iconsWidth * 1.7 + border.width: 2 + border.color: "black" + color: "black" + opacity: 0.5 + radius: 10 + y: horizontalLayout ? background.height / 2 - height : instruction.height + 10 + x: horizontalLayout ? background.width - width - 25 : background.width / 2 - width / 2 + } + + OptionsRow { + id: optionsRow + anchors.top: optionDeck.top + anchors.topMargin: 15 + anchors.horizontalCenter: optionDeck.horizontalCenter + iconsWidth: horizontalLayout ? Math.min(50, (background.width - piano.x - piano.width) / 5) : 45 + + playButtonVisible: true + undoButtonVisible: true + changeAccidentalStyleButtonVisible: [6, 11].indexOf(items.bar.level) != -1 + + onUndoButtonClicked: Activity.undoPreviousAnswer() + } + + ExclusiveGroup { + id: configOptions + } + + DialogActivityConfig { + id: dialogActivityConfig + content: Component { + Column { + id: column + spacing: 5 + width: dialogActivityConfig.width + height: dialogActivityConfig.height + + property alias coloredNotesModeBox: coloredNotesModeBox + property alias colorlessNotesModeBox: colorlessNotesModeBox + + GCDialogCheckBox { + id: coloredNotesModeBox + width: column.width - 50 + text: qsTr("Display colored notes.") + checked: items.mode === "coloredNotes" + exclusiveGroup: configOptions + onCheckedChanged: { + if(coloredNotesModeBox.checked) { + items.mode = "coloredNotes" + } + } + } + + GCDialogCheckBox { + id: colorlessNotesModeBox + width: coloredNotesModeBox.width + text: qsTr("Display colorless notes.") + checked: items.mode === "colorlessNotes" + exclusiveGroup: configOptions + onCheckedChanged: { + if(colorlessNotesModeBox.checked) { + items.mode = "colorlessNotes" + } + } + } + } + } + onLoadData: { + if(dataToSave && dataToSave["mode"]) + items.mode = dataToSave["mode"] + } + onSaveData: dataToSave["mode"] = items.mode + onClose: { + multipleStaff.eraseAllNotes() + iAmReady.visible = true + Activity.currentLevel = 0 + home() + } } DialogHelp { @@ -67,19 +346,24 @@ Bar { id: bar - content: BarEnumContent { value: help | home | level } - onHelpClicked: { - displayDialog(dialogHelp) - } + content: BarEnumContent { value: help | home | level | config | reload } + onHelpClicked: displayDialog(dialogHelp) onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() + onConfigClicked: { + dialogActivityConfig.active = true + displayDialog(dialogActivityConfig) + } + onReloadClicked: { + multipleStaff.eraseAllNotes() + iAmReady.visible = true + } } Bonus { id: bonus - Component.onCompleted: win.connect(Activity.nextLevel) + Component.onCompleted: win.connect(Activity.nextSubLevel) } } - } diff --git a/src/activities/play_piano/dataset.js b/src/activities/play_piano/dataset.js new file mode 100644 --- /dev/null +++ b/src/activities/play_piano/dataset.js @@ -0,0 +1,103 @@ +/* GCompris - dataset.js + * + * Copyright (C) 2018 Aman Kumar Gupta + * + * Authors: + * Beth Hadley (GTK+ version) + * 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 . + **/ + +function getData() { + return [ + [ + "Treble G3Quarter G3Quarter F4Quarter", + "Treble A3Quarter A3Quarter G4Quarter", + "Treble B3Quarter E4Quarter G3Quarter", + "Treble F4Quarter A3Quarter C4Quarter B3Quarter", + "Treble D4Quarter A3Quarter G4Quarter E4Quarter" + ], + [ + "Treble C4Quarter E4Quarter E4Quarter", + "Treble A4Quarter A4Quarter G4Quarter", + "Treble B4Quarter D4Quarter F4Quarter", + "Treble F4Quarter A4Quarter A4Quarter F4Quarter", + "Treble D4Quarter C4Quarter G4Quarter E4Quarter" + ], + [ + "Treble C5Quarter E5Quarter E5Quarter", + "Treble A5Quarter A5Quarter G5Quarter", + "Treble B5Quarter D5Quarter F5Quarter", + "Treble F5Quarter A5Quarter A5Quarter F5Quarter", + "Treble D5Quarter C5Quarter G5Quarter E5Quarter" + ], + [ + "Treble C#5Quarter D4Quarter D#4Quarter", + "Treble A3Quarter A4Quarter G#5Quarter", + "Treble B3Quarter D#5Quarter F#5Quarter", + "Treble F#5Quarter A3Quarter A3Quarter F#4Quarter", + "Treble D5Quarter C#4Quarter G#3Quarter D#4Quarter" + ], + [ + "Treble Db5Quarter D4Quarter Bb3Quarter", + "Treble A3Quarter A4Quarter Bb5Quarter", + "Treble B3Quarter Db5Quarter Ab5Quarter", + "Treble Ab3Quarter Ab3Quarter A3Quarter D4Quarter", + "Treble D5Quarter Db5Quarter Gb4Quarter Bb3Quarter" + ], + [ + "Treble C#5Quarter D4Quarter Ab3Quarter", + "Treble A3Quarter A4Quarter G#5Quarter", + "Treble B3Quarter D#5Quarter Ab5Quarter", + "Treble Ab3Quarter G#3Quarter A3Quarter D4Quarter", + "Treble Ab5Quarter C#4Quarter Gb4Quarter F#5Quarter" + ], + [ + "Bass C3Quarter A3Quarter D3Quarter", + "Bass G3Quarter B3Quarter E3Quarter", + "Bass F3Quarter A3Quarter B3Quarter", + "Bass G3Quarter E3Quarter E3Quarter F3Quarter", + "Bass C3Quarter F3Quarter G3Quarter B3Quarter" + ], + [ + "Bass C4Quarter F4Quarter B3Quarter", + "Bass A3Quarter G4Quarter C4Quarter", + "Bass D4Quarter E4Quarter B3Quarter", + "Bass G3Quarter C4Quarter E4Quarter F4Quarter", + "Bass G4Quarter F4Quarter A3Quarter D4Quarter" + ], + [ + "Bass C3Quarter C#3Quarter D#4Quarter", + "Bass A3Quarter G#3Quarter E3Quarter", + "Bass F3Quarter F#4Quarter F#3Quarter", + "Bass G3Quarter E4Quarter E3Quarter F#3Quarter", + "Bass G4Quarter F3Quarter F#4Quarter C#4Quarter" + ], + [ + "Bass A3Quarter Ab3Quarter Db4Quarter", + "Bass Eb4Quarter F3Quarter F3Quarter", + "Bass F3Quarter Eb4Quarter Db3Quarter", + "Bass F4Quarter Gb3Quarter E4Quarter F4Quarter", + "Bass Gb4Quarter A3Quarter Db4Quarter D4Quarter" + ], + [ + "Bass C3Quarter Gb3Quarter D#4Quarter", + "Bass Eb4Quarter F3Quarter F#3Quarter", + "Bass F4Quarter F#4Quarter Db4Quarter", + "Bass G3Quarter G#3Quarter C#4Quarter C4Quarter", + "Bass G4Quarter F#3Quarter Gb3Quarter Eb4Quarter" + ] + ] +} diff --git a/src/activities/play_piano/play_piano.js b/src/activities/play_piano/play_piano.js --- a/src/activities/play_piano/play_piano.js +++ b/src/activities/play_piano/play_piano.js @@ -21,15 +21,21 @@ */ .pragma library .import QtQuick 2.6 as Quick +.import "dataset.js" as Dataset var currentLevel = 0 -var numberOfLevel = 4 +var currentSubLevel = 0 +var numberOfLevel = 11 +var noteIndexAnswered var items +var levels +var incorrectAnswers = [] function start(items_) { items = items_ currentLevel = 0 - initLevel() + levels = Dataset.getData() + items.piano.currentOctaveNb = 0 } function stop() { @@ -37,18 +43,88 @@ function initLevel() { items.bar.level = currentLevel + 1 + if([5, 10].indexOf(items.bar.level) === -1) + items.piano.useSharpNotation = true + else + items.piano.useSharpNotation = false + + currentSubLevel = 0 + nextSubLevel() +} + +function initSubLevel() { + var currentSubLevelMelody = levels[currentLevel][currentSubLevel - 1] + noteIndexAnswered = -1 + items.multipleStaff.loadFromData(currentSubLevelMelody) + + if(items.bar.level === 1 || items.bar.level === 7) + items.piano.currentOctaveNb = 0 + else if(items.bar.level === 2 || items.bar.level === 8) + items.piano.currentOctaveNb = 1 + else if(items.bar.level === 3) + items.piano.currentOctaveNb = 2 + else + items.piano.currentOctaveNb = items.piano.defaultOctaveNb + + items.multipleStaff.play() +} + +function nextSubLevel() { + currentSubLevel++ + incorrectAnswers = [] + items.score.currentSubLevel = currentSubLevel + if(currentSubLevel > levels[currentLevel].length) + nextLevel() + else + initSubLevel() +} + +// Function will be written when activity logic will be implemented. +function undoPreviousAnswer() { + if(noteIndexAnswered >= 0) { + items.multipleStaff.revertAnswer(noteIndexAnswered) + if(incorrectAnswers.indexOf(noteIndexAnswered) != -1) + incorrectAnswers.pop() + + noteIndexAnswered-- + } +} + +function checkAnswer(noteName) { + var currentSubLevelNotes = levels[currentLevel][currentSubLevel - 1].split(' ') + if(noteIndexAnswered < (currentSubLevelNotes.length - 2)) { + noteIndexAnswered++ + var currentNote = currentSubLevelNotes[noteIndexAnswered + 1] + if((noteName + "Quarter") === currentNote) + items.multipleStaff.indicateAnsweredNote(true, noteIndexAnswered) + else { + incorrectAnswers.push(noteIndexAnswered) + items.multipleStaff.indicateAnsweredNote(false, noteIndexAnswered) + } + + if(noteIndexAnswered === (currentSubLevelNotes.length - 2)) { + if(incorrectAnswers.length === 0) + items.bonus.good("flower") + else + items.bonus.bad("flower") + } + } } function nextLevel() { - if(numberOfLevel <= ++currentLevel) { - currentLevel = 0 + if(!items.iAmReady.visible) { + if(numberOfLevel <= ++currentLevel) { + currentLevel = 0 + } + initLevel() } - initLevel(); } function previousLevel() { - if(--currentLevel < 0) { - currentLevel = numberOfLevel - 1 + if(!items.iAmReady.visible) { + if(--currentLevel < 0) { + currentLevel = numberOfLevel - 1 + } + initLevel() } - initLevel(); }