//============================================== // 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