diff --git a/src/activities/piano_composition/MultipleStaff.qml b/src/activities/piano_composition/MultipleStaff.qml index d4764e2e8..398d20cb0 100644 --- a/src/activities/piano_composition/MultipleStaff.qml +++ b/src/activities/piano_composition/MultipleStaff.qml @@ -1,213 +1,215 @@ /* GCompris - MultipleStaff.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../../core" Item { id: multipleStaff property int nbStaves property string clef property int distanceBetweenStaff: multipleStaff.height / 4 property int currentStaff: 0 property int nbMaxNotesPerStaff: 6 property int firstNoteX: width / 5 property bool noteIsColored property bool isMetronomeDisplayed: false property alias flickableStaves: flickableStaves + signal noteClicked(string noteName, string noteLength) + Flickable { id: flickableStaves flickableDirection: Flickable.VerticalFlick contentWidth: staffColumn.width contentHeight: staffColumn.height + 1.5 * distanceBetweenStaff anchors.fill: parent clip: true Column { id: staffColumn spacing: distanceBetweenStaff anchors.top: parent.top anchors.topMargin: multipleStaff.height / 14 + distanceBetweenStaff / 2 Repeater { id: staves model: nbStaves Staff { id: staff clef: multipleStaff.clef height: multipleStaff.height / 5 width: multipleStaff.width - 5 lastPartition: index == (nbStaves - 1) nbMaxNotesPerStaff: multipleStaff.nbMaxNotesPerStaff noteIsColored: multipleStaff.noteIsColored isMetronomeDisplayed: multipleStaff.isMetronomeDisplayed firstNoteX: multipleStaff.firstNoteX } } } } function addNote(noteName, noteType, highlightWhenPlayed, playAudio) { if(staves.itemAt(currentStaff).notes.count > nbMaxNotesPerStaff) { if(currentStaff + 1 >= nbStaves) { var melody = getAllNotes() nbStaves++ flickableStaves.flick(0, - nbStaves * multipleStaff.height) currentStaff = 0 loadFromData(melody) } currentStaff++ } staves.itemAt(currentStaff).addNote(noteName, noteType, highlightWhenPlayed) if(playAudio) playNoteAudio(noteName, noteType) } function playNoteAudio(noteName, noteLength) { var audioPitchType // We should find a corresponding b type enharmonic notation for # type note to play the audio. if(noteName[1] === "#") { var pianoBlackKeysFlat var pianoBlackKeysSharp if(background.clefType === "treble") { pianoBlackKeysFlat = piano.blackNotesFlatTreble pianoBlackKeysSharp = piano.blackNotesSharpTreble } else { pianoBlackKeysFlat = piano.blackNotesFlatBass pianoBlackKeysSharp = piano.blackNotesSharpBass } var foundNote = false for(var i = 0; (i < pianoBlackKeysSharp.length) && !foundNote; i++) { for(var j = 0; j < pianoBlackKeysSharp[i].length; j++) { if(pianoBlackKeysSharp[i][j][0] === noteName) { noteName = pianoBlackKeysFlat[i][j][0] foundNote = true break } } } audioPitchType = parseInt(noteName[2]) } else if(noteName[1] === "b") audioPitchType = parseInt(noteName[2]) else audioPitchType = parseInt(noteName[1]) if(audioPitchType > 3) audioPitchType = "treble" else audioPitchType = "bass" var noteToPlay = "qrc:/gcompris/src/activities/piano_composition/resource/" + audioPitchType + "_pitches/" + noteName + ".wav" items.audioEffects.play(noteToPlay) } function getAllNotes() { var melody = "" + multipleStaff.clef for(var i = 0; i < nbStaves; i ++) { var staveNotes = staves.itemAt(i).notes for(var j = 0; j < staveNotes.count; j++) { melody += " " + staveNotes.get(j).noteName_ + staveNotes.get(j).noteType_ } } return melody } function loadFromData(data) { eraseAllNotes() var melody = data.split(" "); multipleStaff.clef = melody[0]; for(var i = 1 ; i < melody.length ; ++ i) { var noteLength = melody[i].length; var noteName = melody[i][0] var noteType 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] } addNote(noteName, noteType, false, false) } } function eraseAllNotes() { for(var v = 0 ; v <= currentStaff ; ++ v) staves.itemAt(v).eraseAllNotes(); currentStaff = 0; } function play() { musicTimer.currentPlayedStaff = 0 musicTimer.currentNote = 0 musicTimer.interval = 500 for(var v = 1 ; v < currentStaff ; ++ v) staves.itemAt(v).showMetronome = false // Only display metronome if we want to staves.itemAt(0).showMetronome = isMetronomeDisplayed musicTimer.start() } Timer { id: musicTimer property int currentPlayedStaff: 0 property int currentNote: 0 onRunningChanged: { if(!running && staves.itemAt(currentPlayedStaff) != undefined && staves.itemAt(currentPlayedStaff).notes.get(currentNote) !== undefined) { var currentType = staves.itemAt(currentPlayedStaff).notes.get(currentNote).noteType_ var note = staves.itemAt(currentPlayedStaff).notes.get(currentNote).noteName_ playNoteAudio(note, currentType) if(currentNote == 0) { staves.itemAt(currentPlayedStaff).initMetronome(); } musicTimer.interval = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mDuration - staves.itemAt(currentPlayedStaff).notesRepeater.itemAt(currentNote).play() + staves.itemAt(currentPlayedStaff).notesRepeater.itemAt(currentNote).highlightNote() currentNote ++ if(currentNote > nbMaxNotesPerStaff) { currentNote = 0 currentPlayedStaff ++ if(currentPlayedStaff < nbStaves && currentNote < staves.itemAt(currentPlayedStaff).notes.count) { staves.itemAt(currentPlayedStaff).showMetronome = isMetronomeDisplayed if(currentPlayedStaff > 0) staves.itemAt(currentPlayedStaff - 1).showMetronome = false staves.itemAt(currentPlayedStaff).playNote(currentNote) } } musicTimer.start() } } } } diff --git a/src/activities/piano_composition/Note.qml b/src/activities/piano_composition/Note.qml index ef19cefb8..a02007ddf 100644 --- a/src/activities/piano_composition/Note.qml +++ b/src/activities/piano_composition/Note.qml @@ -1,121 +1,121 @@ /* GCompris - Note.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import QtGraphicalEffects 1.0 import GCompris 1.0 import "../../core" Item { id: note property string noteName property bool noteIsColored: true property string noteType property string blackType: noteName[1] === "#" ? "sharp" : noteName[1] === "b" ? "flat" : ""// empty, "flat" or "sharp" readonly property string length: noteType == "Whole" ? 1 : noteType == "Half" ? 2 : noteType == "Quarter" ? 4 : noteType == 8 readonly property int noteDuration: 2000 / length 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 Image { id: blackTypeImage source: blackType !== "" ? "qrc:/gcompris/src/activities/piano_composition/resource/black" + blackType + ".svg" : "" sourceSize.width: noteImage.width / 2.5 anchors.right: parent.rotation === 180 ? undefined : noteImage.left anchors.left: parent.rotation === 180 ? noteImage.right : undefined rotation: parent.rotation === 180 ? 180 : 0 anchors.rightMargin: -width / 2 anchors.leftMargin: -width anchors.bottom: noteImage.bottom anchors.bottomMargin: height / 2 fillMode: Image.PreserveAspectFit } Image { id: highlightImage source: "qrc:/gcompris/src/activities/piano_composition/resource/note_highlight.png" visible: false sourceSize.width: noteImage.width height: noteImage.height / 2 anchors.bottom: noteImage.bottom } Rectangle { id: highlightRectangle width: noteImage.width * 0.9 height: noteImage.height * 0.9 color: "red" opacity: 0.6 border.color: "white" radius: width / 6 - visible: false + visible: noteMouseArea.containsMouse || highlightTimer.running } Image { id: noteImage source: "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + noteType + ".svg" sourceSize.width: 200 width: note.width height: note.height } // If the result is not good enough maybe have a rectangle and use opacity mask with a note ColorOverlay { anchors.fill: noteImage source: noteImage readonly property int noteColorNumber: blackType == "" ? whiteNoteName[noteName[0]] : blackNoteName[noteName.substring(0,2)] color: noteColorMap[noteColorNumber] // make image like it lays under red glass visible: noteIsColored } Timer { id: highlightTimer interval: noteDuration onRunningChanged: { highlightRectangle.visible = running // highlightImage.visible = running } } } diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml index 6bc921615..062355fa4 100644 --- a/src/activities/piano_composition/Piano_composition.qml +++ b/src/activities/piano_composition/Piano_composition.qml @@ -1,382 +1,383 @@ /* GCompris - Piano_composition.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import QtQuick.Controls 1.0 import GCompris 1.0 import "../../core" import "piano_composition.js" as Activity import "melodies.js" as Dataset ActivityBase { id: activity onStart: focus = true onStop: {} property bool horizontalLayout: background.width > background.height pageComponent: Rectangle { id: background anchors.fill: parent color: "#ABCDEF" signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } Keys.onPressed: { if(event.key === Qt.Key_1) { piano.whiteKeyRepeater.itemAt(0).keyPressed() } if(event.key === Qt.Key_2) { piano.whiteKeyRepeater.itemAt(1).keyPressed() } if(event.key === Qt.Key_3) { piano.whiteKeyRepeater.itemAt(2).keyPressed() } if(event.key === Qt.Key_4) { piano.whiteKeyRepeater.itemAt(3).keyPressed() } if(event.key === Qt.Key_5) { piano.whiteKeyRepeater.itemAt(4).keyPressed() } if(event.key === Qt.Key_6) { piano.whiteKeyRepeater.itemAt(5).keyPressed() } if(event.key === Qt.Key_7) { piano.whiteKeyRepeater.itemAt(6).keyPressed() } if(event.key === Qt.Key_8) { piano.whiteKeyRepeater.itemAt(7).keyPressed() } if(event.key === Qt.Key_F1 && bar.level >= 4) { piano.blackKeyRepeater.itemAt(0).keyPressed() } if(event.key === Qt.Key_F2 && bar.level >= 4) { piano.blackKeyRepeater.itemAt(1).keyPressed() } if(event.key === Qt.Key_F3 && bar.level >= 4) { piano.blackKeyRepeater.itemAt(2).keyPressed() } if(event.key === Qt.Key_F4 && bar.level >= 4) { piano.blackKeyRepeater.itemAt(3).keyPressed() } if(event.key === Qt.Key_F5 && bar.level >= 4) { piano.blackKeyRepeater.itemAt(4).keyPressed() } if(event.key === Qt.Key_Delete) { multipleStaff.eraseAllNotes() } if(event.key === Qt.Key_Space) { multipleStaff.play() } } // 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 } onStart: { Activity.start(items) } onStop: { Activity.stop() } property string currentType: "Whole" property string clefType: bar.level == 2 ? "bass" : "treble" File { id: file onError: console.error("File error: " + msg) } MelodyList { id: melodyList onClose: { visible = false piano.enabled = true bar.visible = true } } 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.1 anchors.leftMargin: parent.width * 0.1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit wrapMode: Text.WordWrap text: Activity.instructions[bar.level - 1].text } } 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 nbMaxNotesPerStaff: 8 noteIsColored: true isMetronomeDisplayed: true 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 + onNoteClicked: playNoteAudio(noteName, noteLength) } 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 } Piano { 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: [4, 5, 6, 7, 8].indexOf(items.bar.level) == -1 ? false : true useSharpNotation: bar.level == 5 ? false : true onNoteClicked: multipleStaff.addNote(note, currentType, false, true) } Image { id: shiftKeyboardLeft source: "qrc:/gcompris/src/core/resource/bar_next.svg" sourceSize.width: piano.width / 7 width: sourceSize.width height: width fillMode: Image.PreserveAspectFit rotation: 180 visible: piano.currentOctaveNb > 0 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 anchors { verticalCenter: piano.verticalCenter left: piano.right } MouseArea { anchors.fill: parent onClicked: piano.currentOctaveNb++ } } Row { id: optionsRow anchors.top: instructionBox.bottom anchors.topMargin: 10 spacing: 15 anchors.horizontalCenter: parent.horizontalCenter readonly property var noteLengthName: ["Whole", "Half", "Quarter", "Eighth"] Image { id: noteTypeOption property int currentIndex: 0 source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex]) sourceSize.width: 50 anchors.top: parent.top anchors.topMargin: -6 visible: (bar.level == 1 || bar.level == 2) ? false : true MouseArea { anchors.fill: parent onClicked: { parent.currentIndex = (parent.currentIndex + 1) % 4 clickAnimation.start() currentType = optionsRow.noteLengthName[parent.currentIndex] } } SequentialAnimation { id: clickAnimation NumberAnimation { target: noteTypeOption property: "scale" to: 0.7 duration: 150 } NumberAnimation { target: noteTypeOption property: "scale" to: 1 duration: 150 } } } Image { id: playButton source: "qrc:/gcompris/src/activities/piano_composition/resource/play.svg" sourceSize.width: 50 MouseArea { anchors.fill: parent onClicked: multipleStaff.play() } } Image { id: clefButton source: clefType == "bass" ? "qrc:/gcompris/src/activities/piano_composition/resource/bassClefButton.svg" : "qrc:/gcompris/src/activities/piano_composition/resource/trebbleClefButton.svg" sourceSize.width: 50 visible: bar.level > 2 MouseArea { anchors.fill: parent onClicked: { multipleStaff.eraseAllNotes() clefType = (clefType == "bass") ? "treble" : "bass" print(piano.currentOctaveNb) } } } Image { id: clearButton source: "qrc:/gcompris/src/activities/piano_composition/resource/edit-clear.svg" sourceSize.width: 50 MouseArea { anchors.fill: parent onClicked: multipleStaff.eraseAllNotes() } } Image { id: openButton source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" sourceSize.width: 50 visible: bar.level == 6 || bar.level == 7 MouseArea { anchors.fill: parent onClicked: { var dataset = Dataset.get() for(var i = 0; i < dataset.length; i++) { melodyList.melodiesModel.append(dataset[i]) } piano.enabled = false bar.visible = false melodyList.visible = true } } } Image { id: saveButton source: "qrc:/gcompris/src/activities/piano_composition/resource/save.svg" sourceSize.width: 50 visible: bar.level == 6 || bar.level == 7 MouseArea { anchors.fill: parent onClicked: { Activity.saveMelody() } } } Image { id: changeAccidentalStyleButton source: piano.useSharpNotation ? "qrc:/gcompris/src/activities/piano_composition/resource/blacksharp.svg" : "qrc:/gcompris/src/activities/piano_composition/resource/blackflat.svg" visible: bar.level >= 4 MouseArea { anchors.fill: parent onClicked: piano.useSharpNotation = !piano.useSharpNotation } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/piano_composition/Staff.qml b/src/activities/piano_composition/Staff.qml index 8c1dfa3a9..72573f866 100644 --- a/src/activities/piano_composition/Staff.qml +++ b/src/activities/piano_composition/Staff.qml @@ -1,203 +1,200 @@ /* GCompris - Staff.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../../core" import "piano_composition.js" as Activity Item { id: staff property Item main: activity.main property int verticalDistanceBetweenLines: height / nbLines // todo width/nbLines * smth property string clef width: 400 height: 100 // Stave readonly property int nbLines: 5 property bool lastPartition: false property bool isMetronomeDisplayed: false property bool showMetronome: false property alias notes: notes property int firstNoteX: defaultFirstNoteX property int defaultFirstNoteX: clefImage.width property int nbMaxNotesPerStaff property bool noteIsColored property alias notesRepeater: notesRepeater Image { id: clefImage source: clef ? "qrc:/gcompris/src/activities/piano_composition/resource/" + clef + "Clef.svg" : "" sourceSize.width: (nbLines - 2) * verticalDistanceBetweenLines } Repeater { model: nbLines Rectangle { width: staff.width height: 5 border.width: 5 color: "black" x: 0 y: index * verticalDistanceBetweenLines } } Rectangle { width: 5 border.width: 5 height: (nbLines - 1) * verticalDistanceBetweenLines + 5 color: "black" x: staff.width y: 0 } // end of partition line Rectangle { width: 5 border.width: 5 height: (nbLines - 1) * verticalDistanceBetweenLines + 5 visible: lastPartition color: "black" x: staff.width - 10 y: 0 } ListModel { id: notes } Rectangle { id: metronome width: 5 border.width: 10 height: (nbLines - 1) * verticalDistanceBetweenLines + 5 visible: isMetronomeDisplayed && showMetronome color: "red" x: firstNoteX - width/2 y: 0 Behavior on x { SmoothedAnimation { id: metronomeAnimation duration: -1 } } } property var mDuration function initMetronome() { var staffDuration = 0; for(var v = 0 ; v < notes.count ; ++ v) { staffDuration += notes.get(v).mDuration; } metronomeAnimation.velocity = 1; mDuration = staffDuration; metronome.x = staff.width; print("total duration " + staffDuration) print("total distance " + metronome.x) } function addNote(noteName, noteType, blackType, highlightWhenPlayed) { var duration if(noteType == "Whole") duration = 2000 / 1 else if(noteType == "Half") duration = 3000 / 2 else if(noteType == "Quarter") duration = 4000 / 4 else duration = 6500 / 8 notes.append({"noteName_": noteName, "noteType_": noteType, "mDuration": duration, "highlightWhenPlayed": highlightWhenPlayed}); } function playNote(noteId) { metronomeAnimation.velocity = staff.width * 1000 / (notes.get(noteId).mDuration * notes.count); print("velocity " + metronomeAnimation.velocity) } function eraseAllNotes() { notes.clear(); } property int noteWidth: (staff.width - 10 - clefImage.width) / 10 Row { id: notesRow x: firstNoteX - noteWidth/2 Repeater { id: notesRepeater model: notes Note { noteName: noteName_ noteType: noteType_ highlightWhenPlayed: highlightWhenPlayed noteIsColored: staff.noteIsColored width: (notes.count == 1 && items.staffLength === "long") ? Math.min(items.background.width,items.background.height) * 0.1 : noteWidth height: staff.height noteDetails: Activity.getNoteDetails(noteName) rotation: { if(noteDetails.positonOnStaff < 0 && noteType === "Whole") return 0 else if(noteDetails.positonOnStaff > 6 && noteType === "Whole") return 180 else return noteDetails.rotation } MouseArea { + id: noteMouseArea anchors.fill: parent - onClicked: { - print(items.staffLength) - print(items.background.width,items.background.height) - } + hoverEnabled: true + onClicked: multipleStaff.noteClicked(noteName, noteType) } - function play() { -// if(highlightWhenPlayed) { - highlightTimer.start(); -// } + function highlightNote() { + highlightTimer.start() } y: { var shift = -verticalDistanceBetweenLines / 2 var relativePosition = noteDetails.positonOnStaff var imageY = (nbLines - 3) * verticalDistanceBetweenLines if(rotation === 180) { return imageY - (4 - relativePosition) * verticalDistanceBetweenLines + shift } return imageY - (6 - relativePosition) * verticalDistanceBetweenLines + shift } } } } }