//============================================================================= // AddNoteNameNoteHeads v. 1.2 // // Copyright (C) 2015-2016 Jon Ensminger // // Parts of code based on shape_notes plugin by Nicolas Froment Copyright (C) // 2015 // Changes notehead to alphabetic or sol-fa named noteheads // // v.1.2 fixes a bug in detecting note durations; the durations are now // detected as fractions // // 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. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //============================================================================= import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.3 import MuseScore 1.0 MuseScore { version: "1.0" description: qsTr("Replaces noteheads with notename noteheads - StaffText font: Bravura size: 19") menuPath: "Plugins.Notes." + qsTr("Add Named Noteheads") property var keySig : 0 property var majorKey : true property var keySigs : [] property var wholeIndex : 0 property var halfIndex : 1 property var quarterIndex : 2 property var useAlpha : true property var useSolfa : false property var moveableDo : false property var useTi : true property var code : 0 property var nhText : "" property var noteHeadColor : "#000000" property var bgColor : "#ffffff" property var nhType : 0 property var bgType : 1 property var noteHead property var yOffset : 2.1 property var userWholeXOffset : 0.0 property var onStaffVisible : false property var offStaffVisible : true //Whole / Half / Quarter -- no ##s or bbs -- F C G D A E B property var alphaCodesWhole : [0xE177,0xE16E,0xE17A,0xE171,0xE168,0xE174,0xE16B, 0xE178,0xE16F,0xE17B,0xE172,0xE169,0xE175,0xE16C, 0xE179,0xE16F,0xE17C,0xE173,0xE170,0xE176,0xE16D] property var alphaCodesHalf : [0xE18E,0xE185,0xE191,0xE188,0xE17F,0xE18B,0xE182, 0xE18F,0xE186,0xE192,0xE189,0xE180,0xE18C,0xE183, 0xE190,0xE187,0xE193,0xE18A,0xE181,0xE18D,0xE184] property var alphaCodesQuarter : [0xE1A5,0xE19C,0xE1A8,0xE19F,0xE196,0xE1A2,0xE199, 0xE1A6,0xE19D,0xE1A9,0xE1A0,0xE197,0xE1A3,0xE19A, 0xE1A7,0xE19E,0xE1AA,0xE1A1,0xE198,0xE1A4,0xE19B] // no #s or bs for solfa noteheads property var solfaCodesWhole : [0xE153,0xE150,0xE154,0xE151,0xE155,0xE152,0xE156, 0xE153,0xE150,0xE154,0xE151,0xE155,0xE152,0xE156, 0xE153,0xE150,0xE154,0xE151,0xE155,0xE152,0xE156] property var solfaCodesHalf : [0xE15B,0xE158,0xE15C,0xE159,0xE15D,0xE15A,0xE15E, 0xE15B,0xE158,0xE15C,0xE159,0xE15D,0xE15A,0xE15E, 0xE15B,0xE158,0xE15C,0xE159,0xE15D,0xE15A,0xE15E] property var solfaCodesQuarter : [0xE163,0xE160,0xE164,0xE161,0xE165,0xE162,0xE166, 0xE163,0xE160,0xE164,0xE161,0xE165,0xE162,0xE166, 0xE163,0xE160,0xE164,0xE161,0xE165,0xE162,0xE166] property var doOffsets : [0,4,1,5,2,6,3,0,4,1,5,2,6,3,0] // Apply the given function to all notes in selection // or, if nothing is selected, in the entire score function applyToNotesInSelection(func) { var cursor = curScore.newCursor(); cursor.rewind(1); var startStaff; var endStaff; var endTick; var fullScore = false; if (!cursor.segment) { // no selection fullScore = true; startStaff = 0; // start with 1st staff endStaff = curScore.nstaves - 1; // and end with last } else { startStaff = cursor.staffIdx; cursor.rewind(2); if (cursor.tick == 0) { // this happens when the selection includes // the last measure of the score. // rewind(2) goes behind the last segment (where // there's none) and sets tick=0 endTick = curScore.lastSegment.tick + 1; } else { endTick = cursor.tick; } endStaff = cursor.staffIdx; } console.log(startStaff + " - " + endStaff + " - " + endTick) for (var staff = startStaff; staff <= endStaff; staff++) { for (var voice = 0; voice < 4; voice++) { cursor.rewind(1); // sets voice to 0 cursor.voice = voice; //voice has to be set after goTo cursor.staffIdx = staff; if (fullScore) cursor.rewind(0) // if no selection, beginning of score while (cursor.segment && (fullScore || cursor.tick < endTick)) { keySig = cursor.keySignature; if (cursor.element && cursor.element.type == Element.CHORD) { var chord = cursor.element; var notes = chord.notes; for (var i = 0; i < notes.length; i++) { var note = notes[i]; if (note) func(note, cursor, chord.durationType); } } cursor.next(); } } } } function createNamedNotehead(note, cursor, durationType) { if (note.type == Element.NOTE) { var durIndex; var code = ""; var codeIndex = note.tpc - 6; var wholeXOffset = 0; if (codeIndex < 0) { // if bb codeIndex += 14; // use natural } else if (note.tpc > 26) { // if ## codeIndex -= 14; // use natural } if (durationType >= 1920) { durIndex = wholeIndex; wholeXOffset = userWholeXOffset; } else if (durationType >= 960) { durIndex = halfIndex; wholeXOffset = 0; } else { durIndex = quarterIndex; wholeXOffset = 0; } if (useAlpha) { switch (durIndex) { case wholeIndex: code = alphaCodesWhole[codeIndex]; break; case halfIndex: code = alphaCodesHalf[codeIndex]; break; case quarterIndex: code = alphaCodesQuarter[codeIndex]; break; default: break; } } else if (useSolfa) { if (moveableDo) { if (majorKey) codeIndex = note.tpc - 6 - keySig; else codeIndex = note.tpc - 6 - (keySig + 3); } if (codeIndex < 0) { codeIndex += 14; } else if (codeIndex > 26) { codeIndex -= 14; } switch (durIndex) { case wholeIndex: code = solfaCodesWhole[codeIndex]; if (!useTi && codeIndex % 7 == 6) { code = 0xE157; } break; case halfIndex: code = solfaCodesHalf[codeIndex]; if (!useTi && codeIndex % 7 == 6) { code = 0xE15F; } break; case quarterIndex: code = solfaCodesQuarter[codeIndex]; if (!useTi && codeIndex % 7 == 6) { code = 0xE167; } break; default: break; } } nhText = String.fromCharCode(code); noteHead = newElement(Element.STAFF_TEXT); noteHead.text = ""+nhText; noteHead.userOff = Qt.point(0,0); noteHead.pos = Qt.point(note.pos.x + wholeXOffset, note.pos.y + yOffset); noteHead.color = noteHeadColor; if (note.line < 10 && note.line > -2) { note.visible = onStaffVisible; } else { note.visible = offStaffVisible; } note.color = bgColor; cursor.add(noteHead); } } ExclusiveGroup { id : exclusiveGroup } ExclusiveGroup { id : tiExclusiveGroup } ExclusiveGroup { id : modeExclusiveGroup } Dialog { id : selectDialog title : "Notehead Options" width: 400 GridLayout { id : grid columns : 3 Text { id : nhOptionsText text : qsTr("NoteHead Options") Layout.columnSpan : 3 } CheckBox { id : alphaCheckBox checked : true exclusiveGroup : exclusiveGroup text : qsTr("Alpha Noteheads") onClicked : { moveDoCheckBox.checked = false; majorCheckBox.checked = false; minorCheckBox.checked = false; tiCheckBox.checked = false; siCheckBox.checked = false; } } Text { id : spText2 text : "" Layout.columnSpan : 2 } CheckBox { id : solfaCheckBox checked : false exclusiveGroup : exclusiveGroup text : qsTr("Sol-Fa Noteheads") Layout.columnSpan : 3 onClicked : { moveDoCheckBox.checked = false; majorCheckBox.checked = true; minorCheckBox.checked = false; tiCheckBox.checked = true; siCheckBox.checked = false; } } CheckBox { id : moveDoCheckBox checked : false text : qsTr("Movable Do") } CheckBox { id : majorCheckBox checked : false exclusiveGroup : modeExclusiveGroup text : qsTr("Major Key") } CheckBox { id : minorCheckBox checked : false exclusiveGroup : modeExclusiveGroup text : qsTr("Minor Key") } CheckBox { id : tiCheckBox checked : false exclusiveGroup : tiExclusiveGroup text : qsTr("Ti") } CheckBox { id : siCheckBox checked : false exclusiveGroup : tiExclusiveGroup text : qsTr("Si") Layout.columnSpan : 2 } Text { id : colOptionsText text : qsTr("Color Options") Layout.columnSpan : 3 } Text { id : nhColorText text : qsTr("Notehead Color") } Rectangle { id : nhColorRect; width : 80 height : 50 color : noteHeadColor Layout.columnSpan : 2 MouseArea { anchors.fill: parent onClicked: { nhColorDialog.open(); } } } Text { id : bgColorText text : qsTr("Note Background Color") } Rectangle { id : bgColorRect; width : 80 height : 50 color : bgColor Layout.columnSpan : 2 MouseArea { anchors.fill: parent onClicked: { bgColorDialog.open(); } } } Text { id : layoutOptionsText Layout.columnSpan : 3 text : qsTr("Layout Options") } Text { id : yOffsetText text : qsTr("Vertical Offset") } SpinBox { id : yOffsetSpinBox decimals : 2 maximumValue : 6.00 minimumValue : -6.00 stepSize: 0.02 value : yOffset Layout.columnSpan : 2 style: SpinBoxStyle{ background: Rectangle { implicitWidth: 80 implicitHeight: 25 border.color: "gray" color : "#eeeeee" radius: 2 } } onEditingFinished : { yOffset = value; console.log("yOffset " + yOffset);} } Text { id : wnOffsetText text : qsTr("Whole Note X Offset") } SpinBox { id : wnOffsetSpinBox decimals : 2 maximumValue : 3.00 minimumValue : -3.00 stepSize: 0.02 value : userWholeXOffset Layout.columnSpan : 2 style: SpinBoxStyle{ background: Rectangle { implicitWidth: 80 implicitHeight: 25 border.color: "gray" color : "#eeeeee" radius: 2 } } onEditingFinished : { userWholeXOffset = value;console.log("wXOffset " + userWholeXOffset);} } Text { id : hiddenOptionsText text : qsTr("Background Note Hide Options") Layout.columnSpan : 3 } CheckBox { id : onHideCheckBox checked : true text : qsTr("Hide On Staff") onClicked : { setHidden() } } CheckBox { id : offHideCheckBox checked : false text : qsTr("Hide Off Staff") onClicked : { setHidden() } } } standardButtons: StandardButton.Cancel | StandardButton.Ok onAccepted: { applyFunction(); } onRejected : Qt.quit() } Dialog { id : readmeDialog title : qsTr("Instructions") width : 470 Text { id : readmeText text : qsTr("Description: Places a named Notehead on existing notes in a selection,
or the whole score if no selection.

