diff --git a/src/activities/note_names/NoteNames.qml b/src/activities/note_names/NoteNames.qml index 5e29d2ba1..4a0019ceb 100755 --- a/src/activities/note_names/NoteNames.qml +++ b/src/activities/note_names/NoteNames.qml @@ -1,445 +1,449 @@ /* GCompris - NoteNames.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.1 import QtQuick.Controls 1.0 import GCompris 1.0 import "../../core" import "../playpiano" import "note_names.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Rectangle { id: background anchors.fill: parent color: "#ABCDEF" property bool keyboardMode: false property bool horizontalLayout: background.width > background.height ? true : false signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property GCAudio audioEffects: activity.audioEffects property alias staff: staff property alias bar: bar property alias bonus: bonus property alias gridRepeater: gridRepeater property alias bottomNotesRepeater: bottomNotesRepeater property alias okButton: okButton property alias score: score - property var clef: items.bar.level > 10 ? "bass" : "treble" + readonly property string clef: items.bar.level > 10 ? "bass" : "treble" } onStart: { Activity.start(items) } onStop: { Activity.stop() } Rectangle { id: instructionBox radius: 10 width: background.width / 1.9 height: horizontalLayout ? background.height / 5 : background.height / 4 anchors.horizontalCenter: parent.horizontalCenter opacity: 0.8 border.width: 6 color: "white" border.color: "#87A6DD" GCText { id: instructionText visible: !staffText.visible color: "black" z: 3 anchors.fill: parent + anchors.rightMargin: parent.width * 0.1 + anchors.leftMargin: parent.width * 0.1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit wrapMode: Text.WordWrap text: [2, 3, 4, 12, 13, 14].indexOf(bar.level) !== -1 ? qsTr("Click on the note name to match the pitch. Then click OK to check.") : [5, 6, 7, 15, 16, 17].indexOf(bar.level) !== -1 ? qsTr("Now there are sharp notes. These pitches are raised a half step.") : // [8, 9, 10, 18, 19, 20] qsTr("Now there are flat notes. These pitches are lowered a half step.") } GCText { id: staffText visible: bar.level == 1 || bar.level == 11 height: background.height / 5 width: background.width / 1.5 z: 3 color: "black" fontSizeMode: Text.Fit anchors.fill: parent + anchors.rightMargin: parent.width * 0.1 + anchors.leftMargin: parent.width * 0.1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap text: bar.level == 1 ? qsTr("These are the eight basic notes in treble clef. They form the C Major Scale.") : qsTr("These are the eight basic notes in bass clef. They also form the C Major Scale. Notice that the note positions are different than in treble clef.") } } Rectangle { id: playScale width: horizontalLayout ? parent.width * 0.3 : parent.width * 0.35 height: 30 * ApplicationInfo.ratio color: "#d8ffffff" border.color: "#2a2a2a" border.width: 3 radius: 8 z: 5 anchors.top: instructionBox.bottom anchors.topMargin: 15 anchors.left: background.left anchors.leftMargin: background.width * 0.15 visible: bar.level == 1 || bar.level == 11 GCText { id: playScaleText anchors.centerIn: parent text: qsTr("Play scale") fontSizeMode: Text.Fit wrapMode: Text.Wrap } MouseArea { id: playScaleArea anchors.fill: parent onClicked: { for(var i = 0; i < Activity.bottomNotes.length; i++) { - var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + Activity.bottomNotes[3].note + '.wav'; - print(noteToPlay) - items.audioEffects.play(noteToPlay) + var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + Activity.bottomNotes[i].note + '.wav'; + items.audioEffects.append(noteToPlay) } } } states: [ State { name: "notclicked" PropertyChanges { target: playScale scale: 1.0 } }, State { name: "clicked" when: playScaleArea.pressed PropertyChanges { target: playScale scale: 0.9 } }, State { name: "hover" when: playScaleArea.containsMouse PropertyChanges { target: playScale scale: 1.1 } } ] Behavior on scale { NumberAnimation { duration: 70 } } } Rectangle { id: playButton width: horizontalLayout ? parent.width * 0.3 : parent.width * 0.35 height: 30 * ApplicationInfo.ratio color: "#d8ffffff" border.color: "#2a2a2a" border.width: 3 radius: 8 z: 5 anchors.top: instructionBox.bottom anchors.topMargin: 15 anchors.leftMargin: 30 anchors.left: playScale.right visible: bar.level == 1 || bar.level == 11 GCText { id: playButtonText anchors.centerIn: parent text: qsTr("Start levels") fontSizeMode: Text.Fit wrapMode: Text.Wrap } MouseArea { id: playButtonArea anchors.fill: parent onClicked: { Activity.nextLevel() } } states: [ State { name: "notclicked" PropertyChanges { target: playButton scale: 1.0 } }, State { name: "clicked" when: playButtonArea.pressed PropertyChanges { target: playButton scale: 0.9 } }, State { name: "hover" when: playButtonArea.containsMouse PropertyChanges { target: playButton scale: 1.1 } } ] Behavior on scale { NumberAnimation { duration: 70 } } } MultipleStaff { id: staff nbStaves: 1 clef: bar.level <= 10 ? "treble" : "bass" height: background.height / 4 width: bar.level == 1 || bar.level == 11 ? background.width * 0.8 : background.width / 2 anchors.bottom: parent.bottom anchors.bottomMargin: bar.level != 11 ? parent.height * 0.35 : parent.height * 0.25 anchors.horizontalCenter: bar.level == 1 || bar.level == 11 ? parent.horizontalCenter : undefined nbMaxNotesPerStaff: bar.level == 1 || bar.level == 11 ? 8 : 1 firstNoteX: bar.level == 1 || bar.level == 11 ? width / 5 : width / 2 } Grid { id: bottomNotesGrid rows: 1 spacing: horizontalLayout ? background.width * 0.03 : background.width * 0.01 anchors.bottom: background.bottom anchors.bottomMargin: background.height * 0.01 anchors.horizontalCenter: parent.horizontalCenter height: staff.height visible: (bar.level == 1 || bar.level == 11) ? true : false property int itemWidth: horizontalLayout ? background.width * 0.05 : background.width * 0.1 property int itemHeight: itemWidth Repeater { id: bottomNotesRepeater model: Activity.bottomNotes Rectangle { id: notes color: dummyNote.noteColorMap[Activity.bottomNotes[index].note] width: bottomNotesGrid.itemWidth height: bottomNotesGrid.itemHeight radius: width / 5 border.color: "black" GCText { id: bottomNotesText text: parseInt(Activity.bottomNotes[0].note) > 0 ? dummyNote.whiteNoteName[Activity.bottomNotes[index].note] : dummyNote.blackNoteName[Activity.bottomNotes[index].note] anchors.centerIn: parent fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter } MouseArea { id: buttonClick anchors.fill: parent onClicked: { select() } } function select() { grid.currentIndex = index var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + Activity.bottomNotes[index].note + '.wav' items.audioEffects.play(noteToPlay); } } } } DialogHelp { id: dialogHelp onClose: home() } Keys.onPressed: { if(event.key === Qt.Key_Space) { grid.currentItem.select() } } Keys.onReleased: { keyboardMode = true event.accepted = false } Keys.onEnterPressed: { grid.currentItem.select(); + Activity.checkAnswer(okButton.currentAnswer); } Keys.onReturnPressed: { grid.currentItem.select(); Activity.checkAnswer(okButton.currentAnswer); } Keys.onRightPressed: grid.moveCurrentIndexRight(); Keys.onLeftPressed: grid.moveCurrentIndexLeft(); Keys.onDownPressed: grid.moveCurrentIndexDown(); Keys.onUpPressed: grid.moveCurrentIndexUp(); ListModel { id: gridRepeater } GridView { id: grid visible: instructionText.visible anchors { left: staff.right right: background.right leftMargin: 15 * ApplicationInfo.ratio rightMargin: 50 * ApplicationInfo.ratio top: playButton.bottom } keyNavigationWraps: true interactive: false model: gridRepeater cellWidth: itemWidth + 10 cellHeight: itemHeight + 10 height: staff.height property int itemWidth: 60 property int itemHeight: itemWidth delegate: Rectangle { id: highlightRectangle width: grid.itemWidth * 1.15 height: grid.itemHeight * 1.15 radius: width / 5 color: index === grid.currentIndex ? "red" : "transparent" border.color: index === grid.currentIndex ? "white" : "transparent" Rectangle { id: noteRectangle color: staff.noteIsColored ? dummyNote.noteColorMap[note] : "white" width: grid.itemWidth height: grid.itemHeight radius: width / 5 border.color: "black" anchors.centerIn: parent visible: true GCText { id: noteText text: parseInt(note) > 0 ? dummyNote.whiteNoteName[note] : dummyNote.blackNoteName[note] anchors.centerIn: parent fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter } MouseArea { id: buttonClick anchors.fill: parent onClicked: select() } } function select() { grid.currentIndex = index var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + items.clef + '_pitches/' + '1' + '/' + note + '.wav' items.audioEffects.play(noteToPlay); okButton.currentAnswer = note } } } // Never visible, only used to access the note names, colors Note { id: dummyNote visible: false value: "1" blackType: [8, 9, 10, 18, 19, 20].indexOf(bar.level) === -1 ? "sharp" : "flat" } Image { id: okButton visible: instructionText.visible source:"qrc:/gcompris/src/core/resource/bar_ok.svg" width: parent.width * 0.1 height: parent.width * 0.1 anchors { right: background.right bottom: score.top bottomMargin: 20 rightMargin: 20 } property string currentAnswer: "" MouseArea { anchors.fill: parent onClicked: { if(okButton.currentAnswer !== "") Activity.checkAnswer(okButton.currentAnswer); okButton.currentAnswer = "" } } } Bar { id: bar content: BarEnumContent { value: (bar.level == 1 || bar.level == 11) ? (help | home | level ) : (help | home | level | reload) } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.reloadLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } Score { id: score anchors.bottom: background.bottom anchors.right: background.right visible: bar.level !== 1 && bar.level !== 11 } } } diff --git a/src/activities/playpiano/MultipleStaff.qml b/src/activities/playpiano/MultipleStaff.qml index 16515ff50..ca692bbf4 100644 --- a/src/activities/playpiano/MultipleStaff.qml +++ b/src/activities/playpiano/MultipleStaff.qml @@ -1,157 +1,157 @@ /* GCompris - MultipleStaff.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.1 import GCompris 1.0 import "../../core" Item { id: multipleStaff property int nbStaves property string clef property int distanceBetweenStaff: 20 property int currentStaff: 0 property int nbMaxNotesPerStaff: 6 property int firstNoteX: width / 5 property bool noteIsColored property bool isMetronomeDisplayed: false Column { Repeater { id: staves model: nbStaves Staff { id: staff clef: multipleStaff.clef height: (multipleStaff.height-distanceBetweenStaff*(nbStaves-1))/nbStaves width: multipleStaff.width y: index * (height+distanceBetweenStaff) lastPartition: index == nbStaves-1 nbMaxNotesPerStaff: multipleStaff.nbMaxNotesPerStaff noteIsColored: multipleStaff.noteIsColored isMetronomeDisplayed: multipleStaff.isMetronomeDisplayed firstNoteX: multipleStaff.firstNoteX } } } function addNote(newValue_, newType_, newBlackType_, highlightWhenPlayed_) { if(staves.itemAt(currentStaff).notes.count > nbMaxNotesPerStaff) { if(currentStaff+1 >= nbStaves) { return } else currentStaff++ } staves.itemAt(currentStaff).addNote(newValue_, newType_, newBlackType_, highlightWhenPlayed_); } function play() { print("hello") musicTimer.currentPlayedStaff = 0; musicTimer.currentNote = 0; musicTimer.interval = 1000; for(var v = 1 ; v < currentStaff ; ++ v) staves.itemAt(v).showMetronome = false; // Only display metronome if we want to staves.itemAt(0).showMetronome = isMetronomeDisplayed; musicTimer.start(); } function eraseAllNotes() { for(var v = 0 ; v <= currentStaff ; ++ v) staves.itemAt(v).eraseAllNotes(); currentStaff = 0; } property var whiteNotes: ["C", "D", "E", "F", "G", "A", "B", "2C", "2D", "2E", "2F"] property var blackNotesSharp: ["C#", "D#", "F#", "G#", "A#", "2C#"] property var blackNotesFlat: ["DB", "EB", "GB", "AB", "BB"] function loadFromData(data) { eraseAllNotes() var melody = data.split(" "); multipleStaff.clef = melody[0]; for(var i = 1 ; i < melody.length ; ++ i) { var noteLength = melody[i].length; var type = parseInt(melody[i][noteLength-1]); var noteStr = melody[i].substr(0, noteLength-1).toUpperCase(); if(whiteNotes.indexOf(noteStr) != -1) addNote(""+(whiteNotes.indexOf(noteStr)+1), type, "", false); else if (blackNotesSharp.indexOf(melody[i][0]) != -1) { addNote(""+(-1*blackNotesSharp.indexOf(noteStr)-1), type, "sharp", false); } else { addNote(""+(-1*blackNotesFlat.indexOf(noteStr)-1), type, "flat", false); } print(melody[i]); } } Timer { id: musicTimer property int currentPlayedStaff: 0 property int currentNote: 0 onRunningChanged: { if(!running && staves.itemAt(currentPlayedStaff).notes.get(currentNote) !== undefined) { var currentType = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mType var note = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mValue // TODO some notes does not play if they are played in the rcc directly... - var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/'+multipleStaff.clef+'_pitches/'+currentType+'/'+note+'.wav'; + var noteToPlay = 'qrc:/gcompris/src/activities/playpiano/resource/' + multipleStaff.clef+ '_pitches/' + currentType + '/'+note + '.wav'; items.audioEffects.play(noteToPlay); //staves.itemAt(currentPlayedStaff).notes.get(currentNote).play() if(currentNote == 0) { staves.itemAt(currentPlayedStaff).initMetronome(); } musicTimer.interval = staves.itemAt(currentPlayedStaff).notes.get(currentNote).mDuration; currentNote ++; if(currentNote > nbMaxNotesPerStaff) { currentNote = 0; currentPlayedStaff ++; if(currentPlayedStaff < nbStaves && currentNote < staves.itemAt(currentPlayedStaff).notes.count) { print("play next staff"); staves.itemAt(currentPlayedStaff).showMetronome = isMetronomeDisplayed; - if(currentPlayedStaff>0) - staves.itemAt(currentPlayedStaff-1).showMetronome = false; + if(currentPlayedStaff > 0) + staves.itemAt(currentPlayedStaff - 1).showMetronome = false; staves.itemAt(currentPlayedStaff).playNote(currentNote); musicTimer.start(); } } else if(staves.itemAt(currentPlayedStaff).notes.get(currentNote) !== undefined) { print("will play next " + JSON.stringify(staves.itemAt(currentPlayedStaff).notes.get(currentNote))); staves.itemAt(currentPlayedStaff).playNote(currentNote); musicTimer.start(); } } } } } diff --git a/src/activities/playpiano/Note.qml b/src/activities/playpiano/Note.qml index ea952d196..f76e54f73 100644 --- a/src/activities/playpiano/Note.qml +++ b/src/activities/playpiano/Note.qml @@ -1,107 +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.1 import QtGraphicalEffects 1.0 import GCompris 1.0 import "../../core" Item { id: note property string value property bool noteIsColored: true property int type: wholeNote property string noteType: type == wholeNote ? "whole" : type == halfNote ? "half" : type == quarterNote ? "quarter" : type == eighthNote ? "eighth" : "" property string blackType: "" // empty, "flat" or "sharp" //width: noteImage.width //height: noteImage.height readonly property int wholeNote: 1 readonly property int halfNote: 2 readonly property int quarterNote: 4 readonly property int eighthNote: 8 readonly property int noteDuration: 2000 / type property var noteColorMap: { "1": "#FF0000", "2": "#FF7F00", "3": "#FFFF00", "4": "#32CD32", "5": "#6495ED", "6": "#D02090", "7": "#FF1493", "8": "#FF0000", "9": "#FF7F00", "10": "#FFFF00", "11": "#32CD32", "-1": "#FF6347", "-2": "#FFD700", "-3": "#20B2AA", "-4": "#8A2BE2", "-5": "#FF00FF" } property var whiteNoteName: { "1": qsTr("C"), "2": qsTr("D"), "3": qsTr("E"), "4": qsTr("F"), "5": qsTr("G"), "6": qsTr("A"), "7": qsTr("B"), "8": qsTr("C") } property var blackNoteName: blackType == "flat" ? flatNoteName : sharpNoteName property var sharpNoteName: { "-1": qsTr("C#"), "-2": qsTr("D#"), "-3": qsTr("F#"), "-4": qsTr("G#"), "-5": qsTr("A#")} property var flatNoteName: { "-1": qsTr("Db"), "-2": qsTr("Eb"), "-3": qsTr("Gb"), "-4": qsTr("Ab"), "-5": qsTr("Bb")} property bool highlightWhenPlayed: false Image { id: blackTypeImage source: blackType !== "" ? "qrc:/gcompris/src/activities/playpiano/resource/black" + blackType + ".svg" : "" visible: value[0] === '-' sourceSize.width: noteImage.width/2.5 anchors.right: noteImage.left anchors.rightMargin: -width/2 anchors.bottom: noteImage.bottom anchors.bottomMargin: -height/2 fillMode: Image.PreserveAspectFit } Image { id: highlightImage source: "qrc:/gcompris/src/activities/playpiano/resource/note-highlight.svg" visible: false sourceSize.width: noteImage.width height: noteImage.height / 2 anchors.bottom: noteImage.bottom } + Rectangle { + id: highlightRectangle + width: noteImage.width * 0.9 + height: noteImage.height * 0.9 + color: "red" + opacity: 0.6 + border.color: "white" + radius: width / 6 + visible: false + } + Image { id: noteImage source: "qrc:/gcompris/src/activities/playpiano/resource/" + noteType + "-note.svg" sourceSize.width: 200 width: note.width height: note.height } // If the result is not good enough maybe have a rectangle and use opacity mask with a note ColorOverlay { anchors.fill: noteImage source: noteImage color: noteColorMap[value] // make image like it lays under red glass visible: noteIsColored } Timer { id: highlightTimer interval: noteDuration - onRunningChanged: highlightImage.visible = running + onRunningChanged: { +// highlightRectangle.visible = running + highlightImage.visible = running + } } } diff --git a/src/activities/playpiano/Staff.qml b/src/activities/playpiano/Staff.qml index bfa97381f..aa8fcadfc 100644 --- a/src/activities/playpiano/Staff.qml +++ b/src/activities/playpiano/Staff.qml @@ -1,182 +1,185 @@ /* GCompris - Staff.qml * * Copyright (C) 2016 Johnny Jazeix * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.1 import GCompris 1.0 import "../../core" Item { id: staff + + property Item main: activity.main + property int verticalDistanceBetweenLines: height / nbLines // todo width/nbLines * smth property string clef width: 400 height: 100 // Stave property int nbLines: 5 property bool lastPartition: false property bool isMetronomeDisplayed: false property bool showMetronome: false property alias notes: notes property int firstNoteX: defaultFirstNoteX property int defaultFirstNoteX: clefImage.width property int nbMaxNotesPerStaff property bool noteIsColored Image { id: clefImage - source: clef ? "qrc:/gcompris/src/activities/playpiano/resource/"+clef+"Clef.svg" : "" + source: clef ? "qrc:/gcompris/src/activities/playpiano/resource/" + clef + "Clef.svg" : "" sourceSize.width: (nbLines-2)*verticalDistanceBetweenLines } Repeater { model: nbLines Rectangle { width: staff.width height: 5 border.width: 5 color: "black" x: 0 y: index * verticalDistanceBetweenLines } } Rectangle { width: 5 border.width: 5 - height: (nbLines-1) * verticalDistanceBetweenLines+5 + 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 + height: (nbLines - 1) * verticalDistanceBetweenLines + 5 visible: lastPartition color: "black" - x: staff.width-10 + x: staff.width - 10 y: 0 } ListModel { id: notes } Rectangle { id: metronome width: 5 border.width: 10 - height: (nbLines-1) * verticalDistanceBetweenLines+5 + height: (nbLines - 1) * verticalDistanceBetweenLines + 5 visible: isMetronomeDisplayed && showMetronome color: "red" - x: firstNoteX-width/2 + x: firstNoteX - width/2 y: 0 Behavior on x { SmoothedAnimation { id: metronomeAnimation duration: -1 } } } property var mDuration function initMetronome() { var staffDuration = 0; for(var v = 0 ; v < notes.count ; ++ v) { staffDuration += notes.get(v).mDuration; } metronomeAnimation.velocity = 1; mDuration = staffDuration; metronome.x = staff.width; print("total duration " + staffDuration) print("total distance " + metronome.x) } function addNote(newValue_, newType_, newBlackType_, highlightWhenPlayed_) { notes.append({"mValue": newValue_, "mType": newType_, "mBlackType": newBlackType_, "mDuration": 2000/newType_, "mHighlightWhenPlayed": highlightWhenPlayed_}); } function playNote(noteId) { metronomeAnimation.velocity = staff.width * 1000 / (notes.get(noteId).mDuration * notes.count); print("velocity " + metronomeAnimation.velocity) } function eraseAllNotes() { notes.clear(); } - property int noteWidth: (staff.width-10-clefImage.width) / 10 + property int noteWidth: (staff.width-10 - clefImage.width) / 10 Row { id: notesRow - x: firstNoteX-noteWidth/2 + x: firstNoteX - noteWidth/2 Repeater { model: notes Note { value: mValue type: mType blackType: mBlackType highlightWhenPlayed: mHighlightWhenPlayed noteIsColored: staff.noteIsColored - width: noteWidth + width: (main.width > main.height) ? noteWidth : (notes.count == 1 ? noteWidth * 2 : noteWidth * 1.2) height: staff.height function play() { if(highlightWhenPlayed) highlightTimer.start(); } y: { var shift = 0; if(clef === "bass") { shift = -3 * verticalDistanceBetweenLines } if(blackType !== "") { if(blackType === "flat") { shift += - verticalDistanceBetweenLines } else { shift += - verticalDistanceBetweenLines / 2 } } if(mValue > 0) { return (nbLines-3)*verticalDistanceBetweenLines - (parseInt(mValue)-1)*verticalDistanceBetweenLines/2 + shift } else if(mValue >= -2) - return (nbLines-3)*verticalDistanceBetweenLines - (Math.abs(parseInt(mValue))-1)*verticalDistanceBetweenLines/2 + shift + return (nbLines - 3) * verticalDistanceBetweenLines - (Math.abs(parseInt(mValue)) - 1) * verticalDistanceBetweenLines/2 + shift else - return (nbLines-3)*verticalDistanceBetweenLines - (Math.abs(parseInt(mValue)))*verticalDistanceBetweenLines/2 + shift + return (nbLines - 3) * verticalDistanceBetweenLines - (Math.abs(parseInt(mValue))) * verticalDistanceBetweenLines/2 + shift } } } spacing: 0 } }