//==============================================
// MuseScore
//
// Koto Notation plugin for MuseScore 3 Ver. 1.0
//
// Copyright (C)201?-2019 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.9
import QtQuick.Controls 1.5
import QtQuick.Layouts 1.3
import MuseScore 3.0
///import FileIO 1.0
MuseScore {
menuPath: "Plugins.Koto Notation" //
version: "3.0"
description: qsTr("Koto (13, 17 ,20 or 25 strings) notation")
pluginType: "dialog"
id: window
width:560 // menu window size
height:285
ExclusiveGroup { id: exclusiveGroupKey }
property var seplines
// property var tuning : [25]
// property var g1,g2,g3,g4,g5,g6,g7,g8,g9,g10,g11,g12,g13,g14,g15,g16,g17,g18,g19,g20,g21,g22,g23,g24,g25
/*/
property var g1
property var g2
property var g3
property var g4
property var g5
property var g6
property var g7
property var g8
property var g9
property var g10
property var g11
property var g12
property var g13
property var g14
property var g15
property var g16
property var g17
property var g18
property var g19
property var g20
property var g21
property var g22
property var g23
property var g24
property var g25
/*/
property var nString : 13
property var fileExist :true
property var selKotoInit : 0
property var tuningInitText : "0,2,4,5,7,9,11,12,14,16,17,19,21"
// property var tuningInitText : "0,2,4,5,7,9,11,12,14,16,17,19,21,23,24,26,28,29,31,33,35,36,38,40,41"
// property var tuningInit : 0
property var tuningBaseInit: 60
property var fontSizeInit : 12
property var yPositionInit : 0
property var xPositionInit : 0
property var partListInit : 0
property var colorListInit : 0
property var fontName: [ "Koto13Ta","Koto17Ta","Koto20T","Koto25T","Koto25LT","Doremi","Doremib","Helvetica","Helvetica"]
property var fontListInitName : "Koto13T"
// Item positions in menu window
property var itemX1 : 10
property var itemX2 : 150
property var itemY1 : 10
property var itemDY : 25
RowLayout { //====================== select KOTO
id: row0
x :itemX1
y :itemY1
Label {
font.pointSize: 16
text: "Select Koto type:"
}
}
RowLayout {
id: row0R
x :itemX2
y :itemY1-3
ComboBox {
currentIndex: selKotoInit
model: ListModel {
id: selKoto
property var key
property var key2
ListElement { text: "13 Strings"; kName: 0 ; sNum:13 }
ListElement { text: "17 Strings"; kName: 1 ; sNum:17 }
ListElement { text: "20 Strings"; kName: 2 ; sNum:21 }
ListElement { text: "25 Strings H"; kName: 3 ; sNum:25 }
ListElement { text: "25 Strings L"; kName: 4 ; sNum:25 }
ListElement { text: "Do,Re,Mi #(katakana)"; kName: 5 ; sNum:0 }
ListElement { text: "Do Re Mi b(katakana)"; kName: 6 ; sNum:0 }
ListElement { text: "MIDI note number"; kName: 7 ; sNum:0 }
ListElement { text: "d MIDI note num."; kName: 8 ; sNum:0 }
}
width: 60
onCurrentIndexChanged: {
console.debug(selKoto.get(currentIndex).text + ", " + selKoto.get(currentIndex).kName)
console.debug(selKoto.get(currentIndex).text + ", " + selKoto.get(currentIndex).sNum)
selKoto.key = selKoto.get(currentIndex).kName
selKoto.key2 = selKoto.get(currentIndex).sNum
}
} // end ComboBox
}
RowLayout { //====================== TUNING
id: row1
x :itemX1
y :itemY1+itemDY*1
Label {
font.pointSize: 14
text: "Tuning"
}
}
RowLayout {
id: row1R
x :itemX1+50
y :itemY1+itemDY*1
TextField {
id : tuning
cursorPosition:0
implicitWidth: 490
font.pointSize: 14
maximumLength : 80
text : tuningInitText
// text : "0,2,4,5,7,9,11,12,14,16,17,19,21" // 13 strings
// text : "0,2,4,5,7,9,11,12,14,16,17,19,21,23,24,26,28" // 17 strings
// text : "0,2,4,5,7,9,11,12,14,16,17,19,21,23,24,26,28,29,31,33,35" // 20(21) strings
// text : "0,2,4,5,7,9,11,12,14,16,17,19,21,23,24,26,28,29,31,33,35,36,38,40,41" // 25 strigs
}
}
RowLayout { //====================== Tuning BASE (MIDI note number)
id: row2
x :itemX1
y :itemY1+itemDY*2+5
Label {
font.pointSize: 14
text: "No1. String Pitch (MIDI note number)"
}
}
RowLayout {
id: row2R
x :itemX2+100
y :itemY1+itemDY*2+5
SpinBox {
id: valTuningBase
implicitWidth: 55
decimals: 0
minimumValue: 0
maximumValue: 127
value: tuningBaseInit
font.pointSize: 16
}
}
RowLayout { //====================== FONT SIZE
id: row3
x :itemX1
y :itemY1+itemDY*3+5
Label {
font.pointSize: 13
text: "Font Size"
}
}
RowLayout {
id: row3R
x :itemX2
y :itemY1+itemDY*3+5
SpinBox {
id: valFontSize
implicitWidth: 55
decimals: 0
minimumValue: 4
maximumValue: 36
value: fontSizeInit
font.pointSize: 14
}
}
RowLayout { //====================== Y POSITION
id: row4
x :itemX1
y :itemY1+itemDY*4+5
Label {
font.pointSize: 14
text: "Y (not used)使用せず"
}
}
RowLayout {
id: row4R
x :itemX2
y :itemY1+itemDY*4+5
SpinBox {
id: valYPosition
implicitWidth: 55
horizontalAlignment: Qt.AlignLeft
decimals: 0
minimumValue: -20
maximumValue: 30
value: yPositionInit
font.pointSize: 14
}
}
RowLayout { //====================== X POSITION
id: row5
x :itemX1
y :itemY1+itemDY*4+40
Label {
font.pointSize: 14
text: "X (not used)使用せず"
}
}
RowLayout {
id: row5R
x :itemX2
y :itemY1+itemDY*4+40
SpinBox {
id: valXPosition
implicitWidth: 55
decimals: 1
minimumValue: -5
maximumValue: 5
value: xPositionInit
stepSize: 0.1
font.pointSize: 14
}
}
RowLayout { //====================== PART
id: row6
x :itemX1
y :itemY1+itemDY*5+40
Label {
font.pointSize: 14
text: "Part"
}
}
RowLayout {
id: row6R
x :itemX2
y :itemY1+itemDY*5+40
ComboBox {
currentIndex: partListInit
anchors.right: parent.right
model: ListModel {
id: partList
property var key
ListElement { text: "Part 1"; pName: 0 }
ListElement { text: "Part 2"; pName: 1 }
ListElement { text: "Part 3"; pName: 2 }
ListElement { text: "Part 4"; pName: 3 }
}
width: 60
onCurrentIndexChanged: {
console.debug(partList.get(currentIndex).text + ", " + partList.get(currentIndex).pName)
partList.key = partList.get(currentIndex).pName
}
} // end ComboBox
}
RowLayout { //====================== COLOR
id: row7
x :itemX1
y :itemY1+itemDY*6+40
Label {
font.pointSize: 14
text: "Color"
}
}
RowLayout {
id: row7R
x :itemX2
y :itemY1+itemDY*6+40
ComboBox {
currentIndex: colorListInit
anchors.right: parent.right
model: ListModel {
id: colorList
property var key
ListElement { text: "Black"; cName: 0 }
ListElement { text: "Red"; cName: 1 }
ListElement { text: "Blue"; cName: 2 }
ListElement { text: "Green"; cName: 3 }
ListElement { text: "Purple"; cName: 4 }
ListElement { text: "Gray"; cName: 5 }
}
width: 60
onCurrentIndexChanged: {
console.debug(colorList.get(currentIndex).text + ", " + colorList.get(currentIndex).cName)
colorList.key = colorList.get(currentIndex).cName
}
} // end ComboBox
}
RowLayout { //====================== CANCEL / OK
id: row8
x : 110
y : 250
Button {
id: closeButton
text: "Cancel"
onClicked: { Qt.quit() }
}
Button {
id: okButton
text: "Ok"
onClicked: {
apply()
Qt.quit()
}
}
}
function apply() {
curScore.startCmd();
applyToSelection();
//console.log("in apply tuning.text="+tuning.text);
/// my_file1.write(selKoto.key+"\n"+tuning.text+"\n"+valTuningBase.value+"\n"+valFontSize.value+"\n"+valYPosition.value+"\n"+valXPosition.value+"\n"+partList.key+"\n"+colorList.key);
curScore.endCmd();
}
onRun: { // ============================= onRun
/// if( fileExist ) var fileContents = my_file1.read();
/// if( !fileExist ) var fileContents="0\n0,2,4,5,7,9,11,12,14,16,17,19,21\n60\n13\n0\n0\n0\n0";
/*/
// seplines=fileContents.split("\n"); // データを改行コードで分割し、配列に格納する
//console.log("Read from file value="+ fileContents);
//console.log("tempPath(): " + my_file1.tempPath() );
// for(var i=0;i>>>>>>>>>>>
//console.log("in applyToSelection tuning.text="+tuning.text);
var tuningText=tuning.text;
//console.log("in applyToSelection tuning.text="+tuningText);
// 位置の微調整
var yPos=valYPosition.value+10 ; // Y offset= +10 // <<<<<<<<<<<<<>>>>>>>>>>>>
var xPos=valXPosition.value; // X offset = -3 // <<<<<<<<<<<<<>>>>>>>>>>>>
if( selKoto.key==0 ) xPos=xPos-2.0; // 13 <<<<<<<<<<<<<>>>>>>>>>>>>
if( selKoto.key==1 ) xPos=xPos-2.0; // 17 <<<<<<<<<<<<<>>>>>>>>>>>>
if( selKoto.key==2 ) xPos=xPos-2.0; // 20 <<<<<<<<<<<<<>>>>>>>>>>>>
if( selKoto.key==3 || selKoto.key==4 ) xPos=xPos-2.0; // <<<<<<<<<<<<<>>>>>>>>>>>>
if( selKoto.key==5 || selKoto.key==6 ) xPos=xPos-0.7; // <<<<<<<<<<<<<>>>>>>>>>>>>
if( selKoto.key==7 ) xPos=xPos-0.7; // <<<<<<<<<<<<<>>>>>>>>>>>>
nString = selKoto.key2;
// var fontSize=fontSizeInit;
var fontSize=valFontSize.value; // <<<<<<<<<<<<<>>>>>>>>>>>>
// var selPart=partListInit;
var selPart=partList.key // <<<<<<<<<<<<<>>>>>>>>>>>>
// RGB color Black , Red , Blue , Green , Purple , Gray
var colorData= [ "#000000" ,"#FF0000" ,"#0000FF" ,"#00FF00" ,"#C007C0" ,"#888888" ];
var fontColor=colorData[colorList.key]; // <<<<<<<<<<<<<>>>>>>>>>>>>
console.log("COLOR="+fontColor);
var fontSizeTag="";
var fontFaceTag="";
console.log("fontSizeTag="+fontSizeTag);
console.log("fontFaceTag="+fontFaceTag);
cursor.rewind(1); // rewind to start of selection
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); // rewind to end of selection
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=selPart;
cursor.rewind(1); // beginning of selection
cursor.voice = voice;
cursor.staffIdx = staff;
if (fullScore) // no selection
cursor.rewind(0); // beginning of score
while (cursor.segment && (fullScore || cursor.tick < endTick)) {
if (cursor.element && cursor.element.type == Element.CHORD) {
var text1 = newElement(Element.STAFF_TEXT);
// ######################## GRACE #####################################
var graceChords = cursor.element.graceNotes;
for (var i = 0; i < graceChords.length; i++) {
var notes = graceChords[i].notes;
nameChord( notes , text1 , selKotoInit , fontName1 , tuningText );
text1.offsetX =xPos -2.3 * (graceChords.length - i); // X position of Grace note
switch (voice) {
case 0: text1.offsetY = yPos; break;
case 1: text1.offsetY = yPos+2; break;
case 2: text1.offsetY = yPos+4; break;
case 3: text1.offsetY = yPos+6; break;
}
text1.color= fontColor;
text1.text= fontSizeTag + fontFaceTag + text1.text;
cursor.add(text1);
text1 = newElement(Element.STAFF_TEXT);
} // end graceChorde
//####################################################################
var notes = cursor.element.notes;
nameChord( notes , text1 , selKotoInit , fontName1 , tuningText );
switch (voice) {
case 0: text1.offsetY = yPos; break;
case 1: text1.offsetY = yPos+2; break;
case 2: text1.offsetY = yPos+4; break;
case 3: text1.offsetY = yPos+6; break;
}
text1.color= fontColor;
text1.offsetX= xPos;
text1.text= fontSizeTag + fontFaceTag + text1.text;
//console.log(text1.text);
cursor.add(text1);
} // end if CHORD
cursor.next();
} // end while segment
// } // end for voice
} // end for staff
Qt.quit();
} // end applyToSelection
function nameChord (notes , text1 , selKotoInit , fontName1 , tuningText) {
var tuningBase=valTuningBase.value;
var tuningTmp=tuningText.split(",");
//console.log("in nameChord tuningText="+tuningText);
//console.log("tuningTmp="+tuningTmp);
//console.log("in Chord nString="+nString);
/// var tuning = [g1,g2,g3,g4,g5,g6,g7,g8,g9,g10,g11,g12,g13,g14,g15,g16,g17,g18,g19,g20,g21,g22,g23,g24,g25]
if(selKoto.key==0) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q"]// 13絃箏:A-M:一〜巾、N:弱押し、O:強押し、P:二重押し,Q:四角形,(R:ス,S:ヒ,T:ツ)
else if(selKoto.key==1) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U"]// 17絃A-Q,RSTU , VWX
else if(selKoto.key==2) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y"]// 20絃(21)A-U
else if(selKoto.key==3) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c"]// 25絃A-Y
else if(selKoto.key==4) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c"]// 低音25絃A-Y
else if(selKoto.key==5) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L"]// Do Re Mi b (Katakana)
else if(selKoto.key==6) var fingerings = ["A","B","C","D","E","F","G","H","I","J","K","L"]// Do Re Mi # (Katakana)
else var fingerings=[]
var Lenf=fingerings.length-1;
//console.log("Lenf="+Lenf);
if(selKoto.key==0) var sp=" ";
else if(selKoto.key==1) var sp=" ";
else if(selKoto.key==2) var sp=" ";
else if(selKoto.key==3) var sp=" ";
else if(selKoto.key==4) var sp=" ";
else if(selKoto.key>=5) var sp="";
var pitch1=0;
for (var iChord = 0; iChord < notes.length; iChord++) { // 和音の音数
if(selKoto.key>=5) var sep ="\n";// "\n"; // ","; // change to "\n" if you want them vertically
else var sep="";
if ( iChord > 0 ) text1.text = sep + text1.text;
if(notes[iChord].tieBack == null ){ // not TIE TIE TIE
if(selKoto.key==5 || selKoto.key==6) {
pitch1=(notes[iChord].pitch) % 12 ;
if(notes[iChord].tieBack == null ) text1.text=fingerings[pitch1]+text1.text ;
} else if(selKoto.key==7) {
text1.text= notes[iChord].pitch +text1.text ; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< MIDI NOTE Number
} else if(selKoto.key==8) {
text1.text= notes[iChord].pitch - tuningBase +text1.text ; // <<<<<<<<<<<<<<<<<<<<<<<<<<< relative MIDI NOTE Number
} else { // selKoto.key == 1 or 2 or 3 or 4
// text1.xOffset = -3
// var chordLength = currentCursor.chord().notes;
// var len = chordLength-1; //和音の上下を逆にする場合
pitch1=notes[iChord].pitch ;
var len=0;
var oshide=0;
var genNum;
var CrLf="\n";
// var CrLf="";
//while (len >= 0){ // 音符の読みを下から並べるループ //和音の上下を逆とする場合
// while (len < chordLength){ // 音符の読みを下から並べるループ
// var pitch = currentCursor.chord().note(len).pitch; // 音符の音程
/* for(var j=0 ; j<128 ; j++) if(pitch == j){
numOfNote[j]=numOfNote[j]+1; // ある音程の音符の数を数える
noteName[j]=currentCursor.chord().note(len).name; // 音符のある音程の音名を記録
}
*/
for(var gen=0 ; gen parseInt(tuningTmp[nString-1])+tuningBase){ // 巾より高い場合
genNum=nString-1; // 糸は仮に巾
oshide=1; // 押し手が必要な音であるというフラグ
break; // 弦が決まったので、一から巾のforループを抜ける
}else{
if(pitch1 > parseInt(tuningTmp[gen])+tuningBase && pitch1 < parseInt(tuningTmp[gen+1])+tuningBase ) { // ある音程が、チューニングの音程を下回り かつ 隣の糸より高い場合
// }else if(pitch > tuningTmp[gen]+tuningBase) { // ある音程が、現在調べている糸の音程を下回った場合
genNum=gen; // 一つ音程が下の糸の番号とする。
oshide=1; // 押し手が必要な音であるというフラグ
// print("Oshide "+gen+" pitch="+pitch);
break; // 弦が仮に決まったので、一から巾のforループを抜ける
}
}
}// end for(gen
if(pitch1 == parseInt(tuningTmp[genNum])+tuningBase) text1.text+=sp+fingerings[genNum]+CrLf; // ある弦である場合
else if(pitch1 == parseInt(tuningTmp[genNum])+tuningBase+1) text1.text+=fingerings[Lenf-3]+fingerings[genNum]+CrLf; //弱押しの場合
else if(pitch1 == parseInt(tuningTmp[genNum])+tuningBase+2) text1.text+=fingerings[Lenf-2]+fingerings[genNum]+CrLf; //強押しの場合
else if(pitch1 == parseInt(tuningTmp[genNum])+tuningBase+3) text1.text+=fingerings[Lenf-1]+fingerings[genNum]+CrLf; //二重押しの場合
else if(pitch1 > parseInt(tuningTmp[genNum])+tuningBase+3) text1.text+=sp+fingerings[Lenf]+CrLf; //三重押し以上の場合
else if(pitch1 < parseInt(tuningTmp[0])+tuningBase) text1.text+=sp+fingerings[Lenf]+CrLf; //一の弦より低い場合
//len = len-1; //和音の上下を逆とするの場合
len = len+1;
}
} // end TIE
// currentCursor.putStaffText(textKoto); // 文字を出力
} // end iChord
// currentCursor.next();
} // end function
/*
var numOfNoteText="";
if(MidiNo){
for( var j=127 ; j>=0 ; j--) if(numOfNote[j] != 0) {
// print(j+" "+noteName[j]+" "+numOfNote[j]); // ある音程の音符の数を表示する。
numOfNoteText+=poetText.poet=noteName[j]+String(parseInt(j/12)+0)+" : "+numOfNote[j]+CrLf;" : ";//+numOfNoteJ[j]+CrLf;
// 箏の糸番号も表示するとよいが、フォントの設定が必要。箏の種類の情報も必要。MidiNoは使えない。
// テキストの途中でフォントを変更する方法があるか?、2つの領域を使うか?
}
}
*/
// } // end for note
// } // end function
/*
FileIO {
id: my_file1
source: tempPath() + "/temp9999K.txt" // OK
// source: "/Users/tac/Documents/temp9999K.txt" // OK
onError: fileExist=false // console.log(msg)
}
*/
} // end MuseScore