User Options:
  • Alpha- or Solfa- Notehead type
  • Major or Minor key (for Solfa)
  • Ti or Si (for Solfa)
  • Fixed or Movable Do (for Solfa)
  • Notehead color
  • Background note color
  • Notehead vertical offset (for fine tuning)
  • Whole note horizontal offset (for fine tuning)
  • Hide/unhide background notes on staff
  • Hide/unhide background notes off staff (leger lines)

Use Edit\/Undo or Select/All similar elements in MuseScore to
remove named Noteheads.

Some noteheads might require manual repositioning after the plugin
is run. If all noteheads are slightly misaligned vertically, try removing
all noteheads (Edit/Undo or Select/All similar elements/Cut), adjust
the Notehead vertical offset in the plugin's Options dialog, then run
the plugin again. Lower numeric offset values move the noteheads
up, and higher numeric values move the noteheads down.

Please Note: Named Noteheads are Staff Text elements, and will not
change position or name if the underlying note pitches change.
To change note names/positions, use the Undo command and
re-run the plugin after making the changes to the underlying notes.") } standardButtons: StandardButton.Cancel | StandardButton.Ok onAccepted : selectDialog.open(); onRejected : Qt.quit(); } ColorDialog { id: nhColorDialog title: qsTr("Notehead Color") onAccepted : changeColor(nhType) onRejected: Qt.quit(); } ColorDialog { id: bgColorDialog title: qsTr("Note Background Color") onAccepted : changeColor(bgType) onRejected: Qt.quit(); } function changeColor(type) { if (type == nhType) { nhColorRect.color = nhColorDialog.color; noteHeadColor = nhColorDialog.color; } if (type == bgType) { bgColorRect.color = bgColorDialog.color; bgColor = bgColorDialog.color; } } function setHidden() { onStaffVisible = !onHideCheckBox.checked; offStaffVisible = !offHideCheckBox.checked; } function applyFunction() { yOffset = yOffsetSpinBox.value; userWholeXOffset = wnOffsetSpinBox.value; if (majorCheckBox.checked) { majorKey = true; console.log("majorChecked " + majorCheckBox.checked + " majorKey " + majorKey); } else if (minorCheckBox.checked) { majorKey = false; console.log("minorChecked " + minorCheckBox.checked + " majorKey " + majorKey); } if (alphaCheckBox.checked) { useAlpha = true; useSolfa = false; tiCheckBox.checked = false; siCheckBox.checked = false; } else { useSolfa = true; useAlpha = false; if (moveDoCheckBox.checked){ moveableDo = true; } else { moveableDo = false; } if (tiCheckBox.checked){ useTi = true; } else { useTi = false; } } curScore.startCmd(); applyToNotesInSelection(createNamedNotehead); curScore.endCmd(); Qt.quit(); } onRun: { if (typeof curScore === 'undefined') Qt.quit(); readmeDialog.open(); } }