diff --git a/src/activities/piano_composition/ActivityInfo.qml b/src/activities/piano_composition/ActivityInfo.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/ActivityInfo.qml @@ -0,0 +1,53 @@ +/* GCompris - ActivityInfo.qml + * + * Copyright (C) 2016 Johnny Jazeix + * Copyright (C) 2018 Aman Kumar Gupta + * + * 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 GCompris 1.0 + +ActivityInfo { + name: "piano_composition/Piano_composition.qml" + difficulty: 2 + icon: "piano_composition/piano_composition.svg" + author: "Johnny Jazeix <jazeix@gmail.com>" + demo: true + title: qsTr("Piano Composition") + description: qsTr("An activity to learn how the piano keyboard works, how notes are written on a musical staff and explore music composition by loading and saving your work.") + goal: qsTr("Develop an understanding of music composition, and increase interest in making music with a piano keyboard. This activity covers many fundamental aspects of music, but there is much more to explore about music composition. If you enjoy this activity but want a more advanced tool, try downloading MuseScore (http://musescore.org/en/download), an open source music notation tool.") + prerequisite: qsTr("Familiarity with note naming conventions, note-names activity useful to learn this notation.") + manual: qsTr("This activity has several levels, each level adds a new functionality to the previous level. + Level 1: Basic piano keyboard (white keys only) and students can experiment with clicking the colored rectangle keys to write music. + Level 2: The musical staff switches to bass clef, so pitches are lower than in previous level. + Level 3: Option to choose between treble and bass clef, addition of black keys (sharp keys). + Level 4: Flat notation used for black keys. + Level 5: Option to select note duration (whole, half, quarter, eighth notes). + Level 6: Addition of rests (whole, half, quarter, eighth rests) + Level 7: Load children's melodies from around the world and also save your composition. + +The following keyboard bindings work in this game: +- backspace: undo +- delete: erase attempt +- enter/return: OK button +- space bar: play +- left/right arrow keys: switch keyboard octave +- number keys: + - 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" + createdInVersion: 9500 +} diff --git a/src/activities/piano_composition/CMakeLists.txt b/src/activities/piano_composition/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/CMakeLists.txt @@ -0,0 +1 @@ +GCOMPRIS_ADD_RCC(activities/piano_composition *.qml *.svg *.js resource/*.* resource/*/*) diff --git a/src/activities/piano_composition/LyricsArea.qml b/src/activities/piano_composition/LyricsArea.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/LyricsArea.qml @@ -0,0 +1,125 @@ +/* GCompris - LyricsArea.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: lyricsArea + + property string title: "" + property string origin: "" + property string lyrics: "" + + width: horizontalLayout ? parent.width * 0.42 : parent.width * 0.8 + height: horizontalLayout ? parent.height * 0.5 : parent.height * 0.28 + anchors.horizontalCenter: horizontalLayout ? undefined : parent.horizontalCenter + anchors.left: horizontalLayout ? parent.left : undefined + anchors.leftMargin: parent.width * 0.02 + anchors.top: horizontalLayout ? multipleStaff.top : multipleStaff.bottom + anchors.topMargin: horizontalLayout ? parent.height * 0.05 : parent.height * 0.025 + border.width: 3 + radius: 5 + border.color: "black" + opacity: 0.8 + visible: background.isLyricsMode + Item { + id: melodyTitle + width: parent.width + height: parent.height / 6 + GCText { + id: titleText + fontSizeMode: Text.Fit + wrapMode: Text.WordWrap + text: qsTr("Title: %1").arg(lyricsArea.title) + anchors.fill: parent + anchors.rightMargin: parent.width * 0.02 + anchors.leftMargin: parent.width * 0.02 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.bold: true + color: "green" + } + } + + Rectangle { + id: titleUnderline + width: titleText.contentWidth + height: 3 + color: "black" + anchors.top: melodyTitle.bottom + anchors.horizontalCenter: melodyTitle.horizontalCenter + } + + Item { + id: melodyOrigin + width: parent.width + height: parent.height / 8 + anchors.top: titleUnderline.bottom + GCText { + fontSizeMode: Text.Fit + wrapMode: Text.WordWrap + text: qsTr("Origin: %1").arg(lyricsArea.origin) + anchors.fill: parent + anchors.rightMargin: parent.width * 0.02 + anchors.leftMargin: parent.width * 0.02 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.italic: true + color: "red" + } + } + + Item { + id: melodyLyrics + width: parent.width + height: parent.height - melodyTitle.height - melodyOrigin.height - 20 + anchors.top: melodyOrigin.bottom + GCText { + fontSizeMode: Text.Fit + wrapMode: Text.WordWrap + text: lyricsArea.lyrics + anchors.fill: parent + anchors.rightMargin: parent.width * 0.05 + anchors.leftMargin: parent.width * 0.05 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + function resetLyricsArea() { + lyricsArea.title = "" + lyricsArea.origin = "" + lyricsArea.lyrics = "" + optionsRow.lyricsOrPianoModeIndex = 0 + } + + function setLyrics(title, origin, lyrics) { + resetLyricsArea() + lyricsArea.title = title + lyricsArea.origin = origin + lyricsArea.lyrics = lyrics + optionsRow.lyricsOrPianoModeIndex = 1 + } +} diff --git a/src/activities/piano_composition/MelodyList.qml b/src/activities/piano_composition/MelodyList.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/MelodyList.qml @@ -0,0 +1,174 @@ +/* GCompris - MelodyList.qml + * + * Copyright (C) 2017 Divyam Madaan + * Copyright (C) 2018 Aman Kumar Gupta + * + * Authors: + * Beth Hadley (GTK+ version) + * Divyam Madaan (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 QtQuick.Controls 1.5 + +import "../../core" +import "piano_composition.js" as Activity +import "melodies.js" as Dataset + +Rectangle { + id: dialogBackground + color: "#696da3" + border.color: "black" + border.width: 1 + z: 1000 + anchors.fill: parent + visible: false + focus: true + + Keys.onEscapePressed: close() + + signal close + + property alias melodiesModel: melodiesModel + property bool horizontalLayout: dialogBackground.width > dialogBackground.height + property int selectedMelodyIndex: -1 + + ListModel { + id: melodiesModel + } + + Row { + spacing: 2 + Item { width: 10; height: 1 } + + Column { + spacing: 10 + anchors.top: parent.top + Item { width: 1; height: 10 } + Rectangle { + id: titleRectangle + color: "#e6e6e6" + radius: 6.0 + width: dialogBackground.width - 30 + height: title.height * 1.2 + border.color: "black" + border.width: 2 + + GCText { + id: title + text: qsTr("Melodies") + width: dialogBackground.width - 30 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "black" + fontSize: 20 + font.weight: Font.DemiBold + wrapMode: Text.WordWrap + } + } + + Rectangle { + color: "#e6e6e6" + radius: 6.0 + width: dialogBackground.width - 30 + height: dialogBackground.height - 100 + border.color: "black" + border.width: 2 + anchors.margins: 100 + + Flickable { + id: flickableList + anchors.fill: parent + anchors.topMargin: 10 + anchors.leftMargin: 20 + contentWidth: parent.width + contentHeight: melodiesGrid.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Flow { + id: melodiesGrid + width: parent.width + spacing: 40 + anchors.horizontalCenter: parent.horizontalCenter + + Repeater { + id: melodiesRepeater + model: melodiesModel + + Item { + id: melodiesItem + width: dialogBackground.horizontalLayout ? dialogBackground.width / 5 : dialogBackground.width / 4 + height: dialogBackground.height / 5 + + Button { + text: title + onClicked: { + dialogBackground.selectedMelodyIndex = index + items.multipleStaff.stopAudios() + items.multipleStaff.nbStaves = 2 + items.multipleStaff.loadFromData(melody) + lyricsArea.setLyrics(title, _origin, lyrics) + } + width: parent.width + height: parent.height * 0.8 + style: GCButtonStyle { + theme: "dark" + } + + Image { + source: "qrc:/gcompris/src/core/resource/apply.svg" + sourceSize.width: height + sourceSize.height: height + width: height + height: parent.height / 4 + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: 2 + visible: dialogBackground.selectedMelodyIndex === index + } + } + } + } + } + } + // The scroll buttons + GCButtonScroll { + anchors.right: parent.right + anchors.rightMargin: 5 * ApplicationInfo.ratio + anchors.bottom: flickableList.bottom + anchors.bottomMargin: 30 * ApplicationInfo.ratio + width: parent.width / 20 + height: width * heightRatio + onUp: flickableList.flick(0, 1400) + onDown: flickableList.flick(0, -1400) + upVisible: (flickableList.visibleArea.yPosition <= 0) ? false : true + downVisible: ((flickableList.visibleArea.yPosition + flickableList.visibleArea.heightRatio) >= 1) ? false : true + } + } + Item { width: 1; height: 10 } + } + } + + GCButtonCancel { + onClose: { + dialogBackground.selectedMelodyIndex = -1 + parent.close() + } + } +} diff --git a/src/activities/piano_composition/MultipleStaff.qml b/src/activities/piano_composition/MultipleStaff.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/MultipleStaff.qml @@ -0,0 +1,603 @@ +/* GCompris - MultipleStaff.qml + * + * Copyright (C) 2016 Johnny Jazeix + * 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" +import "qrc:/gcompris/src/activities/piano_composition/NoteNotations.js" as NoteNotations + +Item { + id: multipleStaff + + property int nbStaves + property string clef + property int distanceBetweenStaff: multipleStaff.height / 3.3 + readonly property real clefImageWidth: 3 * height / 25 + + // Stores the note index which is selected. + property int selectedIndex: -1 + + // The notes that are to be colored can be assigned to this variable in the activity + property var coloredNotes: [] + + // When the notesColor is inbuilt, the default color mapping will be done, else assign any color externally in the activity. Example: Reference notes in note_names are red colored. + property string notesColor: "inbuilt" + property bool noteHoverEnabled: false + + // Stores if the notes are to be centered on the staff. Used in Play_piano and Play_rhythm. + property bool centerNotesPosition: false + property bool isPulseMarkerDisplayed: false + property bool noteAnimationEnabled: false + readonly property bool isMusicPlaying: musicTimer.running + + property alias flickableStaves: flickableStaves + property alias musicElementModel: musicElementModel + property alias musicElementRepeater: musicElementRepeater + property real flickableTopMargin: multipleStaff.height / 14 + distanceBetweenStaff / 3.5 + readonly property real pulseMarkerX: pulseMarker.x + readonly property bool isPulseMarkerRunning: pulseMarkerAnimation.running + property bool isFlickable: true + property bool enableNotesSound: true + property int currentEnteringStaff: 0 + property int bpmValue: 60 + + // The position where the 1st note in the centered state is to be placed. + property real firstCenteredNotePosition: multipleStaff.width / 3.3 + property real spaceBetweenNotes: 0 + + /** + * Emitted when a note is clicked. + * + * It is used for selecting note to play, erase and do other operations on it. + */ + signal noteClicked(int noteIndex) + + /** + * Emitted when the animation of the note from the right of the staff to the left is finished. + * + * It's used in note_names activity. + */ + signal noteAnimationFinished + + /** + * Emitted when the pulseMarker's animation is finished. + */ + signal pulseMarkerAnimationFinished + + /** + * Used in play_rhythm activity. It tells the instants when pulseMarker reaches a note and the drum sound is to be played. + */ + signal playDrumSound + + ListModel { + id: musicElementModel + } + + Flickable { + id: flickableStaves + interactive: multipleStaff.isFlickable + flickableDirection: Flickable.VerticalFlick + contentWidth: staffColumn.width + contentHeight: staffColumn.height + distanceBetweenStaff + anchors.fill: parent + clip: true + Behavior on contentY { + NumberAnimation { duration: 250 } + } + + Column { + id: staffColumn + spacing: distanceBetweenStaff + anchors.top: parent.top + anchors.topMargin: flickableTopMargin + Repeater { + id: staves + model: nbStaves + Staff { + id: staff + height: multipleStaff.height / 5 + width: multipleStaff.width - 5 + lastPartition: index == (nbStaves - 1) + } + } + } + + Repeater { + id: musicElementRepeater + model: musicElementModel + MusicElement { + id: musicElement + noteName: noteName_ + noteType: noteType_ + highlightWhenPlayed: highlightWhenPlayed_ + noteIsColored: multipleStaff.coloredNotes.indexOf(noteName[0]) != -1 + soundPitch: soundPitch_ + clefType: clefType_ + elementType: elementType_ + isDefaultClef: isDefaultClef_ + + property int staffNb: staffNb_ + property alias noteAnimation: noteAnimation + // The shift which the elements experience when a sharp/flat note is added before them. + readonly property real sharpShiftDistance: blackType != "" ? width / 6 : 0 + + noteDetails: multipleStaff.getNoteDetails(noteName, noteType, clefType) + + MouseArea { + id: noteMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: multipleStaff.noteClicked(index) + } + + function highlightNote() { + highlightTimer.start() + } + + readonly property real defaultXPosition: musicElementRepeater.itemAt(index - 1) ? (musicElementRepeater.itemAt(index - 1).width + musicElementRepeater.itemAt(index - 1).x) + : 0 + + x: { + if(multipleStaff.noteAnimationEnabled) + return NaN + // !musicElementRepeater.itemAt(index - 1) acts as a fallback condition when there is no previous element present. It happens when Qt clears the model internally. + if(isDefaultClef || !musicElementRepeater.itemAt(index - 1)) + return 0 + else if(musicElementRepeater.itemAt(index - 1).elementType === "clef") { + if(centerNotesPosition) + return sharpShiftDistance + defaultXPosition + multipleStaff.firstCenteredNotePosition + else + return sharpShiftDistance + defaultXPosition + 10 + } + else + return sharpShiftDistance + defaultXPosition + multipleStaff.spaceBetweenNotes + } + + onYChanged: { + if(noteAnimationEnabled && elementType === "note") + noteAnimation.start() + } + + y: { + if(elementType === "clef") + return flickableTopMargin + staves.itemAt(staffNb).y + else if(noteDetails === undefined || staves.itemAt(staffNb) == undefined) + return 0 + + var verticalDistanceBetweenLines = staves.itemAt(0).verticalDistanceBetweenLines + var shift = -verticalDistanceBetweenLines / 2 + var relativePosition = noteDetails.positionOnStaff + var imageY = flickableTopMargin + staves.itemAt(staffNb).y + 2 * verticalDistanceBetweenLines + + if(rotation === 180) { + return imageY - (4 - relativePosition) * verticalDistanceBetweenLines + shift + } + + return imageY - (6 - relativePosition) * verticalDistanceBetweenLines + shift + } + + NumberAnimation { + id: noteAnimation + target: musicElement + properties: "x" + duration: 9000 + from: multipleStaff.width - 10 + to: multipleStaff.clefImageWidth + onStopped: { + noteAnimationFinished() + } + } + } + } + + Image { + id: secondStaffDefaultClef + sourceSize.width: musicElementModel.count ? multipleStaff.clefImageWidth : 0 + y: staves.count === 2 ? flickableTopMargin + staves.itemAt(1).y : 0 + visible: (currentEnteringStaff === 0) && (nbStaves === 2) + source: background.clefType ? "qrc:/gcompris/src/activities/piano_composition/resource/" + background.clefType.toLowerCase() + "Clef.svg" + : "" + } + } + + Rectangle { + id: pulseMarker + width: activity.horizontalLayout ? 5 : 3 + border.width: width / 2 + height: staves.itemAt(0) == undefined ? 0 : 4 * staves.itemAt(0).verticalDistanceBetweenLines + width + opacity: isPulseMarkerDisplayed && pulseMarkerAnimation.running + color: "red" + y: flickableTopMargin + + property real nextPosition: 0 + + NumberAnimation { + id: pulseMarkerAnimation + target: pulseMarker + property: "x" + to: pulseMarker.nextPosition + onStopped: { + if(pulseMarker.x === multipleStaff.width) + pulseMarkerAnimationFinished() + else + playDrumSound() + } + } + } + + /** + * Initializes the default clefs on the staves. + * + * @param clefType: The clef type to be initialized. + */ + function initClefs(clefType) { + musicElementModel.clear() + musicElementModel.append({ "elementType_": "clef", "clefType_": clefType, "staffNb_": 0, "isDefaultClef_": true, + "noteName_": "", "noteType_": "", "soundPitch_": clefType, + "highlightWhenPlayed_": false }) + } + + /** + * Pauses the sliding animation of the notes. + */ + function pauseNoteAnimation() { + for(var i = 0; i < musicElementModel.count; i++) { + if(musicElementRepeater.itemAt(i).noteAnimation.running) + musicElementRepeater.itemAt(i).noteAnimation.pause() + } + } + + function resumeNoteAnimation() { + for(var i = 0; i < musicElementModel.count; i++) { + musicElementRepeater.itemAt(i).noteAnimation.resume() + } + } + + /** + * Gets all the details of any note like note image, position on staff etc. from NoteNotations. + */ + function getNoteDetails(noteName, noteType, clefType) { + var notesDetails = NoteNotations.get() + var noteNotation + if(noteType === "Rest") + noteNotation = noteName + noteType + else + noteNotation = clefType + noteName + + for(var i = 0; i < notesDetails.length; i++) { + if(noteNotation === notesDetails[i].noteName) { + return notesDetails[i] + } + } + } + + /** + * Adds a note to the staff. + */ + function addMusicElement(elementType, noteName, noteType, highlightWhenPlayed, playAudio, clefType, soundPitch, isUnflicked) { + if(soundPitch == undefined || soundPitch === "") + soundPitch = clefType + + var isNextStaff = (selectedIndex == -1) && musicElementModel.count && ((staves.itemAt(0).width - musicElementRepeater.itemAt(musicElementModel.count - 1).x - musicElementRepeater.itemAt(musicElementModel.count - 1).width) < multipleStaff.clefImageWidth) + + // If the incoming element is a clef, make sure that there is enough required space to fit one more note too. Else it creates problem when the note is erased and the view is redrawn, else move on to the next staff. + if(elementType === "clef" && musicElementModel.count && (selectedIndex == -1)) { + if(staves.itemAt(0).width - musicElementRepeater.itemAt(musicElementModel.count - 1).x - musicElementRepeater.itemAt(musicElementModel.count - 1).width - 2 * Math.max(multipleStaff.clefImageWidth, musicElementRepeater.itemAt(0).noteImageWidth) < 0) + isNextStaff = true + } + + if(isNextStaff && !noteAnimationEnabled) { + multipleStaff.currentEnteringStaff++ + if(multipleStaff.currentEnteringStaff >= multipleStaff.nbStaves) + multipleStaff.nbStaves++ + // When a new staff is added, initialise it with a default clef. + musicElementModel.append({"noteName_": "", "noteType_": "", "soundPitch_": soundPitch, + "clefType_": clefType, "highlightWhenPlayed_": false, + "staffNb_": multipleStaff.currentEnteringStaff, + "isDefaultClef_": true, "elementType_": "clef"}) + + if(!isUnflicked) + flickableStaves.flick(0, - nbStaves * multipleStaff.height) + + if(elementType === "clef") + return 0 + + isNextStaff = false + } + + if(selectedIndex === -1) { + var isDefualtClef = false + if(!musicElementModel.count) + isDefualtClef = true + musicElementModel.append({"noteName_": noteName, "noteType_": noteType, "soundPitch_": soundPitch, + "clefType_": clefType, "highlightWhenPlayed_": highlightWhenPlayed, + "staffNb_": multipleStaff.currentEnteringStaff, + "isDefaultClef_": isDefualtClef, "elementType_": elementType}) + + } + else { + var tempModel = createNotesBackup() + var insertingIndex = selectedIndex + 1 + if(elementType === "clef") + insertingIndex-- + + tempModel.splice(insertingIndex, 0, {"elementType_": elementType, "noteName_": noteName, "noteType_": noteType, + "soundPitch_": soundPitch, "clefType_": clefType }) + if(elementType === "clef") { + for(var i = 0; i < musicElementModel.count && tempModel[i]["elementType_"] != "clef"; i++) + tempModel[i]["soundPitch_"] = clefType + } + selectedIndex = -1 + + redraw(tempModel) + } + + multipleStaff.selectedIndex = -1 + background.clefType = musicElementModel.get(musicElementModel.count - 1).soundPitch_ + + if(playAudio) + playNoteAudio(noteName, noteType, soundPitch, musicElementRepeater.itemAt(musicElementModel.count - 1).duration) + } + + /** + * Creates a backup of the musicElementModel before erasing it. + * + * This backup data is used to redraw the notes. + */ + function createNotesBackup() { + var tempModel = [] + for(var i = 0; i < musicElementModel.count; i++) + tempModel.push(JSON.parse(JSON.stringify(musicElementModel.get(i)))) + + return tempModel + } + + /** + * Redraws all the notes on the staves. + */ + function redraw(notes) { + musicElementModel.clear() + currentEnteringStaff = 0 + selectedIndex = -1 + for(var i = 0; i < notes.length; i++) { + addMusicElement(notes[i]["elementType_"], notes[i]["noteName_"], notes[i]["noteType_"], false, false, notes[i]["clefType_"], notes[i]["soundPitch_"], true) + } + + // Remove the remaining unused staffs. + if((multipleStaff.currentEnteringStaff + 1 < multipleStaff.nbStaves) && (multipleStaff.nbStaves > 2)) { + nbStaves = multipleStaff.currentEnteringStaff + 1 + flickableStaves.flick(0, - nbStaves * multipleStaff.height) + } + + if(musicElementModel.get(musicElementModel.count - 1).isDefaultClef_ && nbStaves > 2) + musicElementModel.remove(musicElementModel.count - 1) + + if(musicElementModel.get(musicElementModel.count - 1).staffNb_ < nbStaves - 1 && nbStaves != 2) + nbStaves = musicElementModel.get(musicElementModel.count - 1).staffNb_ + 1 + + currentEnteringStaff = musicElementModel.get(musicElementModel.count - 1).staffNb_ + background.clefType = musicElementModel.get(musicElementModel.count - 1).soundPitch_ + } + + /** + * Erases the selected note. + * + * @param noteIndex: index of the note to be erased + */ + function eraseNote(noteIndex) { + musicElementModel.remove(noteIndex) + selectedIndex = -1 + var tempModel = createNotesBackup() + redraw(tempModel) + } + + /** + * Erases all the notes. + */ + function eraseAllNotes() { + musicElementModel.clear() + selectedIndex = -1 + multipleStaff.currentEnteringStaff = 0 + initClefs(background.clefType) + } + + /** + * 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, soundPitch, duration) { + if(noteName) { + if(noteType != "Rest") { + // We should find a corresponding b type enharmonic notation for # type note to play the audio. + if(noteName[1] === "#") { + var blackKeysFlat = piano.blackKeyFlatNoteLabelsArray + var blackKeysSharp = piano.blackKeySharpNoteLabelsArray + + for(var i = 0; i < blackKeysSharp.length; i++) { + if(blackKeysSharp[i][0] === noteName) { + noteName = blackKeysFlat[i][0] + break + } + } + } + audioLooper.stop() + var noteToPlay = "qrc:/gcompris/src/activities/piano_composition/resource/" + soundPitch.toLowerCase() + "_pitches/" + noteName + ".wav" + audioLooper.playMusic(noteToPlay, duration) + } + } + } + + Timer { + id: audioLooper + + signal playMusic(string noteToPlay, int duration) + property string audio + property int loopCounter + + onPlayMusic: { + audio = noteToPlay + audioLooper.interval = Math.min(1000, duration) + loopCounter = Math.ceil(duration / 1000) + start() + } + repeat: true + triggeredOnStart: true + onTriggered: { + if(--loopCounter < 0) + stop() + else + items.audioEffects.play(audio) + } + onRunningChanged: { + if(!running) { + items.audioEffects.stop() + } + } + } + + /** + * Get all the notes from the musicElementModel and returns the melody. + */ + function getAllNotes() { + var notes = createNotesBackup() + return notes + } + + /** + * Loads melody from the provided data, to the staffs. + * + * @param data: melody to be loaded + */ + function loadFromData(data) { + if(data != undefined) { + var melody = data.split(" ") + background.clefType = melody[0] + eraseAllNotes() + for(var i = 1 ; i < melody.length; ++i) { + var noteLength = melody[i].length + var noteName = melody[i][0] + var noteType + if(melody[i].substring(noteLength - 4, noteLength) === "Rest") { + noteName = melody[i].substring(0, noteLength - 4) + noteType = "Rest" + } + else if(melody[i][1] === "#" || melody[i][1] === "b") { + noteType = melody[i].substring(3, melody[i].length) + noteName += melody[i][1] + melody[i][2]; + } + else { + noteType = melody[i].substring(2, melody[i].length) + noteName += melody[i][1] + } + addMusicElement("note", noteName, noteType, false, false, melody[0]) + } + var tempModel = createNotesBackup() + redraw(tempModel) + } + } + + /** + * Used in the activity play_piano. + * + * Checks if the answered note is correct + */ + function indicateAnsweredNote(isCorrectAnswer, noteIndexAnswered) { + musicElementRepeater.itemAt(noteIndexAnswered).noteAnswered = true + musicElementRepeater.itemAt(noteIndexAnswered).isCorrectlyAnswered = isCorrectAnswer + } + + /** + * Used in the activity play_piano. + * + * Reverts the previous answer. + */ + function revertAnswer(noteIndexReverting) { + musicElementRepeater.itemAt(noteIndexReverting).noteAnswered = false + } + + function play() { + musicTimer.currentNote = 0 + selectedIndex = -1 + musicTimer.interval = 1 + if(isFlickable) + flickableStaves.flick(0, nbStaves * multipleStaff.height) + + pulseMarkerAnimation.stop() + + if(musicElementModel.count > 1) + pulseMarker.x = musicElementRepeater.itemAt(1).x + musicElementRepeater.itemAt(1).width / 2 + else + pulseMarker.x = 0 + + musicTimer.start() + } + + /** + * Stops the audios playing. + */ + function stopAudios() { + musicElementModel.clear() + musicTimer.stop() + items.audioEffects.stop() + } + + Timer { + id: musicTimer + + property int currentNote: 0 + + onRunningChanged: { + if(!running && musicElementModel.get(currentNote) !== undefined) { + var currentType = musicElementModel.get(currentNote).noteType_ + var note = musicElementModel.get(currentNote).noteName_ + var soundPitch = musicElementModel.get(currentNote).soundPitch_ + var currentStaff = musicElementModel.get(currentNote).staffNb_ + background.clefType = musicElementModel.get(currentNote).clefType_ + + if(musicElementModel.get(currentNote).isDefaultClef_ && currentStaff > 1) { + flickableStaves.contentY = staves.itemAt(currentStaff - 1).y + } + + musicTimer.interval = musicElementRepeater.itemAt(currentNote).duration + if(multipleStaff.enableNotesSound) + playNoteAudio(note, currentType, soundPitch, musicTimer.interval) + pulseMarkerAnimation.stop() + pulseMarkerAnimation.duration = Math.max(1, musicTimer.interval) + if(musicElementRepeater.itemAt(currentNote + 1) != undefined) + pulseMarker.nextPosition = musicElementRepeater.itemAt(currentNote + 1).x + musicElementRepeater.itemAt(currentNote + 1).width / 2 + else + pulseMarker.nextPosition = multipleStaff.width + + pulseMarkerAnimation.start() + + if(!isPulseMarkerDisplayed) + musicElementRepeater.itemAt(currentNote).highlightNote() + currentNote++ + musicTimer.start() + } + } + } +} diff --git a/src/activities/piano_composition/MusicElement.qml b/src/activities/piano_composition/MusicElement.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/MusicElement.qml @@ -0,0 +1,200 @@ +/* GCompris - musicElement.qml + * + * Copyright (C) 2016 Johnny Jazeix + * 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 QtGraphicalEffects 1.0 +import GCompris 1.0 + +import "../../core" + +Item { + id: musicElement + width: noteImageWidth + height: multipleStaff.height / 5 + + property string noteName + property string noteType + property string soundPitch + property string clefType + property string elementType + property bool noteIsColored: true + property bool isDefaultClef: false + property string blackType: noteName === "" ? "" + : noteName[1] === "#" ? "sharp" + : noteName[1] === "b" ? "flat" : ""// empty, "flat" or "sharp" + + /** + * Calculates and assign the timer interval for a note. + */ + function calculateTimerDuration(noteType) { + noteType = noteType.toLowerCase() + if(noteType === "whole") + return 240000 / multipleStaff.bpmValue + else if(noteType === "half") + return 120000 / multipleStaff.bpmValue + else if(noteType === "quarter") + return 60000 / multipleStaff.bpmValue + else + return 30000 / multipleStaff.bpmValue + } + + readonly property int duration: { + if(elementType != "clef") { + if(noteType === "Rest") + return calculateTimerDuration(noteName) + else + return calculateTimerDuration(noteType) + } + return 0 + } + + readonly property real noteImageWidth: (multipleStaff.width - 15 - clefImageWidth) / 10 + + readonly property var noteColorMap: { "1": "#FF0000", "2": "#FF7F00", "3": "#FFFF00", + "4": "#32CD32", "5": "#6495ED", "6": "#D02090", "7": "#FF1493", "8": "#FF0000", + "-1": "#FF6347", "-2": "#FFD700", "-3": "#20B2AA", "-4": "#8A2BE2", + "-5": "#FF00FF" } + + readonly property var whiteNoteName: { "C": "1", "D": "2", "E": "3", "F": "4", "G": "5", "A": "6", "B": "7", "C": "8" } + + readonly property var sharpNoteName: { "C#": "-1", "D#": "-2", "F#": "-3", "G#": "-4", "A#": "-5" } + readonly property var flatNoteName: { "Db": "-1", "Eb": "-2", "Gb": "-3", "Ab": "-4", "Bb": "-5" } + readonly property var blackNoteName: blackType == "" ? blackType + : blackType == "flat" ? flatNoteName : sharpNoteName + + property bool highlightWhenPlayed: false + property alias highlightTimer: highlightTimer + + property var noteDetails + + property bool noteAnswered: false + property bool isCorrectlyAnswered: false + + rotation: { + if((noteDetails === undefined) || elementType === "clef") + return 0 + else if((noteDetails.positionOnStaff < 0) && (noteType === "Whole")) + return 180 + else + return noteDetails.rotation + } + + Image { + id: blackTypeImage + source: blackType !== "" ? "qrc:/gcompris/src/activities/piano_composition/resource/black" + blackType + ".svg" : "" + 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: -noteImage.width / 4 + anchors.leftMargin: -noteImage.width / 2.5 + anchors.bottom: noteImage.bottom + anchors.bottomMargin: parent.height / 6 + fillMode: Image.PreserveAspectFit + } + + Rectangle { + id: highlightRectangle + width: musicElement.width + height: musicElement.height * 0.9 + color: "red" + opacity: 0.6 + border.color: "white" + radius: width / 6 + visible: (multipleStaff.noteHoverEnabled && noteMouseArea.containsMouse) || highlightTimer.running + } + + Rectangle { + id: selectedNoteIndicator + width: musicElement.width + height: musicElement.height * 0.9 + color: "blue" + opacity: 0.6 + border.color: "white" + radius: width / 5 + visible: selectedIndex == index + } + + Image { + id: noteImage + source: (noteDetails === undefined) ? "" + : noteType != "Rest" ? "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + noteType + ".svg" + : "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + ".svg" + sourceSize.width: 200 + width: musicElement.width + height: musicElement.height + } + + Image { + id: clefImage + source: (elementType === "clef") ? "qrc:/gcompris/src/activities/piano_composition/resource/" + clefType.toLowerCase() + "Clef.svg" : "" + sourceSize.width: multipleStaff.clefImageWidth + } + + 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 + source: noteImage + + readonly property int invalidConditionNumber: -6 + readonly property int noteColorNumber: { + if(noteDetails === undefined || noteType === "" || noteType === "Rest" || noteName === "") + return invalidConditionNumber + else if((blackType === "") && (whiteNoteName[noteName[0]] != undefined)) + return whiteNoteName[noteName[0]] + else if((noteName.length > 2) && (blackNoteName[noteName.substring(0,2)] != undefined)) + return blackNoteName[noteName.substring(0,2)] + else + return invalidConditionNumber + } + + color: { + if(multipleStaff.notesColor === "inbuilt") + return (noteColorNumber > invalidConditionNumber) ? noteColorMap[noteColorNumber] : "black" // make image like it lays under red glass + else + return multipleStaff.notesColor + } + visible: noteIsColored && (elementType != "clef") + } + + Timer { + id: highlightTimer + interval: duration + } +} diff --git a/src/activities/piano_composition/NoteNotations.js b/src/activities/piano_composition/NoteNotations.js new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/NoteNotations.js @@ -0,0 +1,621 @@ +/* GCompris - NoteNotations.js + * + * 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 . + **/ + +function get() { + return [ + { + "noteName": "TrebleD3", + "imageName": "trebleD3", + "rotation": 0, + "positionOnStaff": 9 + }, + { + "noteName": "TrebleE3", + "imageName": "trebleE3", + "rotation": 0, + "positionOnStaff": 8.5 + }, + { + "noteName": "TrebleF#3", + "imageName": "bassG4", + "rotation": 0, + "positionOnStaff": 8 + }, + { + "noteName": "TrebleF3", + "imageName": "bassG4", + "rotation": 0, + "positionOnStaff": 8 + }, + { + "noteName": "TrebleGb3", + "imageName": "bassF4", + "rotation": 0, + "positionOnStaff": 7.5 + }, + { + "noteName": "TrebleG3", + "imageName": "bassF4", + "rotation": 0, + "positionOnStaff": 7.5 + }, + { + "noteName": "TrebleG#3", + "imageName": "bassF4", + "rotation": 0, + "positionOnStaff": 7.5 + }, + { + "noteName": "TrebleA3", + "imageName": "trebleA3", + "rotation": 0, + "positionOnStaff": 7 + }, + { + "noteName": "TrebleA#3", + "imageName": "trebleA3", + "rotation": 0, + "positionOnStaff": 7 + }, + { + "noteName": "TrebleAb3", + "imageName": "trebleA3", + "rotation": 0, + "positionOnStaff": 7 + }, + { + "noteName": "TrebleB3", + "imageName": "trebleB3", + "rotation": 0, + "positionOnStaff": 6.5 + }, + { + "noteName": "TrebleBb3", + "imageName": "trebleB3", + "rotation": 0, + "positionOnStaff": 6.5 + }, + { + "noteName": "TrebleC4", + "imageName": "trebleC4", + "rotation": 0, + "positionOnStaff": 6 + }, + { + "noteName": "TrebleC#4", + "imageName": "trebleC4", + "rotation": 0, + "positionOnStaff": 6 + }, + { + "noteName": "TrebleDb4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5.5 + }, + { + "noteName": "TrebleD4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5.5 + }, + { + "noteName": "TrebleD#4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5.5 + }, + { + "noteName": "TrebleEb4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5 + }, + { + "noteName": "TrebleE4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5 + }, + { + "noteName": "TrebleF4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4.5 + }, + { + "noteName": "TrebleF#4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4.5 + }, + { + "noteName": "TrebleG4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4 + }, + { + "noteName": "TrebleGb4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4 + }, + { + "noteName": "TrebleG#4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4 + }, + { + "noteName": "TrebleAb4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 3.5 + }, + { + "noteName": "TrebleA4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 3.5 + }, + { + "noteName": "TrebleA#4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 3.5 + }, + { + "noteName": "TrebleBb4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 3 + }, + { + "noteName": "TrebleB4", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 3 + }, + { + "noteName": "TrebleC5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2.5 + }, + { + "noteName": "TrebleC#5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2.5 + }, + { + "noteName": "TrebleDb5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2 + }, + { + "noteName": "TrebleD5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2 + }, + { + "noteName": "TrebleD#5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2 + }, + { + "noteName": "TrebleEb5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1.5 + }, + { + "noteName": "TrebleE5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1.5 + }, + { + "noteName": "TrebleF5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1 + }, + { + "noteName": "TrebleF#5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1 + }, + { + "noteName": "TrebleG5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 0.5 + }, + { + "noteName": "TrebleGb5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 0.5 + }, + { + "noteName": "TrebleG#5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 0.5 + }, + { + "noteName": "TrebleAb5", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 0 + }, + { + "noteName": "TrebleA5", + "imageName": "trebleC4", + "rotation": 180, + "positionOnStaff": 0 + }, + { + "noteName": "TrebleA#5", + "imageName": "trebleC4", + "rotation": 180, + "positionOnStaff": 0 + }, + { + "noteName": "TrebleBb5", + "imageName": "trebleB3", + "rotation": 180, + "positionOnStaff": -0.5 + }, + { + "noteName": "TrebleB5", + "imageName": "trebleB3", + "rotation": 180, + "positionOnStaff": -0.5 + }, + { + "noteName": "TrebleC6", + "imageName": "trebleA3", + "rotation": 180, + "positionOnStaff": -1 + }, + { + "noteName": "TrebleC#6", + "imageName": "trebleA3", + "rotation": 180, + "positionOnStaff": -1 + }, + { + "noteName": "TrebleDb6", + "imageName": "bassF4", + "rotation": 180, + "positionOnStaff": -1.5 + }, + { + "noteName": "TrebleD6", + "imageName": "bassF4", + "rotation": 180, + "positionOnStaff": -1.5 + }, + { + "noteName": "TrebleD#6", + "imageName": "bassF4", + "rotation": 180, + "positionOnStaff": -1.5 + }, + { + "noteName": "TrebleEb6", + "imageName": "bassG4", + "rotation": 180, + "positionOnStaff": -2 + }, + { + "noteName": "TrebleE6", + "imageName": "bassG4", + "rotation": 180, + "positionOnStaff": -2 + }, + { + "noteName": "TrebleF6", + "imageName": "trebleE3", + "rotation": 180, + "positionOnStaff": -2.5 + }, + { + "noteName": "BassF1", + "imageName": "trebleD3", + "rotation": 0, + "positionOnStaff": 9 + }, + { + "noteName": "BassG1", + "imageName": "trebleE3", + "rotation": 0, + "positionOnStaff": 8.5 + }, + { + "noteName": "BassA1", + "imageName": "bassG4", + "rotation": 0, + "positionOnStaff": 8 + }, + { + "noteName": "BassB1", + "imageName": "bassF4", + "rotation": 0, + "positionOnStaff": 7.5 + }, + { + "noteName": "BassC2", + "imageName": "trebleA3", + "rotation": 0, + "positionOnStaff": 7 + }, + { + "noteName": "BassD2", + "imageName": "trebleB3", + "rotation": 0, + "positionOnStaff": 6.5 + }, + { + "noteName": "BassE2", + "imageName": "trebleC4", + "rotation": 0, + "positionOnStaff": 6 + }, + { + "noteName": "BassF2", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5.5 + }, + { + "noteName": "BassG2", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 5 + }, + { + "noteName": "BassA2", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4.5 + }, + { + "noteName": "BassB2", + "imageName": "genericNote", + "rotation": 0, + "positionOnStaff": 4 + }, + { + "noteName": "BassC3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 3.5 + }, + { + "noteName": "BassC#3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 3.5 + }, + { + "noteName": "BassDb3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 3 + }, + { + "noteName": "BassD3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 3 + }, + { + "noteName": "BassD#3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 3 + }, + { + "noteName": "BassEb3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2.5 + }, + { + "noteName": "BassE3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2.5 + }, + { + "noteName": "BassF3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2 + }, + { + "noteName": "BassF#3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 2 + }, + { + "noteName": "BassG3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1.5 + }, + { + "noteName": "BassGb3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1.5 + }, + { + "noteName": "BassG#3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1.5 + }, + { + "noteName": "BassAb3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1 + }, + { + "noteName": "BassA3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1 + }, + { + "noteName": "BassA#3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 1 + }, + { + "noteName": "BassBb3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 0.5 + }, + { + "noteName": "BassB3", + "imageName": "genericNote", + "rotation": 180, + "positionOnStaff": 0.5 + }, + { + "noteName": "BassC4", + "imageName": "trebleC4", + "rotation": 180, + "positionOnStaff": 0 + }, + { + "noteName": "BassC#4", + "imageName": "trebleC4", + "rotation": 180, + "positionOnStaff": 0 + }, + { + "noteName": "BassD4", + "imageName": "trebleB3", + "rotation": 180, + "positionOnStaff": -0.5 + }, + { + "noteName": "BassD#4", + "imageName": "trebleB3", + "rotation": 180, + "positionOnStaff": -0.5 + }, + { + "noteName": "BassDb4", + "imageName": "trebleB3", + "rotation": 180, + "positionOnStaff": -0.5 + }, + { + "noteName": "BassE4", + "imageName": "trebleA3", + "rotation": 180, + "positionOnStaff": -1 + }, + { + "noteName": "BassEb4", + "imageName": "trebleA3", + "rotation": 180, + "positionOnStaff": -1 + }, + { + "noteName": "BassF4", + "imageName": "bassF4", + "rotation": 180, + "positionOnStaff": -1.5 + }, + { + "noteName": "BassF#4", + "imageName": "bassF4", + "rotation": 180, + "positionOnStaff": -1.5 + }, + { + "noteName": "BassG4", + "imageName": "bassG4", + "rotation": 180, + "positionOnStaff": -2 + }, + { + "noteName": "BassGb4", + "imageName": "bassG4", + "rotation": 180, + "positionOnStaff": -2 + }, + { + "noteName": "BassA4", + "imageName": "trebleE3", + "rotation": 180, + "positionOnStaff": -2.5 + }, + { + "noteName": "BassB4", + "imageName": "trebleD3", + "rotation": 180, + "positionOnStaff": -3 + }, + { + "noteName": "wholeRest", + "imageName": "wholeRest", + "rotation": 0, + "positionOnStaff": 3.4 + }, + { + "noteName": "halfRest", + "imageName": "wholeRest", + "rotation": 180, + "positionOnStaff": 1.6 + }, + { + "noteName": "quarterRest", + "imageName": "quarterRest", + "rotation": 0, + "positionOnStaff": 4.5 + }, + { + "noteName": "eighthRest", + "imageName": "eighthRest", + "rotation": 0, + "positionOnStaff": 4.6 + } + ] +} 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,373 @@ +/* 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 + + //: Whole note, Half note, Quarter note and Eighth note are the different length notes in the musical notation. + readonly property var noteLengthName: [[qsTr("Whole note"), "Whole"], [qsTr("Half note"), "Half"], [qsTr("Quarter note"), "Quarter"], [qsTr("Eighth note"), "Eighth"]] + + //: Whole rest, Half rest, Quarter rest and Eighth rest are the different length rests (silences) in the musical notation. + readonly property var translatedRestNames: [qsTr("Whole rest"), qsTr("Half rest"), qsTr("Quarter rest"), qsTr("Eighth rest")] + readonly property var restAddedMessage: [qsTr("Added whole rest"), qsTr("Added half rest"), qsTr("Added quarter rest"), qsTr("Added eighth rest")] + readonly property var lyricsOrPianoModes: [[qsTr("Piano"), "piano"], [qsTr("Lyrics"), "lyrics"]] + + property real iconsWidth: Math.min(50, (background.width - optionsRow.spacing * 13) / 16) + property alias noteOptionsIndex: noteOptions.currentIndex + property alias lyricsOrPianoModeIndex: lyricsOrPianoModeOption.currentIndex + property alias restOptionIndex: restOptions.currentIndex + property alias clefButtonIndex: clefButton.currentIndex + + property bool noteOptionsVisible: false + property bool playButtonVisible: false + property bool clefButtonVisible: 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 + property bool bpmVisible: false + + signal undoButtonClicked + signal clearButtonClicked + signal openButtonClicked + signal saveButtonClicked + signal playButtonClicked + signal clefAdded + signal bpmIncreased + signal bpmDecreased + signal emitOptionMessage(string message) + + SwitchableOptions { + id: noteOptions + source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex][1]) + nbOptions: optionsRow.noteLengthName.length + currentIndex: 2 + onClicked: { + background.currentType = optionsRow.noteLengthName[currentIndex][1] + emitOptionMessage(optionsRow.noteLengthName[currentIndex][0]) + } + visible: noteOptionsVisible + } + + Item { + id: bpmMeter + width: 4 * optionsRow.iconsWidth + height: optionsRow.iconsWidth + 10 + visible: bpmVisible + Rectangle { + color: "yellow" + opacity: 0.1 + border.width: 2 + border.color: "black" + anchors.fill: parent + radius: 10 + } + + Image { + source: "qrc:/gcompris/src/core/resource/bar_previous.svg" + sourceSize.width: parent.width / 4 + width: sourceSize.width + height: width + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.leftMargin: -10 + anchors.verticalCenter: parent.verticalCenter + Timer { + id: decreaseBpm + interval: 500 + repeat: true + onTriggered: { + bpmDecreased() + interval = 1 + } + onRunningChanged: { + if(!running) + interval = 500 + } + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.scale = 0.85 + bpmDecreased() + decreaseBpm.start() + } + onReleased: { + decreaseBpm.stop() + parent.scale = 1 + } + } + } + + GCText { + //: BPM is the abbreviation for Beats Per Minute. + text: multipleStaff.bpmValue + qsTr(" BPM") + width: 0.6 * parent.width + height: width + verticalAlignment: Text.AlignVCenter + anchors.centerIn: parent + fontSizeMode: Text.Fit + } + + Image { + source: "qrc:/gcompris/src/core/resource/bar_next.svg" + sourceSize.width: parent.width / 4 + width: sourceSize.width + height: width + fillMode: Image.PreserveAspectFit + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: -10 + Timer { + id: increaseBpm + interval: 500 + repeat: true + onTriggered: { + bpmIncreased() + interval = 1 + } + onRunningChanged: { + if(!running) + interval = 500 + } + } + + MouseArea { + anchors.fill: parent + onPressed: { + parent.scale = 0.85 + bpmIncreased() + increaseBpm.start() + } + onReleased: { + increaseBpm.stop() + parent.scale = 1 + } + } + } + } + + Image { + id: playButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/play.svg" + sourceSize.width: optionsRow.iconsWidth + visible: playButtonVisible + MouseArea { + anchors.fill: parent + onClicked: { + optionsRow.playButtonClicked() + emitOptionMessage(qsTr("Play melody")) + multipleStaff.play() + } + } + } + + Item { + id: clefOption + width: 2.3 * optionsRow.iconsWidth + height: optionsRow.iconsWidth + 10 + visible: clefButtonVisible + Rectangle { + color: "yellow" + opacity: 0.1 + border.width: 2 + border.color: "black" + anchors.fill: parent + radius: 10 + } + + SwitchableOptions { + id: clefButton + nbOptions: 2 + source: "qrc:/gcompris/src/activities/piano_composition/resource/" + (!currentIndex ? "trebbleClefButton.svg" + : "bassClefButton.svg") + sourceSize.width: optionsRow.iconsWidth + visible: clefButtonVisible + onClicked: { + //: Treble clef and Bass clef are the notations to indicate the pitch of the sound written on it. + emitOptionMessage(!currentIndex ? qsTr("Treble clef") : qsTr("Bass clef")) + } + anchors.topMargin: 3 + anchors.left: parent.left + anchors.leftMargin: 5 + } + + Image { + id: addClefButton + sourceSize.width: optionsRow.iconsWidth / 1.4 + source: "qrc:/gcompris/src/activities/piano_composition/resource/add.svg" + anchors.left: clefButton.right + anchors.leftMargin: 8 + visible: clefButton.visible + anchors.top: parent.top + anchors.topMargin: 10 + MouseArea { + anchors.fill: parent + onPressed: parent.scale = 0.8 + onReleased: { + background.clefType = !clefButton.currentIndex ? "Treble" : "Bass" + emitOptionMessage(!clefButton.currentIndex ? qsTr("Added Treble clef") : qsTr("Added Bass clef")) + parent.scale = 1 + clefAdded() + } + } + } + } + + 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: { + emitOptionMessage(qsTr("Undo")) + 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: changeAccidentalStyleButtonVisible ? (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 + //: Sharp notes and Flat notes represents the accidental style of the notes in the music. + emitOptionMessage(piano.useSharpNotation ? qsTr("Sharp notes") : qsTr("Flat notes")) + } + } + } + + SwitchableOptions { + id: lyricsOrPianoModeOption + nbOptions: optionsRow.lyricsOrPianoModes.length + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1-icon.svg".arg(optionsRow.lyricsOrPianoModes[currentIndex][1]) + anchors.top: parent.top + anchors.topMargin: 4 + visible: lyricsOrPianoModeOptionVisible + onClicked: emitOptionMessage(optionsRow.lyricsOrPianoModes[currentIndex][0]) + } + + Item { + id: rests + width: 2.3 * optionsRow.iconsWidth + height: optionsRow.iconsWidth + 10 + visible: restOptionsVisible + Rectangle { + color: "yellow" + opacity: 0.1 + border.width: 2 + border.color: "black" + anchors.fill: parent + radius: 10 + } + + // 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][1] === "Half") ? "Whole" : optionsRow.noteLengthName[currentIndex][1]).toLowerCase() + + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1Rest.svg".arg(restTypeImage) + nbOptions: optionsRow.noteLengthName.length + onClicked: { + background.restType = optionsRow.noteLengthName[currentIndex][1] + emitOptionMessage(optionsRow.translatedRestNames[currentIndex]) + } + rotation: optionsRow.noteLengthName[currentIndex][1] === "Half" ? 180 : 0 + visible: restOptionsVisible + anchors.topMargin: -3 + anchors.left: parent.left + anchors.leftMargin: 5 + } + + Image { + id: addRestButton + sourceSize.width: optionsRow.iconsWidth / 1.4 + source: "qrc:/gcompris/src/activities/piano_composition/resource/add.svg" + anchors.left: restOptions.right + anchors.leftMargin: 8 + visible: restOptions.visible + anchors.top: parent.top + anchors.topMargin: 10 + MouseArea { + anchors.fill: parent + onPressed: parent.scale = 0.8 + onReleased: { + emitOptionMessage(optionsRow.restAddedMessage[restOptionIndex]) + parent.scale = 1 + background.addMusicElementAndPushToStack(restType.toLowerCase(), "Rest") + } + } + } + } +} diff --git a/src/activities/piano_composition/PianoKey.qml b/src/activities/piano_composition/PianoKey.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/PianoKey.qml @@ -0,0 +1,86 @@ +/* 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 + property bool isKeyEnabled: true + + signal keyPressed + + SequentialAnimation { + id: keyPressAnimation + NumberAnimation { + target: pianoKey + property: "scale" + duration: 250 + to: 0.9 + } + NumberAnimation { + target: pianoKey + property: "scale" + duration: 250 + to: 1 + } + } + + border.color: "black" + Rectangle { + width: labelSquareSize + height: width + anchors.bottom: pianoKey.bottom + anchors.horizontalCenter: pianoKey.horizontalCenter + color: (piano.coloredKeyLabels.indexOf(keyName[0]) != -1) ? (piano.labelsColor === "inbuilt" ? noteColor : labelsColor) : "white" + 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 + } + } + + MouseArea { + anchors.fill: parent + onPressed: { + if(pianoKey.isKeyEnabled) { + keyPressed() + pianoKey.scale = 0.95 + } + } + onReleased: pianoKey.scale = 1 + } +} diff --git a/src/activities/piano_composition/PianoOctaveKeyboard.qml b/src/activities/piano_composition/PianoOctaveKeyboard.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/PianoOctaveKeyboard.qml @@ -0,0 +1,220 @@ +/* GCompris - PianoOctaveKeyboard.qml +* +* Copyright (C) 2016 Johnny Jazeix +* 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" + +Item { + id: piano + z: 3 + + property alias keyRepeater: keyRepeater + + property int numberOfWhite: 7 + property int currentOctaveNb: 0 + readonly property int maxNbOctaves: whiteKeyNoteLabels.length + + property real whiteWidth: width / numberOfWhite // 23 + property real whiteHeight: height // 120 + property real blackWidth: (whiteWidth + 1) / 2 // 13 + property real blackHeight: 2 * height / 3 // 80 + + property bool leftOctaveVisible: false + + property var coloredKeyLabels: ['C', 'D', 'E', 'F', 'G', 'A', 'B'] + // When the labelsColor is inbuilt, the default color mapping will be done, else assign any color externally in the activity. Example: Reference keys in note_names are red colored and all other are white. + property string labelsColor: "inbuilt" + + //: Translators, C, D, E, F, G, A and B are the note notations in English musical notation system. The numbers in the arguments represents the octave number of the note. For instance, C4 is a C note in the 4th octave. + readonly property var whiteKeyNoteLabelsArray: [ + ["F1", qsTr("F%1").arg(1)], + ["G1", qsTr("G%1").arg(1)], + ["A1", qsTr("A%1").arg(1)], + ["B1", qsTr("B%1").arg(1)], + ["C2", qsTr("C%1").arg(2)], + ["D2", qsTr("D%1").arg(2)], + ["E2", qsTr("E%1").arg(2)], + ["F2", qsTr("F%1").arg(2)], + ["G2", qsTr("G%1").arg(2)], + ["A2", qsTr("A%1").arg(2)], + ["B2", qsTr("B%1").arg(2)], + ["C3", qsTr("C%1").arg(3)], + ["D3", qsTr("D%1").arg(3)], + ["E3", qsTr("E%1").arg(3)], + ["F3", qsTr("F%1").arg(3)], + ["G3", qsTr("G%1").arg(3)], + ["A3", qsTr("A%1").arg(3)], + ["B3", qsTr("B%1").arg(3)], + ["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)], + ["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)], + ["D6", qsTr("D%1").arg(6)], + ["E6", qsTr("E%1").arg(6)], + ["F6", qsTr("F%1").arg(6)] + ] + + //: 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. + readonly property var blackKeySharpNoteLabelsArray: [ + ["C#3", qsTr("C#%1").arg(3)], + ["D#3", qsTr("D#%1").arg(3)], + ["F#3", qsTr("F#%1").arg(3)], + ["G#3", qsTr("G#%1").arg(3)], + ["A#3", qsTr("A#%1").arg(3)], + ["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)], + ["C#6", qsTr("C#%1").arg(6)], + ["D#6", qsTr("D#%1").arg(6)] + ] + + //: 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. + readonly property var blackKeyFlatNoteLabelsArray: [ + ["Db3", qsTr("Db%1").arg(3)], + ["Eb3", qsTr("Eb%1").arg(3)], + ["Gb3", qsTr("Gb%1").arg(3)], + ["Ab3", qsTr("Ab%1").arg(3)], + ["Bb3", qsTr("Bb%1").arg(3)], + ["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)], + ["Db6", qsTr("Db%1").arg(6)], + ["Eb6", qsTr("Eb%1").arg(6)] + ] + readonly property var blackKeyNoteLabels: useSharpNotation ? blackKeySharpNoteLabelsArray : blackKeyFlatNoteLabelsArray + + readonly property var keyLabelColors: { "C": "#FF0000", "D": "#FF7F00", "E": "#FFFF00", "F": "#32CD32", + "G": "#6495ED", "A": "#D02090", "B": "#FF1493", + "C#": "#FF6347", "D#": "#FFD700", "F#": "#20B2AA", "G#": "#8A2BE2", "A#": "#FF00FF", + "Db": "#FF6347", "Eb": "#FFD700", "Gb": "#20B2AA", "Ab": "#8A2BE2", "Bb": "#FF00FF"} + + // White key notes are from C3 to G4 when the clef is bass + property var whiteKeyNoteLabelsBass: [ + whiteKeyNoteLabelsArray.slice(11, 18), + whiteKeyNoteLabelsArray.slice(18, 22) + ] + // White key notes are from G3 to C6 when the clef is treble + property var whiteKeyNoteLabelsTreble: [ + whiteKeyNoteLabelsArray.slice(13, 18), + whiteKeyNoteLabelsArray.slice(18, 25), + whiteKeyNoteLabelsArray.slice(25, 32), + whiteKeyNoteLabelsArray.slice(32, 36) + ] + readonly property var whiteKeyNoteLabels: background.clefType === "Treble" ? whiteKeyNoteLabelsTreble : whiteKeyNoteLabelsBass + + 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 + + Repeater { + id: keyRepeater + model: whiteKeyNoteLabels.length ? whiteKeyNoteLabels[currentOctaveNb % whiteKeyNoteLabels.length].length : 0 + + Item { + width: whiteWidth + height: whiteHeight + x: (((currentOctaveNb === 0) ? (piano.numberOfWhite - whiteKeyNoteLabels[0].length + index) : index)) * whiteWidth + + property alias whiteKey: whiteKey + property alias blackKey: blackKey + + PianoKey { + id: whiteKey + color: "white" + width: whiteWidth + height: whiteHeight + labelSquareSize: piano.labelSquareSize + noteColor: keyLabelColors[keyName[0]] + keyName: whiteKeyNoteLabels[currentOctaveNb % whiteKeyNoteLabels.length][index][1] + labelsVisible: whiteLabelsVisible + isKeyEnabled: piano.whiteKeysEnabled + onKeyPressed: noteClicked(whiteKeyNoteLabels[currentOctaveNb % whiteKeyNoteLabels.length][index][0]) + } + + PianoKey { + id: blackKey + color: "black" + width: blackWidth + height: Math.min(blackHeight, whiteHeight - labelSquareSize - 10) + x: -width / 2 + visible: { + if(index || leftOctaveVisible) + return (["D", "E", "G", "A", "B"].indexOf(whiteKeyNoteLabels[currentOctaveNb % whiteKeyNoteLabels.length][index][0][0]) != -1) + return false + } + labelSquareSize: piano.labelSquareSize - 2 + noteColor: keyName ? keyLabelColors[keyName.substr(0, 2)] : "black" + keyName: { + for(var i = 0; i < blackKeyFlatNoteLabelsArray.length; i++) { + if((blackKeyFlatNoteLabelsArray[i][0][0] === whiteKeyNoteLabels[currentOctaveNb % whiteKeyNoteLabels.length][index][0][0]) + && (blackKeyFlatNoteLabelsArray[i][0][2] === whiteKeyNoteLabels[currentOctaveNb % whiteKeyNoteLabels.length][index][0][1])) + return blackKeyNoteLabels[i][1] + } + return "" + } + labelsVisible: blackLabelsVisible + isKeyEnabled: piano.blackKeysEnabled + onKeyPressed: { + for(var i = 0; i < blackKeyNoteLabels.length; i++) { + if(blackKeyNoteLabels[i][1] === keyName) { + noteClicked(blackKeyNoteLabels[i][0]) + break + } + } + } + } + } + } +} diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/Piano_composition.qml @@ -0,0 +1,431 @@ +/* GCompris - Piano_composition.qml + * + * Copyright (C) 2016 Johnny Jazeix + * 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 QtQuick.Controls 1.5 +import GCompris 1.0 + +import "../../core" +import "qrc:/gcompris/src/core/core.js" as 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 + + 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: { + var keyboardBindings = {} + keyboardBindings[Qt.Key_1] = 0 + keyboardBindings[Qt.Key_2] = 1 + keyboardBindings[Qt.Key_3] = 2 + keyboardBindings[Qt.Key_4] = 3 + keyboardBindings[Qt.Key_5] = 4 + keyboardBindings[Qt.Key_6] = 5 + keyboardBindings[Qt.Key_7] = 6 + keyboardBindings[Qt.Key_F1] = 1 + keyboardBindings[Qt.Key_F2] = 2 + keyboardBindings[Qt.Key_F3] = 3 + keyboardBindings[Qt.Key_F4] = 4 + keyboardBindings[Qt.Key_F5] = 5 + + if(event.key >= Qt.Key_1 && event.key <= Qt.Key_7) { + piano.keyRepeater.itemAt(keyboardBindings[event.key]).whiteKey.keyPressed() + } + else if(event.key >= Qt.Key_F1 && event.key <= Qt.Key_F5) { + if(piano.blackKeysEnabled) + findBlackKey(keyboardBindings[event.key]) + } + if(event.key === Qt.Key_Left && shiftKeyboardLeft.visible) { + piano.currentOctaveNb-- + } + if(event.key === Qt.Key_Right && shiftKeyboardRight.visible) { + piano.currentOctaveNb++ + } + if(event.key === Qt.Key_Delete) { + optionsRow.clearButtonClicked() + } + if(event.key === Qt.Key_Backspace) { + optionsRow.undoButtonClicked() + } + if(event.key === Qt.Key_Space) { + multipleStaff.play() + } + } + + function findBlackKey(keyNumber) { + for(var i = 0; keyNumber; i++) { + if(piano.keyRepeater.itemAt(i) == undefined) + break + if(piano.keyRepeater.itemAt(i).blackKey.visible) + keyNumber-- + if(keyNumber == 0) + piano.keyRepeater.itemAt(i).blackKey.keyPressed() + } + } + + // 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 multipleStaff: multipleStaff + property string staffLength: "short" + property alias melodyList: melodyList + property alias file: file + property alias piano: piano + property alias optionsRow: optionsRow + property alias lyricsArea: lyricsArea + } + + onStart: { Activity.start(items) } + onStop: { Activity.stop() } + + property string currentType: "Quarter" + property string restType: "Quarter" + property string clefType: bar.level == 2 ? "Bass" : "Treble" + property bool isLyricsMode: (optionsRow.lyricsOrPianoModeIndex === 1) && optionsRow.lyricsOrPianoModeOptionVisible + + File { + id: file + onError: console.error("File error: " + msg) + } + + Item { + id: clickedOptionMessage + + signal show(string message) + onShow: { + messageText.text = message + messageAnimation.stop() + messageAnimation.start() + } + + width: horizontalLayout ? parent.width / 12 : parent.width / 6 + height: width * 0.4 + visible: false + anchors.top: optionsRow.bottom + anchors.horizontalCenter: optionsRow.horizontalCenter + z: 5 + Rectangle { + id: messageRectangle + width: messageText.contentWidth + 5 + height: messageText.height + 5 + anchors.centerIn: messageText + color: "black" + opacity: 0.5 + border.width: 3 + border.color: "black" + radius: 15 + } + + GCText { + id: messageText + anchors.fill: parent + anchors.rightMargin: parent.width * 0.02 + anchors.leftMargin: parent.width * 0.02 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + fontSizeMode: Text.Fit + color: "white" + } + + SequentialAnimation { + id: messageAnimation + onStarted: clickedOptionMessage.visible = true + PauseAnimation { + duration: 1000 + } + NumberAnimation { + targets: [messageRectangle, messageText] + property: "opacity" + to: 0 + duration: 200 + } + onStopped: { + clickedOptionMessage.visible = false + messageRectangle.opacity = 0.5 + messageText.opacity = 1 + } + } + } + + MelodyList { + id: melodyList + onClose: { + visible = false + piano.enabled = true + bar.visible = true + focus = false + activity.focus = true + } + } + + Rectangle { + id: instructionBox + radius: 10 + width: background.width * 0.98 + height: 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.02 + anchors.leftMargin: parent.width * 0.02 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + fontSizeMode: Text.Fit + wrapMode: Text.WordWrap + text: Activity.instructions[bar.level - 1].text + } + } + + MultipleStaff { + id: multipleStaff + width: horizontalLayout ? parent.width * 0.50 : parent.width * 0.8 + height: horizontalLayout ? parent.height * 0.58 : parent.height * 0.3 + nbStaves: 2 + clef: clefType + coloredNotes: ['C','D', 'E', 'F', 'G', 'A', 'B'] + anchors.right: horizontalLayout ? parent.right: undefined + anchors.horizontalCenter: horizontalLayout ? undefined : parent.horizontalCenter + anchors.top: instructionBox.bottom + anchors.topMargin: parent.height * 0.1 + anchors.rightMargin: parent.width * 0.043 + noteHoverEnabled: true + onNoteClicked: { + if(selectedIndex === noteIndex) + selectedIndex = -1 + else { + selectedIndex = noteIndex + background.clefType = musicElementModel.get(selectedIndex).soundPitch_ + playNoteAudio(musicElementModel.get(selectedIndex).noteName_, musicElementModel.get(selectedIndex).noteType_, background.clefType, musicElementRepeater.itemAt(selectedIndex).duration) + } + } + } + + GCButtonScroll { + id: multipleStaffFlickButton + anchors.right: parent.right + anchors.rightMargin: 5 * ApplicationInfo.ratio + anchors.verticalCenter: multipleStaff.verticalCenter + width: horizontalLayout ? parent.width * 0.033 : parent.width * 0.06 + height: width * heightRatio + onUp: multipleStaff.flickableStaves.flick(0, multipleStaff.height * 1.3) + onDown: multipleStaff.flickableStaves.flick(0, -multipleStaff.height * 1.3) + upVisible: multipleStaff.flickableStaves.visibleArea.yPosition > 0 + downVisible: (multipleStaff.flickableStaves.visibleArea.yPosition + multipleStaff.flickableStaves.visibleArea.heightRatio) < 1 + } + + PianoOctaveKeyboard { + id: piano + width: horizontalLayout ? parent.width * 0.34 : parent.width * 0.7 + height: horizontalLayout ? parent.height * 0.40 : parent.width * 0.26 + anchors.horizontalCenter: horizontalLayout ? undefined : parent.horizontalCenter + anchors.left: horizontalLayout ? parent.left : undefined + anchors.leftMargin: parent.width * 0.04 + anchors.top: horizontalLayout ? multipleStaff.top : multipleStaff.bottom + anchors.topMargin: horizontalLayout ? parent.height * 0.08 : parent.height * 0.025 + blackLabelsVisible: [3, 4, 5, 6, 7, 8].indexOf(items.bar.level) == -1 ? false : true + useSharpNotation: bar.level != 4 + blackKeysEnabled: bar.level > 2 + visible: !background.isLyricsMode + currentOctaveNb: (background.clefType === "Bass") ? 0 : 1 + onNoteClicked: { + parent.addMusicElementAndPushToStack(note, currentType) + } + } + + function addMusicElementAndPushToStack(noteName, noteType, elementType) { + if(noteType === "Rest") + elementType = "rest" + else if(elementType == undefined) + elementType = "note" + + var tempModel = multipleStaff.createNotesBackup() + Activity.pushToStack(tempModel) + multipleStaff.addMusicElement(elementType, noteName, noteType, false, true, background.clefType) + } + + Image { + id: shiftKeyboardLeft + source: "qrc:/gcompris/src/core/resource/bar_previous.svg" + sourceSize.width: piano.width / 7 + width: sourceSize.width + height: width + fillMode: Image.PreserveAspectFit + visible: (piano.currentOctaveNb > 0) && piano.visible + 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) && piano.visible + anchors { + verticalCenter: piano.verticalCenter + left: piano.right + } + MouseArea { + anchors.fill: parent + onClicked: piano.currentOctaveNb++ + } + } + + LyricsArea { + id: lyricsArea + } + + OptionsRow { + id: optionsRow + anchors.top: instructionBox.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: parent.horizontalCenter + + noteOptionsVisible: bar.level > 4 + playButtonVisible: true + clefButtonVisible: bar.level > 2 + clearButtonVisible: true + undoButtonVisible: true + openButtonVisible: bar.level > 6 + saveButtonVisible: bar.level > 6 + changeAccidentalStyleButtonVisible: bar.level >= 4 + lyricsOrPianoModeOptionVisible: bar.level > 6 + restOptionsVisible: bar.level > 5 + bpmVisible: true + + onUndoButtonClicked: { + Activity.undoChange() + } + onClearButtonClicked: { + if((multipleStaff.musicElementModel.count > 1) && multipleStaff.selectedIndex === -1) { + Core.showMessageDialog(main, + qsTr("You have not selected any note. Do you want to erase all the notes?"), + qsTr("Yes"), function() { + Activity.undoStack = [] + lyricsArea.resetLyricsArea() + multipleStaff.eraseAllNotes() + multipleStaff.nbStaves = 2 + }, + qsTr("No"), null, + null + ) + } + else if((multipleStaff.musicElementModel.count > 1) && !multipleStaff.musicElementModel.get(multipleStaff.selectedIndex).isDefaultClef_) { + var noteIndex = multipleStaff.selectedIndex + var tempModel = multipleStaff.createNotesBackup() + Activity.pushToStack(tempModel) + multipleStaff.eraseNote(noteIndex) + } + } + 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() + onClefAdded: { + var insertingIndex = multipleStaff.selectedIndex === -1 ? multipleStaff.musicElementModel.count : multipleStaff.selectedIndex + var tempModel = multipleStaff.createNotesBackup() + Activity.pushToStack(tempModel) + multipleStaff.addMusicElement("clef", "", "", false, false, background.clefType) + if(background.clefType === "Bass") + piano.currentOctaveNb = 0 + else + piano.currentOctaveNb = 1 + } + onBpmDecreased: { + if(multipleStaff.bpmValue - 1 >= 1) + multipleStaff.bpmValue-- + } + onBpmIncreased: { + multipleStaff.bpmValue++ + } + onEmitOptionMessage: clickedOptionMessage.show(message) + } + + 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/README b/src/activities/piano_composition/README new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/README @@ -0,0 +1,10 @@ +1. bassClef.svg: https://openclipart.org/detail/12475/bass-clef +2. trebleClef.svg : https://openclipart.org/detail/12473/treble-clef +3. blackflat.svg: https://openclipart.org/detail/198043/mono-music-flat +4. blacksharp.svg: https://openclipart.org/detail/198039/mono-music-cross +5. half-note.svg: https://openclipart.org/detail/92971/half-note-stem-facing-up +6. quarter-note.svg: https://openclipart.org/detail/92875/quarter-note-stem-facing-up +7. eighth-note.svg: https://openclipart.org/detail/92869/eighth-note-stem-facing-up +8. whole-note.svg: https://openclipart.org/detail/140893/whole-note +9. bassClefButton.svg licensed under public domain +10. trebbleClefButton.svg licensed under public domain diff --git a/src/activities/piano_composition/Staff.qml b/src/activities/piano_composition/Staff.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/Staff.qml @@ -0,0 +1,77 @@ +/* GCompris - Staff.qml + * + * Copyright (C) 2016 Johnny Jazeix + * 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" + +Item { + id: staff + + property Item main: activity.main + + property int verticalDistanceBetweenLines: height / nbLines + + // Stave + readonly property int nbLines: 5 + + property bool lastPartition: false + + readonly property int yShift: activity.horizontalLayout ? 0 : 1.5 + + Repeater { + id: staffLines + model: nbLines + Rectangle { + width: staff.width + height: activity.horizontalLayout ? 5 : 3 + border.width: height / 2 + color: "black" + y: index * verticalDistanceBetweenLines + yShift + } + } + + // Ending vertical line of the staff. + Rectangle { + id: staffEndLine + width: activity.horizontalLayout ? 5 : 3 + border.width: width / 2 + height: (nbLines - 1) * verticalDistanceBetweenLines + width + color: "black" + x: staff.width + y: yShift + } + + // The 2nd vertical line denoting the end of multiple staves + Rectangle { + id: multiStaffEndLine + width: activity.horizontalLayout ? 5 : 3 + border.width: width / 2 + height: (nbLines - 1) * verticalDistanceBetweenLines + width + visible: lastPartition + color: "black" + x: staff.width - 10 + y: yShift + } +} diff --git a/src/activities/piano_composition/SwitchableOptions.qml b/src/activities/piano_composition/SwitchableOptions.qml new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/SwitchableOptions.qml @@ -0,0 +1,63 @@ +/* GCompris - SwitchableOptions.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" + +Image { + id: switchableOptions + + property int currentIndex: 0 + property int nbOptions: 1 + + signal clicked + + sourceSize.width: optionsRow.iconsWidth + anchors.top: parent.top + anchors.topMargin: -6 + MouseArea { + anchors.fill: parent + onClicked: { + parent.currentIndex = (parent.currentIndex + 1) % nbOptions + clickAnimation.start() + parent.clicked() + } + } + + SequentialAnimation { + id: clickAnimation + NumberAnimation { + target: switchableOptions + property: "scale" + to: 0.7 + duration: 150 + } + NumberAnimation { + target: switchableOptions + property: "scale" + to: 1 + duration: 150 + } + } +} diff --git a/src/activities/piano_composition/melodies.js b/src/activities/piano_composition/melodies.js new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/melodies.js @@ -0,0 +1,232 @@ +/* GCompris - melodies.js + * + * Copyright (C) 2016 Johnny Jazeix + * 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 . + **/ + +function get() { + return [ + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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 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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + }, + { + "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" + } + ]; +} diff --git a/src/activities/piano_composition/piano_composition.js b/src/activities/piano_composition/piano_composition.js new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/piano_composition.js @@ -0,0 +1,148 @@ +/* GCompris - piano_composition.js + * + * Copyright (C) 2016 Johnny Jazeix + * 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 . + */ +.pragma library +.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 undoStack = [] +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("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("Click on the note symbol to write different length notes such as whole notes, half notes, quarter notes and eighth notes.") + }, + { + "text": qsTr("Rests are equivalent to notes during which silence is maintained. Click on the rest symbol to select the rest length and then click on the add button to enter it to the staff.") + }, + { + "text": qsTr("Now you can load music and also save your composed one.") + } +] + +function start(items_) { + items = items_ + currentLevel = 0 + initLevel() +} + +function saveMelody() { + var notes = items.multipleStaff.getAllNotes() + if (!items.file.exists(userDir)) { + if (!items.file.mkpath(userDir)) + console.error("Could not create directory " + userDir); + else + console.debug("Created directory " + userDir); + } + + if(items.file.exists(userFile)) + 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() { + items.multipleStaff.stopAudios() +} + +function initLevel() { + items.bar.level = currentLevel + 1 + items.multipleStaff.bpmValue = 60 + + if(items.bar.level === 2) { + items.background.clefType = "Bass" + items.piano.currentOctaveNb = 0 + } + else { + items.background.clefType = "Treble" + items.piano.currentOctaveNb = 1 + } + + items.multipleStaff.initClefs(items.background.clefType) + + if(items.bar.level === 4) + items.piano.useSharpNotation = false + else + items.piano.useSharpNotation = true + + items.multipleStaff.nbStaves = 2 + items.optionsRow.noteOptionsIndex = 2 + items.background.currentType = "Quarter" + items.lyricsArea.resetLyricsArea() + undoStack = [] +} + +function pushToStack(data) { + undoStack.push(data) + // Maintain most recent 5 changes. Remove older ones (stack behaves as queue here). + if(undoStack.length > 5) + undoStack.shift() +} + +function undoChange() { + if(undoStack.length > 0) { + var undoState = undoStack[undoStack.length - 1] + undoStack.pop() + items.multipleStaff.redraw(undoState) + } +} + +function nextLevel() { + items.multipleStaff.eraseAllNotes() + if(numberOfLevel <= ++currentLevel) { + currentLevel = 0 + } + initLevel() +} + +function previousLevel() { + items.multipleStaff.eraseAllNotes() + if(--currentLevel < 0) { + currentLevel = numberOfLevel - 1 + } + initLevel() +} diff --git a/src/activities/piano_composition/piano_composition.svg b/src/activities/piano_composition/piano_composition.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/piano_composition.svg @@ -0,0 +1,113 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/README b/src/activities/piano_composition/resource/README new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/README @@ -0,0 +1,53 @@ +Copyright: Generated by Timothée Giet, 2018 (using a custom version of https://www3.nd.edu/~dthain/courses/cse20211/fall2013/wavfile/ ) +License: CC-BY-SA 4.0 +Files: +audio/A3.wav +audio/A4.wav +audio/A5.wav +audio/Ab3.wav +audio/Ab4.wav +audio/Ab5.wav +audio/B3.wav +audio/B4.wav +audio/B5.wav +audio/Bb3.wav +audio/Bb4.wav +audio/Bb5.wav +audio/C3.wav +audio/C4.wav +audio/C5.wav +audio/C6.wav +audio/D3.wav +audio/D4.wav +audio/D5.wav +audio/Db3.wav +audio/Db4.wav +audio/Db5.wav +audio/E3.wav +audio/E4.wav +audio/E5.wav +audio/Eb3.wav +audio/Eb4.wav +audio/Eb5.wav +audio/F3.wav +audio/F4.wav +audio/F5.wav +audio/G3.wav +audio/G4.wav +audio/G5.wav +audio/Gb3.wav +audio/Gb4.wav +audio/Gb5.wav + +Links for the images: +1. add.svg: http://www.iconarchive.com/show/flatastic-1-icons-by-custom-icon-design/add-1-icon.html +License: Free for non-commercial use. + +3. lyrics-icon.svg: https://lh3.googleusercontent.com/RfKfhilrQ5gW3dxKHnURpW0_shDLe8C6_7o6av6dv_83RFIJJb57eYE2zpygyZPzJRs=w64 + +4. piano-icon.svg: https://image.flaticon.com/icons/svg/195/195186.svg + +The following images have been taken from https://www.unc.edu/~mcash914/comp101/project10/page2.html : +5. eighthRest.svg +6. quarterRest.svg +7. wholeRest.svg diff --git a/src/activities/piano_composition/resource/add.svg b/src/activities/piano_composition/resource/add.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/add.svg @@ -0,0 +1,3052 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/activities/piano_composition/resource/audio/A3.wav b/src/activities/piano_composition/resource/audio/A3.wav new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ + + + + + + + image/svg+xml + + + + + Openclipart + + + Treble clef + 2006-12-03T12:59:04 + Hollow treble clef for use at drawings + https://openclipart.org/detail/12473/treble-clef-by-rickvanderzwet-12473 + + + rickvanderzwet + + + + + clef + line art + music + sign + silhouette + treble + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassClefButton.svg b/src/activities/piano_composition/resource/bassClefButton.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassClefButton.svg @@ -0,0 +1,295 @@ + + + + + + + + + + + + + webpage + button + shape + + + + + Benji Park + + + + + Benji Park + + + + + Benji Park + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassF4Eighth.svg b/src/activities/piano_composition/resource/bassF4Eighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassF4Eighth.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassF4Half.svg b/src/activities/piano_composition/resource/bassF4Half.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassF4Half.svg @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassF4Quarter.svg b/src/activities/piano_composition/resource/bassF4Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassF4Quarter.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassF4Whole.svg b/src/activities/piano_composition/resource/bassF4Whole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassF4Whole.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassG4Eighth.svg b/src/activities/piano_composition/resource/bassG4Eighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassG4Eighth.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassG4Half.svg b/src/activities/piano_composition/resource/bassG4Half.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassG4Half.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassG4Quarter.svg b/src/activities/piano_composition/resource/bassG4Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassG4Quarter.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/bassG4Whole.svg b/src/activities/piano_composition/resource/bassG4Whole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/bassG4Whole.svg @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/blackflat.svg b/src/activities/piano_composition/resource/blackflat.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/blackflat.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/blacksharp.svg b/src/activities/piano_composition/resource/blacksharp.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/blacksharp.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/edit-clear.svg b/src/activities/piano_composition/resource/edit-clear.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/edit-clear.svg @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Edit Clear + + + Andreas Nilsson + + + http://www.tango-project.org + + + clear + reset + blank + edit + + + + + + Jakub Steiner (although minimally ;) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/eighthRest.svg b/src/activities/piano_composition/resource/eighthRest.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/eighthRest.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/activities/piano_composition/resource/failed.svg b/src/activities/piano_composition/resource/failed.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/failed.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/genericNoteEighth.svg b/src/activities/piano_composition/resource/genericNoteEighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/genericNoteEighth.svg @@ -0,0 +1,55 @@ + + + + eighth note + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Eighth Note (Stem Facing Up) + 2010-10-27T16:06:43 + a single stem facing up eighth note + https://openclipart.org/detail/92869/eighth-note-(stem-facing-up)-by-jaschon + + + jaschon + + + + + black and white + eighth + flag + music + music note + musical notation + quaver + sound + staff + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/genericNoteHalf.svg b/src/activities/piano_composition/resource/genericNoteHalf.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/genericNoteHalf.svg @@ -0,0 +1,56 @@ + + + + half note + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Half Note (Stem Facing Up) + 2010-10-28T16:56:55 + A single half note with stem facing up. + https://openclipart.org/detail/92971/half-note-(stem-facing-up)-by-jaschon + + + jaschon + + + + + black and white + half + half note + music + music note + musical notation + note + quaver + sound + staff + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/genericNoteQuarter.svg b/src/activities/piano_composition/resource/genericNoteQuarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/genericNoteQuarter.svg @@ -0,0 +1,56 @@ + + + + quarter note + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Quarter Note (Stem Facing Up) + 2010-10-27T16:29:09 + A single quarter note with stem facing up. + https://openclipart.org/detail/92875/quarter-note-(stem-facing-up)-by-jaschon + + + jaschon + + + + + black and white + crotchet + music + music note + musical notation + note + quarter + quaver + sound + staff + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/genericNoteWhole.svg b/src/activities/piano_composition/resource/genericNoteWhole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/genericNoteWhole.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + whole note + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/lyrics-icon.svg b/src/activities/piano_composition/resource/lyrics-icon.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/lyrics-icon.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/src/activities/piano_composition/resource/open.svg b/src/activities/piano_composition/resource/open.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/open.svg @@ -0,0 +1,589 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Folder Icon Accept + 2005-01-31 + + + Jakub Steiner + + + + http://jimmac.musichall.cz + Active state - when files are being dragged to. + + + Novell, Inc. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/passed.svg b/src/activities/piano_composition/resource/passed.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/passed.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/piano-icon.svg b/src/activities/piano_composition/resource/piano-icon.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/piano-icon.svg @@ -0,0 +1,299 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/activities/piano_composition/resource/play.svg b/src/activities/piano_composition/resource/play.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/play.svg @@ -0,0 +1,241 @@ + + + + + + + + + + + + button + + + + + Open Clip Art Library + + + + + Architetto Francesco Rollandin + + + + + Architetto Francesco Rollandin + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/quarterRest.svg b/src/activities/piano_composition/resource/quarterRest.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/quarterRest.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/activities/piano_composition/resource/save.svg b/src/activities/piano_composition/resource/save.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/save.svg @@ -0,0 +1,747 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Save + + + Jakub Steiner + + + + + hdd + hard drive + save + io + store + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebbleClefButton.svg b/src/activities/piano_composition/resource/trebbleClefButton.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebbleClefButton.svg @@ -0,0 +1,275 @@ + + + + + + + + + + + + + webpage + button + shape + + + + + Benji Park + + + + + Benji Park + + + + + Benji Park + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleA3Eighth.svg b/src/activities/piano_composition/resource/trebleA3Eighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleA3Eighth.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleA3Half.svg b/src/activities/piano_composition/resource/trebleA3Half.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleA3Half.svg @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleA3Quarter.svg b/src/activities/piano_composition/resource/trebleA3Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleA3Quarter.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleA3Whole.svg b/src/activities/piano_composition/resource/trebleA3Whole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleA3Whole.svg @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleB3Eighth.svg b/src/activities/piano_composition/resource/trebleB3Eighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleB3Eighth.svg @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleB3Half.svg b/src/activities/piano_composition/resource/trebleB3Half.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleB3Half.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleB3Quarter.svg b/src/activities/piano_composition/resource/trebleB3Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleB3Quarter.svg @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleB3Whole.svg b/src/activities/piano_composition/resource/trebleB3Whole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleB3Whole.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleC4Eighth.svg b/src/activities/piano_composition/resource/trebleC4Eighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleC4Eighth.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleC4Half.svg b/src/activities/piano_composition/resource/trebleC4Half.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleC4Half.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleC4Quarter.svg b/src/activities/piano_composition/resource/trebleC4Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleC4Quarter.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleC4Whole.svg b/src/activities/piano_composition/resource/trebleC4Whole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleC4Whole.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + whole note + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleClef.svg b/src/activities/piano_composition/resource/trebleClef.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleClef.svg @@ -0,0 +1,142 @@ + + + + + + + + image/svg+xml + + + + + Openclipart + + + Treble clef + 2006-12-03T12:59:04 + Hollow treble clef for use at drawings + https://openclipart.org/detail/12473/treble-clef-by-rickvanderzwet-12473 + + + rickvanderzwet + + + + + clef + line art + music + sign + silhouette + treble + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleD3Quarter.svg b/src/activities/piano_composition/resource/trebleD3Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleD3Quarter.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleE3Eighth.svg b/src/activities/piano_composition/resource/trebleE3Eighth.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleE3Eighth.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleE3Half.svg b/src/activities/piano_composition/resource/trebleE3Half.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleE3Half.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleE3Quarter.svg b/src/activities/piano_composition/resource/trebleE3Quarter.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleE3Quarter.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/trebleE3Whole.svg b/src/activities/piano_composition/resource/trebleE3Whole.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/trebleE3Whole.svg @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2011-05-27T21:51:50 + There didn't seem to be a whole note on openclipart already. Here is a big one! + https://openclipart.org/detail/140893/whole-note-by-10binary + + + 10binary + + + + + music + notation + note + whole + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/undo.svg b/src/activities/piano_composition/resource/undo.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/undo.svg @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Edit Undo + + + edit + undo + revert + + + + + + + + + + + + + + + + + diff --git a/src/activities/piano_composition/resource/wholeRest.svg b/src/activities/piano_composition/resource/wholeRest.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/wholeRest.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + +