Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b40a7f4ad2 | |||
| f4df20188c | |||
| 8dc9b87035 | |||
| c915c48610 |
148
woodwind_tab_generator/Fingering.js
Normal file
148
woodwind_tab_generator/Fingering.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// +--------+
|
||||||
|
// | COLORS |
|
||||||
|
// +--------+
|
||||||
|
// You can change the color of the fingering chart here.
|
||||||
|
// Colors can be hex codes ("#123456") or names ("green")
|
||||||
|
|
||||||
|
function getColor(note) {
|
||||||
|
const colorMap = {
|
||||||
|
C: "#ed1c24",
|
||||||
|
"C#": "#f37021",
|
||||||
|
D: "#f8931e",
|
||||||
|
"D#": "#ffc20e",
|
||||||
|
E: "#fff200",
|
||||||
|
F: "#bed630",
|
||||||
|
"F#": "#72bf44",
|
||||||
|
G: "#00a29d",
|
||||||
|
"G#": "#007dc5",
|
||||||
|
A: "#49479d",
|
||||||
|
"A#": "#8d5ca6",
|
||||||
|
B: "#c9277d",
|
||||||
|
}
|
||||||
|
return colorMap[note] || "gray"
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// INSTRUMENTS
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// Each woodwind has the form:
|
||||||
|
// {
|
||||||
|
// name:
|
||||||
|
// fingering:
|
||||||
|
// format:
|
||||||
|
// },
|
||||||
|
//
|
||||||
|
// NAME: can be anything descriptive.
|
||||||
|
//
|
||||||
|
// FINGERING:
|
||||||
|
// Each note that can be played, then its holes.
|
||||||
|
// Holes are listed starting from the mouthpiece.
|
||||||
|
// You can use these symbols to represent whether the holes are covered:
|
||||||
|
// 0 = ○
|
||||||
|
// o = ⦶
|
||||||
|
// 1 = ●
|
||||||
|
// | = ◐
|
||||||
|
// - = ◒
|
||||||
|
//
|
||||||
|
// FORMAT:
|
||||||
|
// Decide how the fingering will be displayed.
|
||||||
|
// "$note" will be replaced with the name of the note,
|
||||||
|
// "$1" "$2" etc. will be replaced by the hole symbols, with $1 being closest to the mouthpiece.
|
||||||
|
|
||||||
|
|
||||||
|
const woodwinds = [
|
||||||
|
{
|
||||||
|
name: "Soprano Recorder",
|
||||||
|
fingering: {
|
||||||
|
"C5": "11111111",
|
||||||
|
"C#5": "1111111|",
|
||||||
|
"D5": "11111110",
|
||||||
|
"D#5": "111111|o",
|
||||||
|
"E5": "111111oo",
|
||||||
|
"F5": "11110011",
|
||||||
|
"F#5": "1111011o",
|
||||||
|
"G5": "111100oo",
|
||||||
|
"G#5": "111011|o",
|
||||||
|
"A5": "111000oo",
|
||||||
|
"A#5": "110110oo",
|
||||||
|
"B5": "110000oo",
|
||||||
|
"C6": "101000oo",
|
||||||
|
"C#6": "011000oo",
|
||||||
|
"D6": "001000oo",
|
||||||
|
"D#6": "0011111o",
|
||||||
|
"E6": "-11111oo",
|
||||||
|
"F6": "-111101o",
|
||||||
|
"F#6": "-11101oo",
|
||||||
|
"G6": "-11100oo",
|
||||||
|
"G#6": "-11010oo",
|
||||||
|
"A6": "-11000oo",
|
||||||
|
"A#6": "-110011|",
|
||||||
|
"B6": "-11011oo",
|
||||||
|
"C7": "-10011oo",
|
||||||
|
"C#7": "-1011011",
|
||||||
|
"D7": "-1011011",
|
||||||
|
"D#7": "-011011o",
|
||||||
|
},
|
||||||
|
format:
|
||||||
|
`$note
|
||||||
|
|
||||||
|
$1
|
||||||
|
—
|
||||||
|
$2
|
||||||
|
$3
|
||||||
|
$4
|
||||||
|
—
|
||||||
|
$5
|
||||||
|
$6
|
||||||
|
$7
|
||||||
|
$8`
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Alto Recorder",
|
||||||
|
fingering: {
|
||||||
|
"F4": "11111111",
|
||||||
|
"F#4":"1111111|",
|
||||||
|
"G4": "11111110",
|
||||||
|
"G#4":"111111|o",
|
||||||
|
"A4": "111111oo",
|
||||||
|
"A#4":"11110011",
|
||||||
|
"B4": "1111011o",
|
||||||
|
"C5": "111100oo",
|
||||||
|
"C#5":"111011|o",
|
||||||
|
"D5": "111000oo",
|
||||||
|
"D#5":"110110oo",
|
||||||
|
"E5": "110000oo",
|
||||||
|
"F5": "101000oo",
|
||||||
|
"F#5":"011000oo",
|
||||||
|
"G5": "001000oo",
|
||||||
|
"G#5":"0011111o",
|
||||||
|
"A5": "-11111oo",
|
||||||
|
"A#5":"-111101o",
|
||||||
|
"B5": "-11101oo",
|
||||||
|
"C6": "-11100oo",
|
||||||
|
"C#6":"-11010oo",
|
||||||
|
"D6": "-11000oo",
|
||||||
|
"D#6":"-110011|",
|
||||||
|
"E6": "-11011oo",
|
||||||
|
"F6": "-10011oo",
|
||||||
|
"F#6": "-1011011",
|
||||||
|
"G6": "-1011011",
|
||||||
|
"G#6": "-011011o",
|
||||||
|
},
|
||||||
|
format:
|
||||||
|
`$note
|
||||||
|
|
||||||
|
$1
|
||||||
|
—
|
||||||
|
$2
|
||||||
|
$3
|
||||||
|
$4
|
||||||
|
—
|
||||||
|
$5
|
||||||
|
$6
|
||||||
|
$7
|
||||||
|
$8`
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -29,6 +29,8 @@ import QtQuick.Controls 2.15
|
|||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import MuseScore 3.0
|
import MuseScore 3.0
|
||||||
|
|
||||||
|
import "Fingering.js" as Fingering
|
||||||
|
|
||||||
MuseScore {
|
MuseScore {
|
||||||
id: mscore
|
id: mscore
|
||||||
version: "4.4"
|
version: "4.4"
|
||||||
@ -64,18 +66,18 @@ MuseScore {
|
|||||||
// USER SETTINGS (& defaults)
|
// USER SETTINGS (& defaults)
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
property int userFontSize: 6
|
property int userFontSize: 11
|
||||||
property int userJustification: 1 // 0=left,1=center,2=right
|
property int userJustification: 1 // 0=left,1=center,2=right
|
||||||
property real userOffsetY: 1.2
|
property real userOffsetY: 1.7
|
||||||
property real userLineSpacing: 0.7
|
property real userLineSpacing: 0.5
|
||||||
property string userFormatString: "" // mirrors current profile's formatString
|
property string userFormatString: "" // mirrors current profile's format
|
||||||
property string userFontFamily: "Menlo, Consolas, Liberation Mono, Courier New, monospace"
|
property string userFontFamily: "Menlo, Consolas, Liberation Mono, Courier New, monospace"
|
||||||
property bool userTransposed: false
|
property bool userTransposed: false
|
||||||
|
|
||||||
property int defaultFontSize: 6
|
property int defaultFontSize: 11
|
||||||
property int defaultJustification: 1
|
property int defaultJustification: 1
|
||||||
property real defaultOffsetY: 1.2
|
property real defaultOffsetY: 1.7
|
||||||
property real defaultLineSpacing: 0.7
|
property real defaultLineSpacing: 0.5
|
||||||
property string defaultFontFamily: "Menlo, Consolas, Liberation Mono, Courier New, monospace"
|
property string defaultFontFamily: "Menlo, Consolas, Liberation Mono, Courier New, monospace"
|
||||||
property bool defaultTransposed: false
|
property bool defaultTransposed: false
|
||||||
|
|
||||||
@ -83,352 +85,6 @@ MuseScore {
|
|||||||
property var history: 0
|
property var history: 0
|
||||||
property var modified: false
|
property var modified: false
|
||||||
|
|
||||||
//---------------------------------------------------------
|
|
||||||
// FINGERING DICTIONARY
|
|
||||||
// Last bit = plus sign indicator
|
|
||||||
//---------------------------------------------------------
|
|
||||||
|
|
||||||
//Dictionary for your woodwind's fingerings, specifying the note and the fingering pattern (for whistles: left-> right, starting from the fipple end).
|
|
||||||
//NOTE: Note specification convention here uses sharps, so e.g. a Bb whistle needs to be defined as A#
|
|
||||||
//2 indicates a closed hole, 1 indicates a half hole, 0 indicates open hole. The last digit is a reserved section indicating how many plusses (+) should be drawn to signal overblowing.
|
|
||||||
|
|
||||||
|
|
||||||
// ○ = 0
|
|
||||||
// ◐ = 1
|
|
||||||
// ● = 2
|
|
||||||
|
|
||||||
//Example fingering specification for high D whistle overblowing one octave to G6
|
|
||||||
// |---------------------------------------------
|
|
||||||
//windway -> | | | ● ● ● ○ ○ ○ |
|
|
||||||
// |---------------------------------------------
|
|
||||||
// "G6": " 2 2 2 0 0 0 1 " <- last bit indicates number of +'s to add (i.e. how many times to overblow). Can be >= 0
|
|
||||||
|
|
||||||
|
|
||||||
//Example fingering specification for G#5 on a high D whistle
|
|
||||||
// |---------------------------------------------
|
|
||||||
//windway -> | | | ● ● ◐ ○ ○ ○ |
|
|
||||||
// |---------------------------------------------
|
|
||||||
// "G#5": " 2 2 1 0 0 0 0 " <- overblow bit set to zero to indicate first octave
|
|
||||||
// | | | | | | |
|
|
||||||
// variables in format string: $1 $2 $3 $4 $5 $6 $+ and then $note would be replaced with 'G#5'
|
|
||||||
// These variables are replaced by the corresponding symbol or text when run.
|
|
||||||
|
|
||||||
|
|
||||||
//Can have any number of holes in the specification
|
|
||||||
//NOTE: This plugin was generated from a maker's perspective, and thus the note names correspond with the sounding pitch, and not the
|
|
||||||
// typical octave shift down presented in notations. The dictionary can easily be edited to reflect the transposition, however, if desired.
|
|
||||||
//TODO: Add transposition checkmark which corrects notations to octave down pitch commonly used.
|
|
||||||
|
|
||||||
//Whistle definitions
|
|
||||||
property var defaultProfiles: [
|
|
||||||
{
|
|
||||||
name: "Low D Whistle [D4-D6] (Prefer half-holing)",
|
|
||||||
fingeringDict: {
|
|
||||||
"D4": "2222220",
|
|
||||||
"D#4": "2222210",
|
|
||||||
"E4": "2222200",
|
|
||||||
"F4": "2222100",
|
|
||||||
"F#4": "2222000",
|
|
||||||
"G4": "2220000",
|
|
||||||
"G#4": "2210000",
|
|
||||||
"A4": "2200000",
|
|
||||||
"A#4": "2100000",
|
|
||||||
"B4": "2000000",
|
|
||||||
"C5": "0220000",
|
|
||||||
"C#5": "0000000",
|
|
||||||
"D5": "0222221",
|
|
||||||
"D#5": "2222211",
|
|
||||||
"E5": "2222201",
|
|
||||||
"F5": "2222101",
|
|
||||||
"F#5": "2222001",
|
|
||||||
"G5": "2220001",
|
|
||||||
"G#5": "2210001",
|
|
||||||
"A5": "2200001",
|
|
||||||
"A#5": "2100001",
|
|
||||||
"B5": "2000001",
|
|
||||||
"C6": "0202221",
|
|
||||||
"C#6": "0000001",
|
|
||||||
"D6": "2222222",
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Eb Whistle [Eb-D6] (Prefer haf-holing)",
|
|
||||||
"fingeringDict": {
|
|
||||||
"D#4": "2222220",
|
|
||||||
"E4": "2222210",
|
|
||||||
"F4": "2222200",
|
|
||||||
"F#4": "2222100",
|
|
||||||
"G4": "2222000",
|
|
||||||
"G#4": "2220000",
|
|
||||||
"A4": "2210000",
|
|
||||||
"A#4": "2200000",
|
|
||||||
"B4": "2100000",
|
|
||||||
"C5": "2000000",
|
|
||||||
"C#5": "1000000",
|
|
||||||
"D5": "0000000",
|
|
||||||
"D#5": "0222221",
|
|
||||||
"E5": "2222211",
|
|
||||||
"F5": "2222201",
|
|
||||||
"F#5": "2222101",
|
|
||||||
"G5": "2222001",
|
|
||||||
"G#5": "2220001",
|
|
||||||
"A5": "2210001",
|
|
||||||
"A#5": "2200001",
|
|
||||||
"B5": "2100001",
|
|
||||||
"C6": "2000001",
|
|
||||||
"C#6": "1000001",
|
|
||||||
"D6": "0000001"
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "F Whistle [F4-E6] (Prefer haf-holing)",
|
|
||||||
"fingeringDict": {
|
|
||||||
"F4": "2222220",
|
|
||||||
"F#4": "2222210",
|
|
||||||
"G4": "2222200",
|
|
||||||
"G#4": "2222100",
|
|
||||||
"A4": "2222000",
|
|
||||||
"A#4": "2220000",
|
|
||||||
"B4": "2210000",
|
|
||||||
"C5": "2200000",
|
|
||||||
"C#5": "2100000",
|
|
||||||
"D5": "2000000",
|
|
||||||
"D#5": "1000000",
|
|
||||||
"E5": "0000000",
|
|
||||||
"F5": "0222221",
|
|
||||||
"F#5": "2222211",
|
|
||||||
"G5": "2222201",
|
|
||||||
"G#5": "2222101",
|
|
||||||
"A5": "2222001",
|
|
||||||
"A#5": "2220001",
|
|
||||||
"B5": "2210001",
|
|
||||||
"C6": "2200001",
|
|
||||||
"C#6": "2100001",
|
|
||||||
"D6": "2000001",
|
|
||||||
"D#6": "1000001",
|
|
||||||
"E6": "0000001"
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "G Whistle [G4-F#6] (Prefer haf-holing)",
|
|
||||||
"fingeringDict": {
|
|
||||||
"G4": "2222220",
|
|
||||||
"G#4": "2222210",
|
|
||||||
"A4": "2222200",
|
|
||||||
"A#4": "2222100",
|
|
||||||
"B4": "2222000",
|
|
||||||
"C5": "2220000",
|
|
||||||
"C#5": "2210000",
|
|
||||||
"D5": "2200000",
|
|
||||||
"D#5": "2100000",
|
|
||||||
"E5": "2000000",
|
|
||||||
"F5": "1000000",
|
|
||||||
"F#5": "0000000",
|
|
||||||
"G5": "0222221",
|
|
||||||
"G#5": "2222211",
|
|
||||||
"A5": "2222201",
|
|
||||||
"A#5": "2222101",
|
|
||||||
"B5": "2222001",
|
|
||||||
"C6": "2220001",
|
|
||||||
"C#6": "2210001",
|
|
||||||
"D6": "2200001",
|
|
||||||
"D#6": "2100001",
|
|
||||||
"E6": "2000001",
|
|
||||||
"F6": "1000001",
|
|
||||||
"F#6": "0000001"
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Custom Chromatic G Whistle [G4-G#6] (9 holes)",
|
|
||||||
fingeringDict: {
|
|
||||||
"G4": "2222222220",
|
|
||||||
"G#4": "2222222200",
|
|
||||||
"A4": "2222222000",
|
|
||||||
"A#4": "2222220000",
|
|
||||||
"B4": "2222200000",
|
|
||||||
"C5": "2222000000",
|
|
||||||
"C#5": "2220000000",
|
|
||||||
"D5": "2202000000",
|
|
||||||
"D#5": "2022200000",
|
|
||||||
"E5": "2020000000",
|
|
||||||
"F5": "0220000000",
|
|
||||||
"F#5": "0000000000",
|
|
||||||
"G5": "2222222221",
|
|
||||||
"G#5": "2222222201",
|
|
||||||
"A5": "2222222001",
|
|
||||||
"A#5": "2222220001",
|
|
||||||
"B5": "2222200001",
|
|
||||||
"C6": "2222000001",
|
|
||||||
"C#6": "2220000001",
|
|
||||||
"D6": "2200000001",
|
|
||||||
"D#6": "2222222202",
|
|
||||||
"E6": "2222222002",
|
|
||||||
"F6": "2222220002",
|
|
||||||
"F#6": "0222200002",
|
|
||||||
"G6": "2222222222",
|
|
||||||
"G#6": "0222222222"
|
|
||||||
},
|
|
||||||
formatString: "$1 \n$2\n$3\n$4\n$5\n\n$6\n$7\n$8\n$9\n$+\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Bb Whistle [Bb4-A6] (Prefer haf-holing)",
|
|
||||||
"fingeringDict": {
|
|
||||||
"A#4": "2222220",
|
|
||||||
"B4": "2222210",
|
|
||||||
"C5": "2222200",
|
|
||||||
"C#5": "2222100",
|
|
||||||
"D5": "2222000",
|
|
||||||
"D#5": "2220000",
|
|
||||||
"E5": "2210000",
|
|
||||||
"F5": "2200000",
|
|
||||||
"F#5": "2100000",
|
|
||||||
"G5": "2000000",
|
|
||||||
"G#5": "1000000",
|
|
||||||
"A5": "0000000",
|
|
||||||
"A#5": "0222221",
|
|
||||||
"B5": "2222211",
|
|
||||||
"C6": "2222201",
|
|
||||||
"C#6": "2222101",
|
|
||||||
"D6": "2222001",
|
|
||||||
"D#6": "2220001",
|
|
||||||
"E6": "2210001",
|
|
||||||
"F6": "2200001",
|
|
||||||
"F#6": "2100001",
|
|
||||||
"G6": "2000001",
|
|
||||||
"G#6": "1000001",
|
|
||||||
"A6": "0000001"
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "C Whistle [C4-C6] (Prefer cross-fingering)",
|
|
||||||
"fingeringDict": {
|
|
||||||
"C4": "2222220",
|
|
||||||
"C#4": "2222210",
|
|
||||||
"D4": "2222200",
|
|
||||||
"D#4": "2222100",
|
|
||||||
"E4": "2222000",
|
|
||||||
"F4": "2220000",
|
|
||||||
"F#4": "2210000",
|
|
||||||
"G4": "2200000",
|
|
||||||
"G#4": "2022220",
|
|
||||||
"A4": "2000000",
|
|
||||||
"A#4": "0220000",
|
|
||||||
"B4": "0000000",
|
|
||||||
"C5": "0222221",
|
|
||||||
"C#5": "2222211",
|
|
||||||
"D5": "2222201",
|
|
||||||
"D#5": "2222101",
|
|
||||||
"E5": "2222001",
|
|
||||||
"F5": "2220001",
|
|
||||||
"F#5": "2202201",
|
|
||||||
"G5": "2200001",
|
|
||||||
"G#5": "2020001",
|
|
||||||
"A5": "2000001",
|
|
||||||
"A#5": "0202221",
|
|
||||||
"B5": "0000001",
|
|
||||||
"C6": "0222222",
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Soprano Recorder",
|
|
||||||
"fingeringDict": {
|
|
||||||
"C4": "222222220",
|
|
||||||
"C#4": "222222210",
|
|
||||||
"D4": "222222200",
|
|
||||||
"D#4": "222222100",
|
|
||||||
"E4": "222222000",
|
|
||||||
"F4": "222200000",
|
|
||||||
"F#4": "222100000",
|
|
||||||
"G4": "222000000",
|
|
||||||
"G#4": "221000000",
|
|
||||||
"A4": "220000000",
|
|
||||||
"A#4": "210000000",
|
|
||||||
"B4": "200000000",
|
|
||||||
"C5": "000222221",
|
|
||||||
"C#5": "002222211",
|
|
||||||
"D5": "002222201",
|
|
||||||
"D#5": "002222101",
|
|
||||||
"E5": "002222001",
|
|
||||||
"F5": "002220001",
|
|
||||||
"F#5": "002210001",
|
|
||||||
"G5": "002200001",
|
|
||||||
"G#5": "002100001",
|
|
||||||
"A5": "002000001",
|
|
||||||
"A#5": "001000001",
|
|
||||||
"B5": "000000001",
|
|
||||||
},
|
|
||||||
formatString: "$note\n\n$1\n-\n$2\n$3\n$4\n-\n$5\n$6\n$7\n$8\n$+"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "High D Whistle [D5-D7] (Prefer half-holing)",
|
|
||||||
fingeringDict: {
|
|
||||||
"D5": "2222220",
|
|
||||||
"D#5": "2222210",
|
|
||||||
"E5": "2222200",
|
|
||||||
"F5": "2222100",
|
|
||||||
"F#5": "2222000",
|
|
||||||
"G5": "2220000",
|
|
||||||
"G#5": "2210000",
|
|
||||||
"A5": "2200000",
|
|
||||||
"A#5": "2100000",
|
|
||||||
"B5": "2000000",
|
|
||||||
"C6": "0220000",
|
|
||||||
"C#6": "0000000",
|
|
||||||
"D6": "0222221",
|
|
||||||
"D#6": "2222211",
|
|
||||||
"E6": "2222201",
|
|
||||||
"F6": "2222101",
|
|
||||||
"F#6": "2222001",
|
|
||||||
"G6": "2220001",
|
|
||||||
"G#6": "2210001",
|
|
||||||
"A6": "2200001",
|
|
||||||
"A#6": "2100001",
|
|
||||||
"B6": "2000001",
|
|
||||||
"C7": "0202221",
|
|
||||||
"C#7": "0000001",
|
|
||||||
"D7": "2222222",
|
|
||||||
},
|
|
||||||
formatString: "$1\n$2\n$3\n\n$4\n$5\n$6\n$+\n\n$note"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// OCARINA. from hole 1 to 12 they are:
|
|
||||||
//$1 LThumb, $2 Lindex, $3 LMiddle, $4 Lring, $5 LPinky $6 LSubhole ->
|
|
||||||
// -> $7 RThumb, $8 RIndex, $9 RMiddle, $10 RRing, $11 RPinky, $12 RSubhole
|
|
||||||
//Requires centered text
|
|
||||||
name: "Alto C Ocarina [A4-F6] (12 hole)",
|
|
||||||
"fingeringDict": {
|
|
||||||
"A4": "2222222222220",
|
|
||||||
"A#4": "2222222222200",
|
|
||||||
"B4": "2222202222020",
|
|
||||||
"C5": "2222202222200",
|
|
||||||
"C#5": "2222202222020",
|
|
||||||
"D5": "2222202222000",
|
|
||||||
"D#5": "2222202220020",
|
|
||||||
"E5": "2222202220000",
|
|
||||||
"F5": "2222202200000",
|
|
||||||
"F#5": "2222202002000",
|
|
||||||
"G5": "2222202000000",
|
|
||||||
"G#5": "2220202002000",
|
|
||||||
"A5": "2220202000000",
|
|
||||||
"A#5": "2200202002000",
|
|
||||||
"B5": "2200202000000",
|
|
||||||
"C6": "2000202000000",
|
|
||||||
"C#6": "0000202002000",
|
|
||||||
"D6": "0000202000000",
|
|
||||||
"D#6": "0000200002000",
|
|
||||||
"E6": "0000200000000",
|
|
||||||
"F6": "0000000000000"
|
|
||||||
},
|
|
||||||
formatString: " $11\n $5 $10\n $4 $12$9\n $3 $8\n $2$6 \n\n $1 $7\n$note"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Runtime copy of profiles (loaded/saved via preferences)
|
// Runtime copy of profiles (loaded/saved via preferences)
|
||||||
property var profiles: []
|
property var profiles: []
|
||||||
property int currentProfileIndex: 0
|
property int currentProfileIndex: 0
|
||||||
@ -439,16 +95,16 @@ MuseScore {
|
|||||||
|
|
||||||
|
|
||||||
function getCurrentFingeringDict() {
|
function getCurrentFingeringDict() {
|
||||||
return profiles.length > 0 ? profiles[currentProfileIndex].fingeringDict : {}
|
return profiles.length > 0 ? profiles[currentProfileIndex].fingering : {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentFormatString() {
|
function getCurrentFormatString() {
|
||||||
return profiles.length > 0 ? profiles[currentProfileIndex].formatString : ""
|
return profiles.length > 0 ? profiles[currentProfileIndex].format : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCurrentFormatString(newFormat) {
|
function setCurrentFormatString(newFormat) {
|
||||||
if (profiles.length > 0) {
|
if (profiles.length > 0) {
|
||||||
profiles[currentProfileIndex].formatString = newFormat
|
profiles[currentProfileIndex].format = newFormat
|
||||||
userFormatString = newFormat
|
userFormatString = newFormat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,8 +169,8 @@ MuseScore {
|
|||||||
// Save profiles as JSON string
|
// Save profiles as JSON string
|
||||||
var profilesForStorage = profiles.map(p => ({
|
var profilesForStorage = profiles.map(p => ({
|
||||||
name: p.name,
|
name: p.name,
|
||||||
fingeringDict: p.fingeringDict,
|
fingering: p.fingering,
|
||||||
formatString: p.formatString
|
format: p.format
|
||||||
}))
|
}))
|
||||||
pluginSettings.storedProfiles = JSON.stringify(profilesForStorage)
|
pluginSettings.storedProfiles = JSON.stringify(profilesForStorage)
|
||||||
pluginSettings.storedCurrentProfile = currentProfileIndex
|
pluginSettings.storedCurrentProfile = currentProfileIndex
|
||||||
@ -536,24 +192,24 @@ MuseScore {
|
|||||||
var loaded = JSON.parse(profilesStr)
|
var loaded = JSON.parse(profilesStr)
|
||||||
profiles = loaded.map(p => ({
|
profiles = loaded.map(p => ({
|
||||||
name: p.name,
|
name: p.name,
|
||||||
fingeringDict: p.fingeringDict,
|
fingering: p.fingering,
|
||||||
formatString: p.formatString
|
format: p.format
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Failed to parse saved profiles, using defaults")
|
console.log("Failed to parse saved profiles, using defaults")
|
||||||
error("Failed to parse saved profiles, using defaults")
|
error("Failed to parse saved profiles, using defaults")
|
||||||
|
|
||||||
profiles = defaultProfiles.map(p => ({
|
profiles = Fingering.woodwinds.map(p => ({
|
||||||
name: p.name,
|
name: p.name,
|
||||||
fingeringDict: JSON.parse(JSON.stringify(p.fingeringDict)),
|
fingering: JSON.parse(JSON.stringify(p.fingering)),
|
||||||
formatString: p.formatString
|
format: p.format
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
profiles = defaultProfiles.map(p => ({
|
profiles = Fingering.woodwinds.map(p => ({
|
||||||
name: p.name,
|
name: p.name,
|
||||||
fingeringDict: JSON.parse(JSON.stringify(p.fingeringDict)),
|
fingering: JSON.parse(JSON.stringify(p.fingering)),
|
||||||
formatString: p.formatString
|
format: p.format
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,11 +239,11 @@ MuseScore {
|
|||||||
function resetToDefaults() {
|
function resetToDefaults() {
|
||||||
getHistory().begin() // if you have undo/redo
|
getHistory().begin() // if you have undo/redo
|
||||||
|
|
||||||
// 1. Restore profiles from defaultProfiles
|
// 1. Restore profiles from woodwinds
|
||||||
profiles = defaultProfiles.map(p => ({
|
profiles = Fingering.woodwinds.map(p => ({
|
||||||
name: p.name,
|
name: p.name,
|
||||||
fingeringDict: JSON.parse(JSON.stringify(p.fingeringDict)),
|
fingering: JSON.parse(JSON.stringify(p.fingering)),
|
||||||
formatString: p.formatString
|
format: p.format
|
||||||
}))
|
}))
|
||||||
currentProfileIndex = 0
|
currentProfileIndex = 0
|
||||||
|
|
||||||
@ -614,8 +270,8 @@ MuseScore {
|
|||||||
// 4. Persist all changes to Settings
|
// 4. Persist all changes to Settings
|
||||||
pluginSettings.storedProfiles = JSON.stringify(profiles.map(p => ({
|
pluginSettings.storedProfiles = JSON.stringify(profiles.map(p => ({
|
||||||
name: p.name,
|
name: p.name,
|
||||||
fingeringDict: p.fingeringDict,
|
fingering: p.fingering,
|
||||||
formatString: p.formatString
|
format: p.format
|
||||||
})))
|
})))
|
||||||
pluginSettings.storedCurrentProfile = currentProfileIndex
|
pluginSettings.storedCurrentProfile = currentProfileIndex
|
||||||
pluginSettings.storedFontSize = userFontSize
|
pluginSettings.storedFontSize = userFontSize
|
||||||
@ -667,7 +323,7 @@ MuseScore {
|
|||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
// MIDI -> Note Name
|
// MIDI -> Note Name
|
||||||
//---------------------------------------------------------
|
//--------------C-------------------------------------------
|
||||||
|
|
||||||
function pitchToName(midiPitch) {
|
function pitchToName(midiPitch) {
|
||||||
var names = ["C","C#","D","D#","E","F",
|
var names = ["C","C#","D","D#","E","F",
|
||||||
@ -679,44 +335,125 @@ MuseScore {
|
|||||||
return names[pitchClass] + octave
|
return names[pitchClass] + octave
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function subscriptDigit(n) {
|
||||||
|
return String.fromCharCode(0x2080 + n)
|
||||||
|
}
|
||||||
|
|
||||||
|
function noteToPitchName(accidental, pitchClass, defaultValue) {
|
||||||
|
switch (accidental) {
|
||||||
|
case Accidental.FLAT:
|
||||||
|
case Accidental.NATURAL_FLAT:
|
||||||
|
switch (pitchClass) {
|
||||||
|
case 1: return "D♭"
|
||||||
|
case 3: return "E♭"
|
||||||
|
case 4: return "F♭"
|
||||||
|
case 6: return "G♭"
|
||||||
|
case 8: return "A♭"
|
||||||
|
case 10: return "B♭"
|
||||||
|
case 11: return "C♭"
|
||||||
|
default: return defaultValue
|
||||||
|
}
|
||||||
|
case Accidental.SHARP:
|
||||||
|
case Accidental.NATURAL_SHARP:
|
||||||
|
switch (pitchClass) {
|
||||||
|
case 0: return "B♯"
|
||||||
|
case 1: return "C♯"
|
||||||
|
case 3: return "D♯"
|
||||||
|
case 5: return "E♯"
|
||||||
|
case 6: return "F♯"
|
||||||
|
case 8: return "G♯"
|
||||||
|
case 10: return "A♯"
|
||||||
|
default: return defaultValue
|
||||||
|
}
|
||||||
|
case Accidental.NONE:
|
||||||
|
case Accidental.NATURAL:
|
||||||
|
switch (pitchClass) {
|
||||||
|
case 0: return "C"
|
||||||
|
case 2: return "D"
|
||||||
|
case 4: return "E"
|
||||||
|
case 5: return "F"
|
||||||
|
case 7: return "G"
|
||||||
|
case 9: return "A"
|
||||||
|
case 11: return "B"
|
||||||
|
default: return defaultValue
|
||||||
|
}
|
||||||
|
default: return "!" + defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSharp(pitch, keySig) {
|
||||||
|
var p = 6
|
||||||
|
for (var i = 0; i < keySig; ++i) {
|
||||||
|
if (pitch == p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p = (p + 7) % 12
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFlat(pitch, keySig) {
|
||||||
|
var p = 10
|
||||||
|
for (var i = 0; i > keySig; --i) {
|
||||||
|
if (pitch == p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p = (p + 5) % 12
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function noteToName(note, keySig, defaultValue) {
|
||||||
|
var accidental = note.accidentalType
|
||||||
|
var pitchClass = note.pitch % 12
|
||||||
|
if (accidental == Accidental.NONE && isSharp(pitchClass, keySig)) {
|
||||||
|
accidental = Accidental.SHARP
|
||||||
|
}
|
||||||
|
if (accidental == Accidental.NONE && isFlat(pitchClass, keySig)) {
|
||||||
|
accidental = Accidental.FLAT
|
||||||
|
}
|
||||||
|
var octave = Math.floor(note.pitch / 12) - 1
|
||||||
|
var pitchName = noteToPitchName(accidental, pitchClass, defaultValue)
|
||||||
|
return pitchName + subscriptDigit(octave)
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
// BUILD ASCII DIAGRAM
|
// BUILD UTF-8 DIAGRAM
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
//Build fingering text using binary representation of the fingering for a note.
|
//Build fingering text using binary representation of the fingering for a note.
|
||||||
//The last bit is the plus bit, indicating the first octave (0) or the second octave (1), to be annotated with a +
|
function buildFingeringText(binaryString, format, noteName) {
|
||||||
function buildFingeringText(binaryString, formatString, noteName) {
|
const open = "0"
|
||||||
if (!binaryString || binaryString.length < 2)
|
const closed = "1"
|
||||||
|
const half_ver = "|"
|
||||||
|
const half_hor = "-"
|
||||||
|
const open_split = "o"
|
||||||
|
if (!binaryString || binaryString.length < 1)
|
||||||
return "Invalid fingering pattern. Length is wrong"
|
return "Invalid fingering pattern. Length is wrong"
|
||||||
|
|
||||||
if (!formatString || typeof formatString !== "string")
|
if (!format || typeof format !== "string")
|
||||||
return "ERR"
|
return "ERR"
|
||||||
|
|
||||||
var holeCount = binaryString.length - 1
|
var holeCount = binaryString.length
|
||||||
var plusBit = binaryString[binaryString.length - 1]
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// Validate allowed characters
|
// Validate allowed characters
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
for (var i = 0; i < holeCount; i++) {
|
for (var i = 0; i < holeCount; i++) {
|
||||||
if (binaryString[i] !== "0" &&
|
if (binaryString[i] !== open &&
|
||||||
binaryString[i] !== "1" &&
|
binaryString[i] !== closed &&
|
||||||
binaryString[i] !== "1" &&
|
binaryString[i] !== half_ver &&
|
||||||
binaryString[i] !== "2" &&
|
binaryString[i] !== half_hor &&
|
||||||
binaryString[i] !== "3")
|
binaryString[i] !== open_split)
|
||||||
return "Invalid Fingering Pattern Value. Must be one of 0,1,2"
|
return "Invalid Fingering Pattern Value. Check the config in the .qml file!"
|
||||||
}
|
}
|
||||||
|
|
||||||
var plusInt = parseInt(plusBit)
|
|
||||||
if (isNaN(plusInt) || plusInt < 0 || plusInt.toString() !== plusBit) {
|
|
||||||
return "Invalid Plus Bit: must be a positive integer (0, 1, 2, ...)"
|
|
||||||
}
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// Clone format string
|
// Clone format string
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
var output = formatString
|
var output = format
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// Replace note placeholder ($note) with note name
|
// Replace note placeholder ($note) with note name
|
||||||
@ -734,16 +471,17 @@ MuseScore {
|
|||||||
|
|
||||||
for (var i = holeCount - 1; i >= 0; i--) {
|
for (var i = holeCount - 1; i >= 0; i--) {
|
||||||
var symbol
|
var symbol
|
||||||
if (binaryString[i] === "2")
|
if (binaryString[i] === closed)
|
||||||
symbol = String.fromCharCode(0x25CF) // Closed hole - custom font
|
symbol = String.fromCharCode(0x25CF) // closed hole
|
||||||
else if (binaryString[i] === "1")
|
if (binaryString[i] === half_ver)
|
||||||
symbol = String.fromCharCode(0x25D0) // Half hole - custom font
|
symbol = String.fromCharCode(0x25D1) // Half hole, covered on the side
|
||||||
else if (binaryString[i] == "3")
|
if (binaryString[i] == half_hor)
|
||||||
symbol = String.fromCharCode(0x25D2) // Bottom half hole
|
symbol = String.fromCharCode(0x25D2) // Half hole, covered on the bottom
|
||||||
else if (binaryString[i] == "4")
|
if (binaryString[i] == open_split)
|
||||||
symbol = String.fromCharCode(0x29B6) // Open Hold with line
|
symbol = String.fromCharCode(0x29B6) // Open Hole with line
|
||||||
else
|
// symbol = String.fromCharCode(0x25CB) + String.fromCharCode(0x20D2)
|
||||||
symbol = String.fromCharCode(0x25CB) // Open Hole - custom font
|
if (binaryString[i] == open)
|
||||||
|
symbol = String.fromCharCode(0x25CB) // Open Hole
|
||||||
|
|
||||||
var token = "$" + (i+1)
|
var token = "$" + (i+1)
|
||||||
while (output.indexOf(token) !== -1) {
|
while (output.indexOf(token) !== -1) {
|
||||||
@ -751,20 +489,6 @@ MuseScore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var plusCount = parseInt(plusBit)
|
|
||||||
var plusSymbol = "+".repeat(plusCount)
|
|
||||||
if (plusCount === 0) {
|
|
||||||
plusSymbol = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
|
||||||
// Replace plus placeholder ($+) with number of plusses based on plus bit
|
|
||||||
//-------------------------------------------------
|
|
||||||
|
|
||||||
while (output.indexOf("$+") !== -1) {
|
|
||||||
output = output.replace("$+", plusSymbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
// Final validation
|
// Final validation
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
@ -780,24 +504,6 @@ MuseScore {
|
|||||||
// APPLY TEXT FORMATTING
|
// APPLY TEXT FORMATTING
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
function getColor(noteName) {
|
|
||||||
const note = noteName.slice(0, -1)
|
|
||||||
const colorMap = {
|
|
||||||
C: "#ed1c24",
|
|
||||||
"C#": "#f37021",
|
|
||||||
D: "#f8931e",
|
|
||||||
"D#": "#ffc20e",
|
|
||||||
E: "#fff200",
|
|
||||||
F: "#bed630",
|
|
||||||
"F#": "#72bf44",
|
|
||||||
G: "#00a29d",
|
|
||||||
"G#": "#007dc5",
|
|
||||||
A: "#49479d",
|
|
||||||
"A#": "#8d5ca6",
|
|
||||||
B: "#c9277d",
|
|
||||||
}
|
|
||||||
return colorMap[note] || "gray"
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatText(text) {
|
function formatText(text) {
|
||||||
console.log("Formatting text - setting fontSize to:", userFontSize)
|
console.log("Formatting text - setting fontSize to:", userFontSize)
|
||||||
@ -945,28 +651,29 @@ MuseScore {
|
|||||||
while (cursor.segment) {
|
while (cursor.segment) {
|
||||||
if (cursor.element && cursor.element.type === Element.CHORD) {
|
if (cursor.element && cursor.element.type === Element.CHORD) {
|
||||||
var chord = cursor.element
|
var chord = cursor.element
|
||||||
|
var keySignature = cursor.keySignature
|
||||||
var midi = chord.notes[0].pitch
|
var midi = chord.notes[0].pitch
|
||||||
|
|
||||||
if (userTransposed) midi += 12
|
if (userTransposed) midi += 12
|
||||||
|
|
||||||
var noteName = pitchToName(midi)
|
var noteId = pitchToName(midi)
|
||||||
|
var noteName = noteToName(chord.notes[0], keySignature, "?")
|
||||||
|
|
||||||
var text = newElement(Element.STAFF_TEXT)
|
var text = newElement(Element.STAFF_TEXT)
|
||||||
|
|
||||||
if (!dict[noteName]) {
|
if (!dict[noteId]) {
|
||||||
text.text = "☒"
|
diagram = noteName + "\n☒"
|
||||||
console.log("Note not in dictionary:", noteName)
|
console.log("Note not in dictionary:", noteId)
|
||||||
} else {
|
} else {
|
||||||
var diagram = buildFingeringText(dict[noteName], userFormatString, noteName)
|
var diagram = buildFingeringText(dict[noteId], userFormatString, noteName)
|
||||||
|
console.log("Note:", noteId, "Diagram:", diagram.replace(/\n/g, "\\n"))
|
||||||
text.text = "<font face=\"" + getFontStack() + "\">" + diagram + "</font>";
|
|
||||||
|
|
||||||
console.log("Note:", noteName, "Diagram:", diagram.replace(/\n/g, "\\n"))
|
|
||||||
}
|
}
|
||||||
|
text.text = "<font face=\"" + getFontStack() + "\">" + diagram + "</font>";
|
||||||
|
|
||||||
cursor.add(text)
|
cursor.add(text)
|
||||||
formatText(text)
|
formatText(text)
|
||||||
text.color = getColor(noteName)
|
const note = noteId.slice(0, -1)
|
||||||
|
text.color = Fingering.getColor(note)
|
||||||
|
|
||||||
// Verify the text object has the properties set
|
// Verify the text object has the properties set
|
||||||
console.log("Text fontSize after format:", text.fontSize)
|
console.log("Text fontSize after format:", text.fontSize)
|
||||||
@ -1041,7 +748,8 @@ MuseScore {
|
|||||||
|
|
||||||
// Build the full font stack dynamically
|
// Build the full font stack dynamically
|
||||||
function getFontStack() {
|
function getFontStack() {
|
||||||
return "WhistleSymbols" + ", " + userFontFamily;
|
// return "WhistleSymbols" + ", " + userFontFamily;
|
||||||
|
return userFontFamily
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1133,11 +841,11 @@ MuseScore {
|
|||||||
var oldProfileFormat = getCurrentFormatString()
|
var oldProfileFormat = getCurrentFormatString()
|
||||||
getHistory().add(
|
getHistory().add(
|
||||||
function() {
|
function() {
|
||||||
// Undo: restore both userFormatString and the profile's formatString
|
// Undo: restore both userFormatString and the profile's format
|
||||||
userFormatString = oldFormat
|
userFormatString = oldFormat
|
||||||
formatInput.text = oldFormat
|
formatInput.text = oldFormat
|
||||||
if (profiles.length > 0) {
|
if (profiles.length > 0) {
|
||||||
profiles[currentProfileIndex].formatString = oldProfileFormat
|
profiles[currentProfileIndex].format = oldProfileFormat
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
@ -1145,7 +853,7 @@ MuseScore {
|
|||||||
userFormatString = format
|
userFormatString = format
|
||||||
formatInput.text = format
|
formatInput.text = format
|
||||||
if (profiles.length > 0) {
|
if (profiles.length > 0) {
|
||||||
profiles[currentProfileIndex].formatString = format
|
profiles[currentProfileIndex].format = format
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"format string"
|
"format string"
|
||||||
@ -1165,10 +873,10 @@ MuseScore {
|
|||||||
// Event handlers (call setters and save)
|
// Event handlers (call setters and save)
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
function formatStringChanged(formatString) {
|
function formatChanged(format) {
|
||||||
getHistory().begin()
|
getHistory().begin()
|
||||||
setModified(true)
|
setModified(true)
|
||||||
setUserFormatString(formatString)
|
setUserFormatString(format)
|
||||||
getHistory().end()
|
getHistory().end()
|
||||||
saveSettings()
|
saveSettings()
|
||||||
}
|
}
|
||||||
@ -1222,7 +930,7 @@ MuseScore {
|
|||||||
function profileChanged(index) {
|
function profileChanged(index) {
|
||||||
if (index < 0 || index >= profiles.length) return
|
if (index < 0 || index >= profiles.length) return
|
||||||
currentProfileIndex = index
|
currentProfileIndex = index
|
||||||
userFormatString = profiles[index].formatString
|
userFormatString = profiles[index].format
|
||||||
formatInput.text = userFormatString
|
formatInput.text = userFormatString
|
||||||
updatePreview()
|
updatePreview()
|
||||||
saveSettings() // immediately save profile change
|
saveSettings() // immediately save profile change
|
||||||
@ -1652,7 +1360,7 @@ MuseScore {
|
|||||||
}
|
}
|
||||||
property var previousText: userFormatString
|
property var previousText: userFormatString
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
formatStringChanged(formatInput.text)
|
formatChanged(formatInput.text)
|
||||||
updatePreview()
|
updatePreview()
|
||||||
}
|
}
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
@ -1662,7 +1370,7 @@ MuseScore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: "Use placeholders like $1, $2, ..., for the holes, \n$+ for the plus indicator and $note for the note (e.g. G#5)"
|
text: "Use placeholders like $1, $2, ..., for the holes and $note for the note (e.g. G#5)"
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
color: textColor
|
color: textColor
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user