//==============================================
// MuseScore
//
// Quena Notation plugin for MuseScore Ver. 4.4 and later Ver4.6 (2026.4.2)
//
// Copyright (C)2012-2026 Hiroshi Tachibana
//
//
// 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.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import MuseScore 3.0
MuseScore {
menuPath: "Plugins.Quena Fingerings"
version: "2026.4.2"
description: "Quena fingerings, Note names"
pluginType: "dialog"
id: window
width:220 // menu window size
height:400
// グローバル変数
property var quenaLength
property var fontNum
property var fontSizeF // 運指のフォントサイズ
property var fontSizeT // 文字のフォントサイズ
property var yPosition
property var xPosition
property var partNum
property var colorNum
property var fontName: [ "QuenaBoliviav2","Doremi","Doremib","Helvetica" ]
property var iOct
property var normReal
ColumnLayout {
RowLayout { //====================== Length
Label { text: " Quena type" }
ComboBox {
id: quenaLengthList
currentIndex: 12
model: ListModel {
ListElement { text: "High G5" }
ListElement { text: "High F#5" }
ListElement { text: "High F5" }
ListElement { text: "High E5" }
ListElement { text: "High D#5" }
ListElement { text: "High D5" }
ListElement { text: "C#5" }
ListElement { text: "C5" }
ListElement { text: "B4" }
ListElement { text: "A#4" }
ListElement { text: "A4" }
ListElement { text: "G#4" }
ListElement { text: "G4 =====" }
ListElement { text: "F#4" }
ListElement { text: "F4" }
ListElement { text: "E4" }
ListElement { text: "D#4" }
ListElement { text: "D4" }
ListElement { text: "Low C#4" }
ListElement { text: "Low C4" }
ListElement { text: "Low B3" }
ListElement { text: "Low A#3" }
ListElement { text: "Low A3" }
ListElement { text: "Low G#3" }
ListElement { text: "Low G3" }
}
} // end ComboBox
}
RowLayout { //====================== Font type, Fingering or Note name
Label { text: " Display" }
ComboBox {
id: fontList
currentIndex: 0 // 初期設定を変更できます。
model: ListModel {
ListElement { text: "DoReMi+Fingering" }
ListElement { text: "CDE+Fingering" }
ListElement { text: "Fingering" }
ListElement { text: "DoReMi" }
ListElement { text: "CDEFG" }
}
} // end ComboBox
}
RowLayout { //====================== 音程 通常/実音程 G管の筒音G3とするのが通常、G4とするのが実音程
Label { text: " 通常/実音" }
ComboBox {
id: normReal
currentIndex: 0 // 初期設定を変更できます。
model: ListModel {
ListElement { text: " 通常" }
ListElement { text: " 実音程" }
}
} // end ComboBox
}
RowLayout { //====================== FONT SIZE運指
Label { text: " Fingering Font Size" }
SpinBox {
id: valFingFontSize
implicitWidth: 55
from: 12
to: 128
stepSize: 1
value: 42 // 初期設定を変更できます。
}
}
RowLayout { //====================== FONT SIZE文字
Label { text: " Text Font Size " }
SpinBox {
id: valTextFontSize
implicitWidth: 55
from: 4
to: 48
stepSize: 1
value: 14 // 初期設定を変更できます。
}
}
RowLayout { //====================== Y POSITION
Label { text: " Y position +↓" }
SpinBox {
id: valYPosition
implicitWidth: 60
from: -20
to: 30
value: 0 // 初期設定を変更できます。
stepSize: 1
}
}
RowLayout { //====================== X POSITION
Label { text: " X position +→" }
SpinBox {
id: valXPosition
implicitWidth: 60
from: -5
to: 5
value: 0 // 初期設定を変更できます。
stepSize: 1
}
}
RowLayout { //====================== PART
Label { text: " Part" }
ComboBox {
id: partList
currentIndex: 0 // 初期設定を変更できます。
model: ListModel {
ListElement { text: "Part 1" }
ListElement { text: "Part 2" }
ListElement { text: "Part 3" }
ListElement { text: "Part 4" }
}
}
}
RowLayout { //====================== COLOR
Label { text: " Color" }
ComboBox {
id: colorList
currentIndex: 0 // 初期設定を変更できます。
model: ListModel {
ListElement { text: "Black" }
ListElement { text: "Red" }
ListElement { text: "Blue" }
ListElement { text: "Green" }
ListElement { text: "Purple" }
ListElement { text: "Gray" }
}
}
}
RowLayout { //============ Ver. No. ================
Label {
font.pointSize: 8
text: " V"+version+" tcbnhrs
運指監修:柳内淳一"
}
}
Button {
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: 80
text: "Cancel"
onClicked: { quit() }
}
Button {
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: 80
text: "OK"
onClicked: {
quit()
main()
curScore.startCmd()
curScore.endCmd()
}
highlighted: true
}
} // end of ColumnLayout
function main() {
curScore.startCmd()
quenaLength=quenaLengthList.currentIndex-12
fontNum = fontList.currentIndex
fontSizeF = valFingFontSize.value
fontSizeT = valTextFontSize.value
yPosition = valYPosition.value
xPosition = valXPosition.value
partNum = partList.currentIndex
colorNum = colorList.currentIndex
if(normReal.currentIndex==0) iOct=24
if(normReal.currentIndex==1) iOct=12
applyToSelection()
curScore.endCmd()
}
onRun: {
if (typeof curScore === 'undefined') quit();
} // end onRun
//***************************************************************************
function applyToSelection() {
curScore.startCmd()
var cursor = curScore.newCursor();
var startStaff;
var endStaff;
var endTick;
var fullScore = false;
var xPos = xPosition
var yPosF = yPosition+14
var yPosT = yPosition+6
var fingerings = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", // G4=67から
"Q", "R", "S", "T", "U", "V", "W", "X","Y", "Z", "a", "b", "c", "d", //
"e", "f", "g", "h", "i", "j", "k"] // 1-37 // 3オクターブのG ソまで, "l", "m", "n", "o", "p", "q", "r"] // 1- 44 (0-43) length=44
var noteNameS = [ "C","C#","D","D#","E","F","F#","G","G#","A","A#","B" ]
var noteNameF = [ "C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B" ]
// RGB color Black , Red , Blue , Green , Purple , Gray
var colorData= [ "#000000" ,"#FF0000" ,"#0000FF" ,"#00D000" ,"#C007C0" ,"#888888" ];
var fontColor=colorData[colorNum];
var fontFaceTag ="";
var fontFaceTagS="";
var fontFaceTagF="";
var fontFaceTagE="";
var selPart=0;
cursor.rewind(1); // 選択範囲の最初に移動
if (!cursor.segment) { // 選択がない場合
fullScore = true;
startStaff = 0; // 最初
endStaff = curScore.nstaves - 1; // 終わり
} else {
startStaff = cursor.staffIdx;
cursor.rewind(2); // 選択範囲の最後
if (cursor.tick == 0) {
endTick = curScore.lastSegment.tick + 1;
} else {
endTick = cursor.tick;
}
endStaff = cursor.staffIdx;
}
for (var staff = startStaff; staff <= endStaff; staff++) { // 対象となった音符の最初から最後まで
// for (var voice = 0; voice < 4; voice++) {
// var voice=partNum; // 声部の指定
cursor.rewind(1); // beginning of selection
cursor.voice = partNum; // 声部の指定
cursor.staffIdx = staff;
if (fullScore) cursor.rewind(0); // 選択範囲がない場合は先頭から
while (cursor.segment && (fullScore || cursor.tick < endTick)) {
if (cursor.element && cursor.element.type == Element.CHORD) {
var text = newElement(Element.STAFF_TEXT); // 指使い表示用
var text2 = newElement(Element.STAFF_TEXT); // ドレミ、CDE用
text.placement = Placement.BELOW
text.autoplace = false; // add 2026.3.29
text2.placement = Placement.BELOW // add 2026.3.29
text2.autoplace = false; // add 2026.3.29
// ######################## GRACE #####################################
//####################################################################
var notes = cursor.element.notes;
var pitchFing;
var fontSizeTag;
for (var i = 0; i < notes.length; i++) { // 和音の場合のループ
// for (var i = 0; i < 1;i++) { // 和音の場合は、一番下の音のみ
if(notes[i].tieBack == null ) { // タイの後ろ側の音譜でない場合
if(fontNum==2) { // 指使いのみの表示-------------------------------------
pitchFing=notes[i].pitch+quenaLength -67-12+iOct; // 67=G4 , 12=G管
if(pitchFing<0 || pitchFing>=fingerings.length) {
// text.text=" "+text.text; // 演奏可能な音域外の場合
} else{
text.text = fingerings[pitchFing]+text.text ;
text.color = fontColor;
text.offsetX = xPos;
text.offsetY = yPosF-3; // 指使いのみの表示を少し上へ
fontSizeTag="";
text.fontSize=fontSizeF
text.text = fontSizeTag + fontFaceTag + text.text; // 指使い
}
text.placement = Placement.BELOW; // added 2023.6.25
text.autoplace = false; // added 2023.6.25
text2.placement = Placement.BELOW; // added 2023.6.25
text2.autoplace = false; // added 2023.6.25
if(i == notes.length-1) cursor.add(text); // if文としないと、MuseScoreがクラッシュする。
} else if(fontNum==1) { // 指使いとCDE ---------------------------------
pitchFing=notes[i].pitch+quenaLength -67-12+iOct;
if(pitchFing<0 || pitchFing>=fingerings.length) { // 演奏可能な音域外の場合は半角スペース
} else{ // 演奏可能な音内の場合
// 運指
text.text = fingerings[pitchFing]+text.text ; // 運指の文字コードを与える
text.color = fontColor; // 運指の色を与える
text.offsetX = xPos; // 運指のX方向位置を与える
text.offsetY = yPosF; // 運指のY方向位置を与える
fontSizeTag=""; // 運指図の場合、文字サイズを拡大
text.fontSize=fontSizeF
text.text = fontSizeTag + fontFaceTag + text.text; // 文字サイズと運指用フォント名を与える
// CDE
if(notes[i].tpc>=6 && notes[i].tpc<=12 ) { // プロパティーtpcで判定、 flat ♭の場合
text2.text=noteNameF[(notes[i].pitch % 12)] +text2.text; // ♭場合の英語音名を与える
} else if ( notes[i].tpc>=20 && notes[i].tpc<=26 ){ // sharp ♯の場合
text2.text=noteNameS[(notes[i].pitch % 12)] +text2.text ; // ♯場合の英語音名を与える
} else{ // ♯♭でない場合は、♯♭どちらのフォントでもよい
text2.text=noteNameF[(notes[i].pitch % 12)] +text2.text;
}
text2.color= fontColor; // 英語音名の色を与える
text2.offsetX= xPos; // 英語音名のX方向位置を与える
text2.offsetY = yPosT; // 英語音名のY方向位置を与える
fontSizeTag="";
text2.fontSize=fontSizeT
text2.text= fontSizeTag + fontFaceTagE + text2.text; // 文字サイズと英語音名用フォント名を与える
}
text.placement = Placement.BELOW; // added 2023.6.25
text.autoplace = false; // added 2023.6.25
text2.placement = Placement.BELOW; // added 2023.6.25
text2.autoplace = false; // added 2023.6.25
if(i == notes.length-1) cursor.add(text); // 運指を表示する
if(i == notes.length-1) cursor.add(text2); // CDEを表示する
} else if(fontNum==0) { // 指使いとドレミDoReMi---------------------------
pitchFing=notes[i].pitch+quenaLength -67-12+iOct;
if(pitchFing<0 || pitchFing>=fingerings.length) { // 演奏可能な音域外の場合
} else{ // 演奏可能な音内の場合
// 運指
text.text = fingerings[pitchFing]+text.text ;
text.color = fontColor;
text.offsetX = xPos;
text.offsetY = yPosF;
text.fontSize=fontSizeF
fontSizeTag="";
text.text = fontSizeTag +fontFaceTag+ text.text;
// ドレミ
// pitchFing=(notes[i].pitch +quenaLength) % 12 ; //移動ドに対応する場合
pitchFing=(notes[i].pitch ) % 12 ;
text2.text=fingerings[pitchFing]+text2.text ;
fontSizeTag="";
if(notes[i].tpc>=6 && notes[i].tpc<=12 ) { // Flat ドレミ♭
text2.text= fontSizeTag + fontFaceTagF + text2.text;
} else if ( notes[i].tpc>=20 && notes[i].tpc<=26 ){ // Sharp ドレミ♯
text2.text= fontSizeTag + fontFaceTagS + text2.text;
} else {
text2.text= fontSizeTag + fontFaceTagS + text2.text;
text2.text= fontSizeTag + fontFaceTagS + text2.text;
}
text2.color = fontColor;
text2.offsetX = xPos;
text2.offsetY = yPosT
text2.fontSize=fontSizeT
}
text.placement = Placement.BELOW; // added 2023.6.25
text.autoplace = false; // added 2023.6.25
text2.placement = Placement.BELOW; // added 2023.6.25
text2.autoplace = false; // added 2023.6.25
if(i == notes.length-1) cursor.add(text);
if(i == notes.length-1) cursor.add(text2);
} else if(fontNum==4) { // CDEのみ----------------------------------
if ( i > 0 ) text2.text = "\n"+ text2.text; // 和音の場合は、順次改行して縦に表示する。
if(notes[i].tpc>=6 && notes[i].tpc<=12 ) { // flat ♭の場合
text2.text=noteNameF[(notes[i].pitch % 12)] +text2.text; // ♭場合の英語音名を与える
} else if ( notes[i].tpc>=20 && notes[i].tpc<=26 ){ // sharp ♯の場合
text2.text=noteNameS[(notes[i].pitch % 12)] +text2.text ;
} else{
text2.text=noteNameF[(notes[i].pitch % 12)] +text2.text ;
}
text2.color= fontColor;
text2.offsetX = xPos;
text2.offsetY = yPosT;
text2.fontSize=fontSizeT
fontSizeTag="";
text2.text= fontSizeTag + fontFaceTagE + text2.text;
text.placement = Placement.BELOW; // added 2023.6.25
text.autoplace = false; // added 2023.6.25
text2.placement = Placement.BELOW; // added 2023.6.25
text2.autoplace = false; // added 2023.6.25
if(i == notes.length-1) cursor.add(text2);
} else if(fontNum==3) { // DoReMiのみ-------------------------------
if ( i > 0 ) text2.text = "\n"+ text2.text; // 和音の場合は、順次改行して縦に表示する。
// pitchFing=(notes[i].pitch +quenaLength) % 12 ;
pitchFing=(notes[i].pitch) % 12 ;
text2.text=fingerings[pitchFing]+text2.text ;
fontSizeTag=""; // 英語音名より少し小さめとする。
if(notes[i].tpc>=6 && notes[i].tpc<=12 ) { // Flat ドレミ♭
text2.text= fontSizeTag + fontFaceTagF + text2.text;
} else if ( notes[i].tpc>=20 && notes[i].tpc<=26 ) { // Sharp ドレミ♯
text2.text= fontSizeTag + fontFaceTagS + text2.text;
} else {
text2.text= fontSizeTag + fontFaceTagS + text2.text;
}
text2.color = fontColor;
text2.offsetX = xPos;
text2.offsetY = yPosT;
text2.fontSize=fontSizeT
text.placement = Placement.BELOW; // added 2023.6.25
text.autoplace = false; // added 2023.6.25
text2.placement = Placement.BELOW; // added 2023.6.25
text2.autoplace = false; // added 2023.6.25
if(i == notes.length-1) cursor.add(text2);
}
} // end != Tie
/*
switch (voice) {
case 0: text.pos.y = yPos; break;
case 1: text.pos.y = yPos+2; break;
case 2: text.pos.y = yPos+4; break;
case 3: text.pos.y = yPos+6; break;
}
*/
} // end for note i 和音
} // end if CHORD
cursor.next(); // 次の音符へ移動する
} // end while segment
} // end for staff
curScore.endCmd()
quit();
} // end apply
} // end MuseScore