Changeset View
Changeset View
Standalone View
Standalone View
src/activities/piano_composition/MultipleStaff.qml
Context not available. | |||||
32 | property string clef | 32 | property string clef | ||
---|---|---|---|---|---|
33 | property int distanceBetweenStaff: multipleStaff.height / 3.3 | 33 | property int distanceBetweenStaff: multipleStaff.height / 3.3 | ||
34 | 34 | | |||
35 | property int currentStaff: 0 | 35 | property int insertingIndex: 0 | ||
36 | | ||||
37 | property int nbMaxNotesPerStaff: 6 | | |||
38 | 36 | | |||
39 | // Stores the note number which is to be replaced. | 37 | // Stores the note number which is to be replaced. | ||
40 | property int noteToReplace: -1 | 38 | property int selectedIndex: -1 | ||
41 | property bool noteIsColored | 39 | property bool noteIsColored | ||
42 | property bool noteHoverEnabled: true | 40 | property bool noteHoverEnabled: true | ||
43 | property bool centerNotesPosition: false | 41 | property bool centerNotesPosition: false | ||
Context not available. | |||||
45 | readonly property bool isMusicPlaying: musicTimer.running | 43 | readonly property bool isMusicPlaying: musicTimer.running | ||
46 | 44 | | |||
47 | property alias flickableStaves: flickableStaves | 45 | property alias flickableStaves: flickableStaves | ||
46 | property alias notesModel: notesModel | ||||
48 | property real flickableTopMargin: multipleStaff.height / 14 + distanceBetweenStaff / 3.5 | 47 | property real flickableTopMargin: multipleStaff.height / 14 + distanceBetweenStaff / 3.5 | ||
49 | property bool isFlickable: true | 48 | property bool isFlickable: true | ||
49 | property int currentEnteringStaff: 0 | ||||
50 | 50 | | |||
51 | /** | 51 | /** | ||
52 | * Emitted when a note is clicked. | 52 | * Emitted when a note is clicked. | ||
Context not available. | |||||
55 | */ | 55 | */ | ||
56 | signal noteClicked(string noteName, string noteType, int noteIndex) | 56 | signal noteClicked(string noteName, string noteType, int noteIndex) | ||
57 | 57 | | |||
58 | /** | | |||
59 | * Emitted when a change in any note is made to push it to the undo stack. | | |||
60 | */ | | |||
61 | signal pushToUndoStack(int noteIndex, string oldNoteName, string oldNoteType) | | |||
62 | | ||||
63 | /** | 58 | /** | ||
64 | * Emitted for the notes while a melody is playing. | 59 | * Emitted for the notes while a melody is playing. | ||
65 | * | 60 | * | ||
Context not available. | |||||
98 | } | 93 | } | ||
99 | } | 94 | } | ||
100 | 95 | | |||
101 | property real noteWidth: (multipleStaff.width - 15 - staves.itemAt(0).clefImageWidth) / 11.3 | 96 | readonly property real noteWidth: (multipleStaff.width - 15 - staves.itemAt(0).clefImageWidth) / 10 | ||
102 | Repeater { | 97 | Repeater { | ||
103 | id: notesRepeater | 98 | id: notesRepeater | ||
104 | model: notesModel | 99 | model: notesModel | ||
Context not available. | |||||
110 | width: flickableStaves.noteWidth | 105 | width: flickableStaves.noteWidth | ||
111 | height: multipleStaff.height / 5 | 106 | height: multipleStaff.height / 5 | ||
112 | 107 | | |||
113 | readonly property int currentStaffNb: index / nbMaxNotesPerStaff | 108 | property int staffNb: staffNb_ | ||
114 | readonly property bool isFirstNoteOnStaff: (index % nbMaxNotesPerStaff) === 0 | 109 | readonly property real shiftDistance: blackType != "" ? flickableStaves.noteWidth / 6 : 0 | ||
115 | readonly property real defaultX: isFirstNoteOnStaff ? staves.itemAt(0).clefImageWidth | | |||
116 | : (notesRepeater.itemAt(index - 1) == undefined) ? 0 | | |||
117 | : (notesRepeater.itemAt(index - 1).x + flickableStaves.noteWidth) | | |||
118 | readonly property real centeredPosition: (multipleStaff.width / 2.5 - (flickableStaves.noteWidth * notesModel.count / 2) + defaultX) | | |||
119 | readonly property real shiftDistance: blackType != "" ? width / 7 : 0 | | |||
120 | | ||||
121 | x: shiftDistance + (multipleStaff.centerNotesPosition ? centeredPosition : defaultX) | | |||
122 | 110 | | |||
123 | noteDetails: multipleStaff.getNoteDetails(noteName, noteType) | 111 | noteDetails: multipleStaff.getNoteDetails(noteName, noteType) | ||
124 | 112 | | |||
Context not available. | |||||
133 | highlightTimer.start() | 121 | highlightTimer.start() | ||
134 | } | 122 | } | ||
135 | 123 | | |||
124 | x: shiftDistance + (isFirstNote_ ? (staves.itemAt(0).clefImageWidth + 5) | ||||
125 | : (notesRepeater.itemAt(index - 1) == undefined) ? 0 | ||||
126 | : (notesRepeater.itemAt(index - 1).x + flickableStaves.noteWidth)) | ||||
127 | | ||||
136 | y: { | 128 | y: { | ||
137 | if(noteDetails === undefined || staves.itemAt(currentStaffNb) == undefined) | 129 | if(noteDetails === undefined || staves.itemAt(staffNb) == undefined) | ||
138 | return 0 | 130 | return 0 | ||
139 | 131 | | |||
140 | var verticalDistanceBetweenLines = staves.itemAt(0).verticalDistanceBetweenLines | 132 | var verticalDistanceBetweenLines = staves.itemAt(0).verticalDistanceBetweenLines | ||
141 | var shift = -verticalDistanceBetweenLines / 2 | 133 | var shift = -verticalDistanceBetweenLines / 2 | ||
142 | var relativePosition = noteDetails.positionOnStaff | 134 | var relativePosition = noteDetails.positionOnStaff | ||
143 | var imageY = flickableTopMargin + staves.itemAt(currentStaffNb).y + 2 * verticalDistanceBetweenLines | 135 | var imageY = flickableTopMargin + staves.itemAt(staffNb).y + 2 * verticalDistanceBetweenLines | ||
144 | 136 | | |||
145 | if(rotation === 180) { | 137 | if(rotation === 180) { | ||
146 | return imageY - (4 - relativePosition) * verticalDistanceBetweenLines + shift | 138 | return imageY - (4 - relativePosition) * verticalDistanceBetweenLines + shift | ||
Context not available. | |||||
189 | /** | 181 | /** | ||
190 | * Adds a note to the staff. | 182 | * Adds a note to the staff. | ||
191 | */ | 183 | */ | ||
192 | function addNote(noteName, noteType, highlightWhenPlayed, playAudio, isReplacing) { | 184 | function addNote(noteName, noteType, highlightWhenPlayed, playAudio) { | ||
193 | var duration | 185 | var duration | ||
194 | if(noteType === "Rest") | 186 | if(noteType === "Rest") | ||
195 | duration = calculateTimerDuration(noteName) | 187 | duration = calculateTimerDuration(noteName) | ||
196 | else | 188 | else | ||
197 | duration = calculateTimerDuration(noteType) | 189 | duration = calculateTimerDuration(noteType) | ||
198 | 190 | | |||
199 | if(!isReplacing) { | 191 | var isNextStaff = notesModel.count && ((staves.itemAt(0).width - notesRepeater.itemAt(notesModel.count - 1).x) < 2 * flickableStaves.noteWidth) | ||
200 | pushToUndoStack(notesModel.count, "none", "none", noteName, noteType) | 192 | var isFirstPosition = false | ||
201 | notesModel.append({"noteName_": noteName, "noteType_": noteType, "mDuration": duration, | 193 | if((notesModel.count == 0) || isNextStaff) { | ||
202 | "highlightWhenPlayed": highlightWhenPlayed}) | 194 | if(isNextStaff) | ||
195 | multipleStaff.currentEnteringStaff++ | ||||
196 | | ||||
197 | if(multipleStaff.currentEnteringStaff >= multipleStaff.nbStaves) { | ||||
198 | var melody = getAllNotes() | ||||
199 | multipleStaff.nbStaves++ | ||||
200 | flickableStaves.flick(0, - nbStaves * multipleStaff.height) | ||||
201 | multipleStaff.currentEnteringStaff = 0 | ||||
202 | loadFromData(melody) | ||||
203 | multipleStaff.currentEnteringStaff++ | ||||
204 | } | ||||
205 | | ||||
206 | isFirstPosition = true | ||||
203 | } | 207 | } | ||
208 | | ||||
209 | if(multipleStaff.insertingIndex == notesModel.count) | ||||
210 | notesModel.append({"noteName_": noteName, "noteType_": noteType, "mDuration": duration, | ||||
211 | "highlightWhenPlayed": highlightWhenPlayed, "staffNb_": multipleStaff.currentEnteringStaff, | ||||
212 | "isFirstNote_": isFirstPosition}) | ||||
204 | else { | 213 | else { | ||
205 | var oldNoteDetails = notesModel.get(noteToReplace) | 214 | var tempModel = createNotesBackup() | ||
206 | pushToUndoStack(noteToReplace, oldNoteDetails.noteName_, oldNoteDetails.noteType_) | 215 | tempModel.splice(multipleStaff.insertingIndex, 0, { "noteName_": noteName, "noteType_": noteType }) | ||
207 | notesModel.set(noteToReplace, { "noteName_": noteName, "noteType_": noteType, "mDuration": duration }) | 216 | redrawNotes(tempModel) | ||
217 | } | ||||
218 | | ||||
219 | multipleStaff.insertingIndex = notesModel.count | ||||
220 | multipleStaff.selectedIndex = -1 | ||||
221 | | ||||
222 | if(playAudio) | ||||
223 | playNoteAudio(noteName, noteType) | ||||
224 | } | ||||
225 | | ||||
226 | /** | ||||
227 | * Creates a backup of the notesModel before erasing it. | ||||
228 | * | ||||
229 | * This backup data is used to redraw the notes. | ||||
230 | */ | ||||
231 | function createNotesBackup() { | ||||
232 | var tempModel = [] | ||||
233 | for(var i = 0; i < notesModel.count; i++) | ||||
234 | tempModel.push(JSON.parse(JSON.stringify(notesModel.get(i)))) | ||||
235 | | ||||
236 | return tempModel | ||||
237 | } | ||||
238 | | ||||
239 | /** | ||||
240 | * Redraws all the notes on the staves. | ||||
241 | */ | ||||
242 | function redrawNotes(notes) { | ||||
243 | eraseAllNotes() | ||||
244 | for(var i = 0; i < notes.length; i++) { | ||||
245 | addNote(notes[i]["noteName_"], notes[i]["noteType_"], false, false) | ||||
208 | } | 246 | } | ||
209 | 247 | | |||
210 | if(notesModel.count > nbMaxNotesPerStaff * nbStaves) { | 248 | if((multipleStaff.currentEnteringStaff + 1 < multipleStaff.nbStaves) && (multipleStaff.nbStaves > 2)) { | ||
211 | var melody = getAllNotes() | 249 | var melody = getAllNotes() | ||
212 | nbStaves++ | 250 | multipleStaff.nbStaves = multipleStaff.currentEnteringStaff + 1 | ||
213 | flickableStaves.flick(0, - nbStaves * multipleStaff.height) | 251 | flickableStaves.flick(0, - nbStaves * multipleStaff.height) | ||
214 | currentStaff = 0 | 252 | multipleStaff.currentEnteringStaff = 0 | ||
215 | loadFromData(melody) | 253 | loadFromData(melody) | ||
216 | } | 254 | } | ||
217 | | ||||
218 | if(playAudio) | | |||
219 | playNoteAudio(noteName, noteType) | | |||
220 | } | 255 | } | ||
221 | 256 | | |||
222 | /** | 257 | /** | ||
Context not available. | |||||
226 | * @param noteType: new note type. | 261 | * @param noteType: new note type. | ||
227 | */ | 262 | */ | ||
228 | function replaceNote(noteName, noteType) { | 263 | function replaceNote(noteName, noteType) { | ||
229 | if(noteToReplace != -1) { | 264 | if(selectedIndex != -1) { | ||
230 | addNote(noteName, noteType, false, true, true) | 265 | var tempModel = createNotesBackup() | ||
266 | tempModel[selectedIndex]= { "noteName_": noteName, "noteType_": noteType } | ||||
267 | redrawNotes(tempModel) | ||||
231 | } | 268 | } | ||
232 | noteToReplace = -1 | 269 | selectedIndex = -1 | ||
233 | } | 270 | } | ||
234 | 271 | | |||
235 | /** | 272 | /** | ||
Context not available. | |||||
249 | else | 286 | else | ||
250 | restName = "eighth" | 287 | restName = "eighth" | ||
251 | 288 | | |||
252 | var oldNoteDetails = notesModel.get(noteIndex) | | |||
253 | pushToUndoStack(noteIndex, oldNoteDetails.noteName_, oldNoteDetails.noteType_) | | |||
254 | notesModel.set(noteIndex, { "noteName_": restName, "noteType_": "Rest" }) | 289 | notesModel.set(noteIndex, { "noteName_": restName, "noteType_": "Rest" }) | ||
290 | var tempModel = createNotesBackup() | ||||
291 | redrawNotes(tempModel) | ||||
255 | } | 292 | } | ||
256 | 293 | | |||
257 | /** | 294 | /** | ||
Context not available. | |||||
259 | */ | 296 | */ | ||
260 | function eraseAllNotes() { | 297 | function eraseAllNotes() { | ||
261 | notesModel.clear() | 298 | notesModel.clear() | ||
262 | noteToReplace = -1 | 299 | selectedIndex = -1 | ||
300 | multipleStaff.insertingIndex = 0 | ||||
301 | multipleStaff.currentEnteringStaff = 0 | ||||
263 | } | 302 | } | ||
264 | 303 | | |||
265 | /** | 304 | /** | ||
266 | * Undo the change made to the last note. | 305 | * Undo the change made to the last note. | ||
267 | */ | 306 | */ | ||
268 | function undoChange(undoNoteDetails) { | 307 | function undoChange(undoNoteDetails) { | ||
269 | if(undoNoteDetails.oldNoteName_ === "none") | 308 | if(undoNoteDetails.oldNoteName_ === "none") { | ||
309 | if((undoNoteDetails.noteIndex_ === (notesModel.count - 1)) && notesModel.get(notesModel.count - 1).isFirstNote_ && (multipleStaff.currentEnteringStaff != 0)) | ||||
310 | multipleStaff.currentEnteringStaff-- | ||||
270 | notesModel.remove(undoNoteDetails.noteIndex_) | 311 | notesModel.remove(undoNoteDetails.noteIndex_) | ||
312 | | ||||
313 | var tempModel = createNotesBackup() | ||||
314 | redrawNotes(tempModel) | ||||
315 | } | ||||
271 | else { | 316 | else { | ||
272 | noteToReplace = undoNoteDetails.noteIndex_ | 317 | selectedIndex = undoNoteDetails.noteIndex_ | ||
273 | replaceNote(undoNoteDetails.oldNoteName_, undoNoteDetails.oldNoteType_) | 318 | replaceNote(undoNoteDetails.oldNoteName_, undoNoteDetails.oldNoteType_) | ||
274 | } | 319 | } | ||
275 | noteToReplace = -1 | 320 | selectedIndex = -1 | ||
276 | } | 321 | } | ||
277 | 322 | | |||
278 | /** | 323 | /** | ||
Context not available. | |||||
354 | noteName += melody[i][1] | 399 | noteName += melody[i][1] | ||
355 | } | 400 | } | ||
356 | 401 | | |||
357 | addNote(noteName, noteType, false, false, false) | 402 | addNote(noteName, noteType, false, false) | ||
358 | } | 403 | } | ||
359 | } | 404 | } | ||
360 | 405 | | |||
Context not available. | |||||
380 | function play() { | 425 | function play() { | ||
381 | musicTimer.currentNote = 0 | 426 | musicTimer.currentNote = 0 | ||
382 | musicTimer.interval = 500 | 427 | musicTimer.interval = 500 | ||
428 | /* | ||||
383 | for(var v = 1 ; v < currentStaff ; ++ v) | 429 | for(var v = 1 ; v < currentStaff ; ++ v) | ||
384 | staves.itemAt(v).showMetronome = false | 430 | staves.itemAt(v).showMetronome = false | ||
385 | // Only display metronome if we want to | 431 | // Only display metronome if we want to | ||
386 | staves.itemAt(0).showMetronome = isMetronomeDisplayed | 432 | staves.itemAt(0).showMetronome = isMetronomeDisplayed | ||
433 | **/ | ||||
387 | 434 | | |||
388 | musicTimer.start() | 435 | musicTimer.start() | ||
389 | } | 436 | } | ||
Context not available. |