Move fingering descriptions to separate file
This allows the user to modify the instruments, without understanding the code. Some documentation is included, and a lot of names are changed to be closer to the domain.
This commit is contained in:
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: {
|
||||
"C4": "11111111",
|
||||
"C#4": "1111111|",
|
||||
"D4": "11111110",
|
||||
"D#4": "111111|o",
|
||||
"E4": "111111oo",
|
||||
"F4": "11110011",
|
||||
"F#4": "1111011o",
|
||||
"G4": "111100oo",
|
||||
"G#4": "111011|o",
|
||||
"A4": "111000oo",
|
||||
"A#4": "110110oo",
|
||||
"B4": "110000oo",
|
||||
"C5": "101000oo",
|
||||
"C#5": "011000oo",
|
||||
"D5": "001000oo",
|
||||
"D#5": "0011111o",
|
||||
"E5": "-11111oo",
|
||||
"F5": "-111101o",
|
||||
"F#5": "-11101oo",
|
||||
"G5": "-11100oo",
|
||||
"G#5": "-11010oo",
|
||||
"A5": "-11000oo",
|
||||
"A#5": "-110011|",
|
||||
"B5": "-11011oo",
|
||||
"C6": "-10011oo",
|
||||
"C#6": "-1011011",
|
||||
"D6": "-1011011",
|
||||
"D#6": "-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 MuseScore 3.0
|
||||
|
||||
import "Fingering.js" as Fingering
|
||||
|
||||
MuseScore {
|
||||
id: mscore
|
||||
version: "4.4"
|
||||
@ -68,7 +70,7 @@ MuseScore {
|
||||
property int userJustification: 1 // 0=left,1=center,2=right
|
||||
property real userOffsetY: 1.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 bool userTransposed: false
|
||||
|
||||
@ -83,73 +85,6 @@ MuseScore {
|
||||
property var history: 0
|
||||
property var modified: false
|
||||
|
||||
//---------------------------------------------------------
|
||||
// FINGERING DICTIONARY
|
||||
//---------------------------------------------------------
|
||||
|
||||
//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#
|
||||
//
|
||||
|
||||
|
||||
// ○ = 0
|
||||
// ● = 1
|
||||
// ◐ = |
|
||||
// ◐ = -
|
||||
// ○ = o
|
||||
|
||||
//Example fingering specification for G#5 on a high D whistle
|
||||
// |---------------------------------------------
|
||||
//windway -> | | | ● ● ◐ ○ ○ ○ |
|
||||
// |---------------------------------------------
|
||||
// "G#5": " 1 1 | 0 0 0
|
||||
// | | | | | |
|
||||
// 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.
|
||||
|
||||
//Whistle definitions
|
||||
property var defaultProfiles: [
|
||||
{
|
||||
"name": "Soprano Recorder",
|
||||
"fingeringDict": {
|
||||
"C4": "11111111",
|
||||
"C#4": "1111111|",
|
||||
"D4": "11111110",
|
||||
"D#4": "111111|o",
|
||||
"E4": "111111oo",
|
||||
"F4": "11110011",
|
||||
"F#4": "1111011o",
|
||||
"G4": "111100oo",
|
||||
"G#4": "111011|o",
|
||||
"A4": "111000oo",
|
||||
"A#4": "110110oo",
|
||||
"B4": "110000oo",
|
||||
"C5": "101000oo",
|
||||
"C#5": "011000oo",
|
||||
"D5": "001000oo",
|
||||
"D#5": "0011111o",
|
||||
"E5": "-11111oo",
|
||||
"F5": "-111101o",
|
||||
"F#5": "-11101oo",
|
||||
"G5": "-11100oo",
|
||||
"G#5": "-11010oo",
|
||||
"A5": "-11000oo",
|
||||
"A#5": "-110011|",
|
||||
"B5": "-11011oo",
|
||||
"C6": "-10011oo",
|
||||
"C#6": "-1011011",
|
||||
"D6": "-1011011",
|
||||
"D#6": "-011011o",
|
||||
},
|
||||
formatString: "$note\n\n$1\n—\n$2\n$3\n$4\n—\n$5\n$6\n$7\n$8"
|
||||
},
|
||||
]
|
||||
|
||||
// Runtime copy of profiles (loaded/saved via preferences)
|
||||
property var profiles: []
|
||||
property int currentProfileIndex: 0
|
||||
@ -160,16 +95,16 @@ MuseScore {
|
||||
|
||||
|
||||
function getCurrentFingeringDict() {
|
||||
return profiles.length > 0 ? profiles[currentProfileIndex].fingeringDict : {}
|
||||
return profiles.length > 0 ? profiles[currentProfileIndex].fingering : {}
|
||||
}
|
||||
|
||||
function getCurrentFormatString() {
|
||||
return profiles.length > 0 ? profiles[currentProfileIndex].formatString : ""
|
||||
return profiles.length > 0 ? profiles[currentProfileIndex].format : ""
|
||||
}
|
||||
|
||||
function setCurrentFormatString(newFormat) {
|
||||
if (profiles.length > 0) {
|
||||
profiles[currentProfileIndex].formatString = newFormat
|
||||
profiles[currentProfileIndex].format = newFormat
|
||||
userFormatString = newFormat
|
||||
}
|
||||
}
|
||||
@ -234,8 +169,8 @@ MuseScore {
|
||||
// Save profiles as JSON string
|
||||
var profilesForStorage = profiles.map(p => ({
|
||||
name: p.name,
|
||||
fingeringDict: p.fingeringDict,
|
||||
formatString: p.formatString
|
||||
fingering: p.fingering,
|
||||
format: p.format
|
||||
}))
|
||||
pluginSettings.storedProfiles = JSON.stringify(profilesForStorage)
|
||||
pluginSettings.storedCurrentProfile = currentProfileIndex
|
||||
@ -257,24 +192,24 @@ MuseScore {
|
||||
var loaded = JSON.parse(profilesStr)
|
||||
profiles = loaded.map(p => ({
|
||||
name: p.name,
|
||||
fingeringDict: p.fingeringDict,
|
||||
formatString: p.formatString
|
||||
fingering: p.fingering,
|
||||
format: p.format
|
||||
}))
|
||||
} catch (e) {
|
||||
console.log("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,
|
||||
fingeringDict: JSON.parse(JSON.stringify(p.fingeringDict)),
|
||||
formatString: p.formatString
|
||||
fingering: JSON.parse(JSON.stringify(p.fingering)),
|
||||
format: p.format
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
profiles = defaultProfiles.map(p => ({
|
||||
profiles = Fingering.woodwinds.map(p => ({
|
||||
name: p.name,
|
||||
fingeringDict: JSON.parse(JSON.stringify(p.fingeringDict)),
|
||||
formatString: p.formatString
|
||||
fingering: JSON.parse(JSON.stringify(p.fingering)),
|
||||
format: p.format
|
||||
}))
|
||||
}
|
||||
|
||||
@ -304,11 +239,11 @@ MuseScore {
|
||||
function resetToDefaults() {
|
||||
getHistory().begin() // if you have undo/redo
|
||||
|
||||
// 1. Restore profiles from defaultProfiles
|
||||
profiles = defaultProfiles.map(p => ({
|
||||
// 1. Restore profiles from woodwinds
|
||||
profiles = Fingering.woodwinds.map(p => ({
|
||||
name: p.name,
|
||||
fingeringDict: JSON.parse(JSON.stringify(p.fingeringDict)),
|
||||
formatString: p.formatString
|
||||
fingering: JSON.parse(JSON.stringify(p.fingering)),
|
||||
format: p.format
|
||||
}))
|
||||
currentProfileIndex = 0
|
||||
|
||||
@ -335,8 +270,8 @@ MuseScore {
|
||||
// 4. Persist all changes to Settings
|
||||
pluginSettings.storedProfiles = JSON.stringify(profiles.map(p => ({
|
||||
name: p.name,
|
||||
fingeringDict: p.fingeringDict,
|
||||
formatString: p.formatString
|
||||
fingering: p.fingering,
|
||||
format: p.format
|
||||
})))
|
||||
pluginSettings.storedCurrentProfile = currentProfileIndex
|
||||
pluginSettings.storedFontSize = userFontSize
|
||||
@ -405,7 +340,7 @@ MuseScore {
|
||||
//---------------------------------------------------------
|
||||
|
||||
//Build fingering text using binary representation of the fingering for a note.
|
||||
function buildFingeringText(binaryString, formatString, noteName) {
|
||||
function buildFingeringText(binaryString, format, noteName) {
|
||||
const open = "0"
|
||||
const closed = "1"
|
||||
const half_ver = "|"
|
||||
@ -414,7 +349,7 @@ MuseScore {
|
||||
if (!binaryString || binaryString.length < 1)
|
||||
return "Invalid fingering pattern. Length is wrong"
|
||||
|
||||
if (!formatString || typeof formatString !== "string")
|
||||
if (!format || typeof format !== "string")
|
||||
return "ERR"
|
||||
|
||||
var holeCount = binaryString.length
|
||||
@ -436,7 +371,7 @@ MuseScore {
|
||||
// Clone format string
|
||||
//-------------------------------------------------
|
||||
|
||||
var output = formatString
|
||||
var output = format
|
||||
|
||||
//-------------------------------------------------
|
||||
// Replace note placeholder ($note) with note name
|
||||
@ -487,24 +422,6 @@ MuseScore {
|
||||
// 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) {
|
||||
console.log("Formatting text - setting fontSize to:", userFontSize)
|
||||
@ -671,7 +588,8 @@ MuseScore {
|
||||
|
||||
cursor.add(text)
|
||||
formatText(text)
|
||||
text.color = getColor(noteName)
|
||||
const note = noteName.slice(0, -1)
|
||||
text.color = Fingering.getColor(note)
|
||||
|
||||
// Verify the text object has the properties set
|
||||
console.log("Text fontSize after format:", text.fontSize)
|
||||
@ -839,11 +757,11 @@ MuseScore {
|
||||
var oldProfileFormat = getCurrentFormatString()
|
||||
getHistory().add(
|
||||
function() {
|
||||
// Undo: restore both userFormatString and the profile's formatString
|
||||
// Undo: restore both userFormatString and the profile's format
|
||||
userFormatString = oldFormat
|
||||
formatInput.text = oldFormat
|
||||
if (profiles.length > 0) {
|
||||
profiles[currentProfileIndex].formatString = oldProfileFormat
|
||||
profiles[currentProfileIndex].format = oldProfileFormat
|
||||
}
|
||||
},
|
||||
function() {
|
||||
@ -851,7 +769,7 @@ MuseScore {
|
||||
userFormatString = format
|
||||
formatInput.text = format
|
||||
if (profiles.length > 0) {
|
||||
profiles[currentProfileIndex].formatString = format
|
||||
profiles[currentProfileIndex].format = format
|
||||
}
|
||||
},
|
||||
"format string"
|
||||
@ -871,10 +789,10 @@ MuseScore {
|
||||
// Event handlers (call setters and save)
|
||||
//---------------------------------------------------------
|
||||
|
||||
function formatStringChanged(formatString) {
|
||||
function formatChanged(format) {
|
||||
getHistory().begin()
|
||||
setModified(true)
|
||||
setUserFormatString(formatString)
|
||||
setUserFormatString(format)
|
||||
getHistory().end()
|
||||
saveSettings()
|
||||
}
|
||||
@ -928,7 +846,7 @@ MuseScore {
|
||||
function profileChanged(index) {
|
||||
if (index < 0 || index >= profiles.length) return
|
||||
currentProfileIndex = index
|
||||
userFormatString = profiles[index].formatString
|
||||
userFormatString = profiles[index].format
|
||||
formatInput.text = userFormatString
|
||||
updatePreview()
|
||||
saveSettings() // immediately save profile change
|
||||
@ -1358,7 +1276,7 @@ MuseScore {
|
||||
}
|
||||
property var previousText: userFormatString
|
||||
onEditingFinished: {
|
||||
formatStringChanged(formatInput.text)
|
||||
formatChanged(formatInput.text)
|
||||
updatePreview()
|
||||
}
|
||||
onTextChanged: {
|
||||
|
||||
Reference in New Issue
Block a user