//============================================================================= // MuseScore // Linux Music Score Editor // $Id:$ // /****************************************************************************** Create chords from selected notes. createchords.js Version: 03.02 Requires: createchords.ui, createchordschange.ui and createchordshelp.ui Opens a window which contains brief details of the current score. The key signature ia shown as per the score but can be changed. The relative positions of the notes in the resulting chords (root, third etc.) can be changed. The notes for all the degrees are shown as they will be created. Individual degrees can be modified via a further window. If no selection has been made the help window is displayed. Note. Only notes in the first voice are processed. History: January, 2012 01.01 First full release Minor keys shown in key drop down alongside major. Cosmetic. Changed calls to newNote to include 3rd argument. Cosmetic. Renamed function myInterval as myHalfNotePosition. Cosmetic. Changed myHalfNotePosition for minor key. Cosmetic. Window changed to show selected measures numbers. Added function mySelectedMeasureNumbers for the above. Cosmetic. Score title in bold dark green. Added function myQColouredText for the above. Save & Reset using the note colour i.e. original notes set to 0,0,1 on saving - reset removes all other notes and then sets the originals back to 0,0,0 (black). Also Save gives warning if more than one note found in a chord. February, 2012 02.01 Second full release (with the above) Internal cosmetic changes to variable and function names etc. Redesign of main form/window: Corrected the warning message when staff 0 selected. Major, minor common or minor natural key. (Minor common is used by traditional composers and is the minor natural with the 5th degree triad 2nd note raised a semitone and the 7th degree 1st note raised a semitone.) Select triad or seventh. Show chord chart of all chords for key, sequence etc. Highlight changed notes. New form/window to allow the notes of a single chord to be changed. Changes are calculated as part of the forms and so the actual code for the production of the chords is greatly simplified. jheGetNoteText semitone flat Cb corrected to B. Changes for minor common made to to: jheGetNoteText jheSetColour New functions: jheGetValueFromColour jheStoreInColour Save now preserves the pitch of the original note in its colour where r = pitch divided 25, g is the remainder of that divided by 5 and b is the remainder of that. Reset removes all notes with colour 0,0,0 in the selection. Other notes have their pitch restored and their colour set back to 0,0,0 (black). May, 2012 03.01 Third full release (with the above) Warning: Save cannot save a pitch of 0. Reset does not remove notes in chords of less than 3 notes. Corrected reset when notes already non-black e.g. when out of range May, 2012 03.02 Correction release (with above) Note. All functions beginning with 'jhe' are part of my 'library' of functions and may be in use in other plugins. They should be grouped together at the end of this file. Author: John H. Everington Special thanks to Marc Sabatella for his brains to my braun. *********************************************************************************/ // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2. // // 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, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //============================================================================= // This is ECMAScript code (ECMA-262 aka "Java Script") //============================================================================= var g_title = "Create Chords From Notes"; var g_keySig; // Key signature as -7 (Cb) to +7 (C#) var g_keyType; // Key type: 0 Major, 1 Minor Common or 2 Minor Natural var g_triad7th; // 0 if triad, 1 if seventh var g_formMain; // Main form var g_formDegree; // Form for changing a chord var g_firstMeasure; // Number of first measure selected var g_lastMeasure; // Number of last measure selected var g_ignoreChange; // Set true when a combobox routine changes another combobox // Chord Chart Variables var g_staffPosX = 80; // Position of each staff line x -> var g_staffFirstPosY = 230; // Position of first position y var g_staffPosGapY = 10; // Gap between positions (twice this is gap between lines) var g_staffPosGapX = 30; // Gap between each chord var g_degreeToChange; // The degree chosen to change via g_formDegree (0 to 6) var g_type = ["Major", "Minor (common)", "Minor (natural)"]; var g_seqPosition = [0, 1, 2, 3]; // Current RTFS of each button ie [0] = 3 first has S var g_RTFS = ["R", "T", "F", "S"]; // Chord table values are changed dependent on key, sequence etc. var g_chords = [[0, 0, 0, 0], // 1 Tonic [0][0], [0][1], [0][2], [0][3] [0, 0, 0, 0], // 2 Supertonic [1][0], [1][1], [1][2], [1][3] [0, 0, 0, 0], // 3 Mediant [2][0], [2][1], [2][2], [2][3] [0, 0, 0, 0], // 4 Subdominant [3][0], [3][1], [3][2], [3][3] [0, 0, 0, 0], // 5 Dominant [4][0], [4][1], [4][2], [4][3] [0, 0, 0, 0], // 6 Submediant [5][0], [5][1], [5][2], [5][3] [0, 0, 0, 0]]; // 7 Leading/Subtonic [6][0], [6][1], [6][2], [6][3] // Chord status: 0 Normal, 1 Not used (seventh) or 2 Changed var g_status = [[0, 0, 0, 1], // As g_chords [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1]]; // Saved root notes var g_roots = [0, 0, 0, 0, 0, 0, 0]; // Colour corresponding to q_status setting (0 and 2 are shown bold) var g_colour = ["00007f", "a0a0ff", "ff8040"]; // Semitone positions relative to a key var g_semiPos = [ // Major key [0 Major, 1 Minor] [[ 0, 4, 7, 11], // 1 [0][0][0], [0][0][1], [0][0][2], [0][0][3] [ 2, 5, 9, 12], // 2 [0][1][0], [0][1][1], [0][1][2], [0][1][3] [ 4, 7, 11, 14], // 3 [0][2][0], [0][2][1], [0][2][2], [0][2][3] [ 5, 9, 12, 16], // 4 [0][3][0], [0][3][1], [0][3][2], [0][3][3] [ 7, 11, 14, 17], // 5 [0][4][0], [0][4][1], [0][4][2], [0][4][3] [ 9, 12, 16, 19], // 6 [0][5][0], [0][5][1], [0][5][2], [0][5][3] [11, 14, 17, 21] // 7 [0][6][0], [0][6][1], [0][6][2], [0][6][3] ], // Minor common key [[ 0, 3, 7, 10], // 1 [1][0][0], [1][0][1], [1][0][2], [1][0][3] [ 2, 5, 8, 12], // 2 [1][1][0], [1][1][1], [1][1][2], [1][1][3] [ 3, 7, 10, 14], // 3 [1][2][0], [1][2][1], [1][2][2], [1][2][3] [ 5, 8, 12, 15], // 4 [1][3][0], [1][3][1], [1][3][2], [1][3][3] [ 7, 11, 14, 17], // 5 [1][4][0], [1][4][1], [1][4][2], [1][4][3] [ 8, 12, 15, 19], // 6 [1][5][0], [1][5][1], [1][5][2], [1][5][3] [11, 14, 17, 20] // 7 [1][6][0], [1][6][1], [1][6][2], [1][6][3] ], // Minor natural key [[ 0, 3, 7, 10], // 1 [1][0][0], [1][0][1], [1][0][2], [1][0][3] [ 2, 5, 8, 12], // 2 [1][1][0], [1][1][1], [1][1][2], [1][1][3] [ 3, 7, 10, 14], // 3 [1][2][0], [1][2][1], [1][2][2], [1][2][3] [ 5, 8, 12, 15], // 4 [1][3][0], [1][3][1], [1][3][2], [1][3][3] [ 7, 10, 14, 17], // 5 [1][4][0], [1][4][1], [1][4][2], [1][4][3] [ 8, 12, 15, 19], // 6 [1][5][0], [1][5][1], [1][5][2], [1][5][3] [10, 14, 17, 20] // 7 [1][6][0], [1][6][1], [1][6][2], [1][6][3] ]]; // Keys used to seed key combo box - reverse order of g_KeySig var g_combiKeys = [["C#", "F#", "B", "E", "A", "D", "G", // Major "C", "F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb"], ["a#", "d#", "g#", "c#", "f#", "b", "e", // Minor "a", "d", "g", "c", "f", "bb", "eb", "ab"]]; var g_Degrees = [["I", "ii", "iii", "IV", "V", "vi", "vii"], // Major ["i", "ii", "III", "iv", "V", "VI", "vii"], // Minor common ["i", "ii", "III", "iv", "V", "VI", "VII"]]; // Minor natural var g_degreeName = [["I Tonic", "ii Supertonic", "iii Mediant", // Major "IV SubDominant", "V Dominant", "vi Submediant", "vii Leading"], ["i Tonic", "ii Supertonic", "III Mediant", // Minor common "iv SubDominant", "V Dominant", "VI Submediant", "vii Subtonic"], ["i Tonic", "ii Supertonic", "III Mediant", // Minor natural "iv SubDominant", "V Dominant", "VI Submediant", "VII Subtonic"]]; var g_cChord = [0, 0, 0, 0]; // Chord to change var g_cChordOrig = [0, 0, 0, 0]; // Original chord before change function init() { // This function is run at the start up of MuseScore. }; function run() { // This function is run when the plugin is selected. var evalString; var n, y; g_keySig = curScore.keysig; g_keyType = 0; // Major key by default g_triad7th = 0; // Triad by default // Verify selection var c = new Cursor(curScore); c.goToSelectionStart(); if (c.eos()) { // Nothing selected so show help helpWanted(); return; } // Load main form g_formMain = jheLoadForm(pluginPath, "createchords.ui"); // Details including selection if (curScore.title.length == 0) { jheQColouredText(g_formMain.textTitle, "376f00", "Untitled", true); } else {jheQColouredText(g_formMain.textTitle, "376f00", curScore.title, true);} g_formMain.textStaves.setText("Staves: " + curScore.staves); g_formMain.textMeasures.setText("Measures: " + curScore.measures); if (c.staff == 0) {g_formMain.textSelected.setText("WARNING! Staff 0 selected");} else {g_formMain.textSelected.setText("Staff selected: " + c.staff);} jheSelectedMeasureNumbers(c, false); g_formMain.textMSelected.setText("Measures selected: " + g_firstMeasure + " to " + g_lastMeasure); // Save and reset g_formMain.pushSave.clicked.connect(saveButton); // Connect the Save Button function g_formMain.pushReset.clicked.connect(resetButton); // Connect the Reset Button function // Key jheSetCombo(g_formMain.comboKey, true, g_combiKeys[0], 15, 14 - (curScore.keysig + 7)); g_formMain.comboKey["currentIndexChanged(int)"].connect(keyChanged); // Key type - default is major key jheSetCombo(g_formMain.comboKeyType, true, g_type, 3, 0); g_formMain.comboKeyType["currentIndexChanged(int)"].connect(keyTypeChanged); // Triad or seventh - default is triad g_formMain.groupTriad7th.radioTriad.clicked.connect(triad7thChanged); g_formMain.groupTriad7th.radio7th.clicked.connect(triad7thChanged); // Overall Sequence displayRTFS(); // Show depending on triad/seventh selection g_formMain.comboSeq1["currentIndexChanged(int)"].connect(seq1Changed); g_formMain.comboSeq2["currentIndexChanged(int)"].connect(seq2Changed); g_formMain.comboSeq3["currentIndexChanged(int)"].connect(seq3Changed); g_formMain.comboSeq4["currentIndexChanged(int)"].connect(seq4Changed); g_formMain.textInv.setText(""); // Inversion text g_ignoreChange = false; // Staff // Position each main line y = g_staffFirstPosY + (g_staffPosGapY * 5); for (n = 0; n < 5; n++) { // g_formMain.staff++n.move(g_staffPosX, y); evalString = "g_formMain.staff" + (n + 1) + ".move(g_staffPosX, y);"; eval (evalString); y = y + (g_staffPosGapY * 2); } setupLegerLines(); // Ledger lines e.g. middle C // Chords updateChords(); // Update g_chordn dependent on key, sequence etc displayChords(); // Show chord chart using g_chordn // Scale degree buttons g_formMain.pushDegree1.clicked.connect(degree1Wanted); g_formMain.pushDegree2.clicked.connect(degree2Wanted); g_formMain.pushDegree3.clicked.connect(degree3Wanted); g_formMain.pushDegree4.clicked.connect(degree4Wanted); g_formMain.pushDegree5.clicked.connect(degree5Wanted); g_formMain.pushDegree6.clicked.connect(degree6Wanted); g_formMain.pushDegree7.clicked.connect(degree7Wanted); // OK, Cancel and Help g_formMain.buttonBox.accepted.connect(chordOK); // Connect the OK Button function g_formMain.pushHelp.clicked.connect(helpWanted); // Connect the Help Button function // Show form g_formMain.show(); return; }; function displayRTFS() { // Set the Overall Sequence combo boxes and chord sequence titles according to triad or seventh var num; if (g_triad7th == 0) { // Triad num = 3; // Show 3 entries (RTF) g_formMain.comboSeq4.setEnabled(false); // Disable S Button } else { // Seventh num = 4; // Show 4 entries (RTFS) g_formMain.comboSeq4.setEnabled(true); // Enable S Button } jheSetCombo(g_formMain.comboSeq1, true, g_RTFS, num, g_seqPosition[0]); jheSetCombo(g_formMain.comboSeq2, true, g_RTFS, num, g_seqPosition[1]); jheSetCombo(g_formMain.comboSeq3, true, g_RTFS, num, g_seqPosition[2]); jheSetCombo(g_formMain.comboSeq4, true, g_RTFS, 4, g_seqPosition[3]); return; }; function setupLegerLines() { // Ledger lines e.g. middle C var n, m, p; var evalString; for (n = 0; n < 7; n++) { for (m = 0; m < 4; m++) { // Line number of each ledger line switch (m) { case 0: p = 1; // C (Top) break; case 1: p = 3; // A break; case 2: p = 15; // Middle C break; case 3: p = 17; // A (Bottom) break; } // Move each ledger line // g_formMain.staffLedger++n++m.move( // g_staffPosX + 6 + (g_staffPosGapX * n), // g_staffFirstPosY + (g_staffPosGapY * p)); evalString = "g_formMain.staffLedger" + (n + 1) + (m + 1) + ".move(g_staffPosX + 6 + (g_staffPosGapX * n)," + "g_staffFirstPosY + (g_staffPosGapY * p));"; eval (evalString); } } hideLedgers(); // Hide each ledger line return; }; function hideLedgers() { // Hide each ledger line and warning var n, m; var evalString; // Hide ledger lines for (n = 0; n < 7; n++) { for (m = 0; m < 4; m++) { // g_formMain.staffLedger++n++m.hide(); evalString = "g_formMain.staffLedger" + (n + 1) + (m + 1) + ".hide();"; eval (evalString); } } // Hide warning g_formMain.labelWarn.hide(); return; }; function helpWanted() { // Help pressed on main window or called when nothing selected formHelp = jheLoadForm(pluginPath, "createchordshelp.ui"); formHelp.pushContinue.accepted.connect(helpContinue); // Connect the helpContinue function formHelp.show(); return; }; function helpContinue() { // Continue pressed on help form formHelp.close(); return; }; function saveButton() { // Save button pressed create a new invisible, silent note to hold the original notes veleocity as // its color.green. // Output a warning if the selection contains chords with more than one note var endTick; var more = false; var c = new Cursor(curScore); var chord; var curNote; c.voice = 0; // Only one voice c.goToSelectionEnd(); endTick = c.tick(); // If no selection, this will go to end of score c.goToSelectionStart(); if (c.eos()) {c.rewind();} // If no selection, start at beginning of score curScore.startUndo(); while (c.tick() < endTick) { if (c.isChord()) { // For each chord within the staff chord = c.chord(); if (chord.notes > 1) {myMore = true;} // More than one note in chord curNote = chord.note(0); // Store the current pitch in the colour if (curNote.pitch > 0) {jheStoreInColour(curNote, curNote.pitch);} } c.next(); } curScore.endUndo(); if (more) {jheMessage("Multiple notes found!");} g_formMain.pushSave.setEnabled(false); // Disable Save return; }; function resetButton() { // Reset button pressed so remove each note whose colour is 0,0,0 (black). // Others have their pitch reset (from the colour) and the colour then set to black. var endTick; var c = new Cursor(curScore); var chord; var curNote; var saveNote; var i, n, x; c.voice = 0; c.goToSelectionEnd(); endTick = c.tick(); // If no selection, this will go to end of score c.goToSelectionStart(); if (c.eos()) {c.rewind();} // If no selection, start at beginning of score curScore.startUndo(); while (c.tick() < endTick) { if (c.isChord()) { // For each chord within the staff chord = c.chord(); if (chord.notes < 3) { c.next(); continue; } // Check if a saved note present (only 1) saveNote = -1; for (i = 0; i < chord.notes; i++) { curNote = chord.note(i); // If colour = pitch, it is 1 to 127 if (curNote.color.red() == 0 && curNote.color.green() == 0 && curNote.color.blue() == 0) {continue;} // Saved note has colour set to its original pitch if (curNote.color.red() < 6 && curNote.color.green() < 5 && curNote.color.blue() < 5) { if (saveNote != -1) { // More than one! saveNote == -1; break; } else {saveNote = i;} } } if (saveNote == -1) { // No saved note or more than one c.next(); continue; } // N.B. Removing a note shifts the other's positions n = chord.notes; x = 0; for (i = 0; i < n; i++) { curNote = chord.note(x); if (i == saveNote) { // Original note - so restore curNote.pitch = jheGetValueFromColour(curNote); jheSetColour(curNote, 0, 0, 0); // Change back to black x++; } else {chord.removeNote(x);} // Added note so delete } } c.next(); } curScore.endUndo(); return; }; function keyChanged(myKey) { // Combobox key changed: set global key signature as -7 (Cb) to +7 (C#) // myKey Key signature as 0 (C#), .... 7 (C), ..... 14 (Cb) g_keySig = (14 - myKey) - 7; updateChords(); // Update g_chordn dependent on key, sequence etc displayChords(); // Show chord chart using g_chordn return; }; function keyTypeChanged(myType) { // Combobox key type changed: between Major or Minor (natural) // myType 0 major, 1 Minor (common), 2 Minor (natural) var n; g_keyType = myType; if (myType == 2) {n = 1;} else {n = myType;} jheSetCombo(g_formMain.comboKey, true, g_combiKeys[n], 15, 14 - (g_keySig + 7)); updateChords(); // Update g_chordn dependent on key, sequence etc displayChords(); // Show chord chart using g_chordn return; }; function triad7thChanged() { // Change between triad and seventh var m, s; if (g_formMain.groupTriad7th.radioTriad.checked) { // Triad g_triad7th = 0; s = 1; // Seventh not available (triad) } else { // Seventh wanted g_triad7th = 1; s = 0; // Seventh available } g_seqPosition[0] = 0; g_seqPosition[1] = 1; g_seqPosition[2] = 2; g_seqPosition[3] = 3; // Set status of seventh for (m = 0; m < 7; m++) { g_status[m][3] = s; } g_ignoreChange = true; // Ignore next change displayRTFS(); // Set sequence combos updateChords(); // Update g_chordn dependent on key, sequence etc displayChords(); // Show chord chart using g_chordn return; }; function seq1Changed(myPos) { // Combobox seq1 changed: set first note sequence // myPos Position in list // Change the text for inversion switch (myPos) { case 0: g_formMain.textInv.setText(""); break; case 1: g_formMain.textInv.setText("First inversion"); break; case 2: g_formMain.textInv.setText("Second inversion"); break; case 3: g_formMain.textInv.setText("Third inversion"); break; } if (g_ignoreChange) { // This is a 2nd change (result of first change) so ignore g_ignoreChange = false; return; } g_ignoreChange = true; // Ignore next change sequenceChanged(0, myPos); return; }; function seq2Changed(myPos) { // Combobox seq2 changed: set second note sequence // myPos Position in list if (g_ignoreChange) { // This is a 2nd change (result of first change) so ignore g_ignoreChange = false; return; } g_ignoreChange = true; // Ignore next change sequenceChanged(1, myPos); return; }; function seq3Changed(myPos) { // Combobox seq3 changed: set third note sequence // myPos Position in list if (g_ignoreChange) { // This is a 2nd change (result of first change) so ignore g_ignoreChange = false; return; } g_ignoreChange = true; // Ignore next change sequenceChanged(2, myPos); return; }; function seq4Changed(myPos) { // Combobox seq4 changed: set fourth note sequence // myPos Position in list if (g_ignoreChange) { // This is a 2nd change (result of first change) so ignore g_ignoreChange = false; return; } g_ignoreChange = true; // Ignore next change sequenceChanged(3, myPos); return; }; function sequenceChanged(mySeq, myPos) { // A combobox sequence changed: set first sequence // mySeq Sequence which has been changed (from zero) ie which button // myPos Position in list ie chosen letter var n; // Find which sequence currently has this ones new position for (var n = 0; n < 4; n++) { if (g_seqPosition[n] == myPos) { // Set it to this ones old position g_seqPosition[n] = g_seqPosition[mySeq]; switch(n) { case 0: g_formMain.comboSeq1.setCurrentIndex(g_seqPosition[n]); break; case 1: g_formMain.comboSeq2.setCurrentIndex(g_seqPosition[n]); break; case 2: g_formMain.comboSeq3.setCurrentIndex(g_seqPosition[n]); break; case 3: g_formMain.comboSeq4.setCurrentIndex(g_seqPosition[n]); break; } break; } } g_seqPosition[mySeq] = myPos; updateChords(); // Update g_chordn dependent on key, sequence etc displayChords(); // Show chord chart using g_chordn return; }; function updateChords() { // Update the chord table var i, n, m; var type; var note; var rootNote; var rootPos; var prev; var chord = [0, 0, 0, 0]; var c7th = [0, 0, 0, 0]; for (m = 0; m < 7; m++) { // Move all notes for a chord to chord for (n = 0; n < 4; n++) { note = g_semiPos[g_keyType][m][g_seqPosition[n]]; chord[n] = note; if (g_seqPosition[n] == 0) { // Root note rootNote = note; rootPos = n; g_roots[m] = note; } i = g_status[m][n]; if (i != 1) {continue;} // Seventh in use // Seventh not in use chord[n] = -999; c7th[n] = note; } // Adjust so in ascending sequence (leaving root the same) // Notes after the root should be greater prev = rootNote; for (n = rootPos; n < 4; n++) { if (chord[n] == -999) {continue;} if (chord[n] < prev) { chord[n] = chord[n] + 12; // Move up an octave } prev = chord[n]; } // Notes before the root should be less prev = rootNote; for (n = rootPos; n > -1; n--) { if (chord[n] == -999) {continue;} if (chord[n] > prev) { chord[n] = chord[n] - 12; // Move down an octave } prev = chord[n]; } for (n = 0; n < 4; n++) { // Move chord to chord table if (chord[n] == -999) { // Seventh ignored so put back for use in change cord chord[n] = c7th[n]; } g_chords[m][n] = chord[n]; } } return; }; function displayChords() { // Display the chord table var i, n, m, p, y; var pos = [0, 0, 0, 0]; var tooHigh; var tooLow; var type; var evalString; hideLedgers(); // Hide each ledger line and top/bottom warnings for (m = 0; m < 7; m++) { // Get the position of all 4 notes to see if they fit the chart tooHigh = false; tooLow = false; for (n = 0; n < 4; n++) { i = g_status[m][n]; if (i != 1) { // Normal or changed pos[n] = 15 - jheStaffPosition( g_keySig + 7, g_keyType, g_chords[m][g_seqPosition[n]]); if (pos[n] < 0) {tooLow = true;} if (pos[n] > 18) {tooHigh = true;} } else {pos[n] = 0;} // Ignore seventh } // See if outside range if (tooLow && !tooHigh) { // Too low - so move all up an octave for (n = 0; n < 4; n++) { pos[n] = pos[n] + 7; } } if (!tooLow && tooHigh) { // Too high - so move all down an octave for (n = 0; n < 4; n++) { pos[n] = pos[n] - 7; } } // If both too low and too high it doesnt fit at top or at bottom - so just restrict for (n = 0; n < 4; n++) { // Set warnings if notes not on chart if (pos[n] < 0) { pos[n] = 0; g_formMain.labelWarn.show(); // Warning } if (pos[n] > 18) { pos[n] = 18; g_formMain.labelWarn.show(); // Warning } } // Show each note in the chord for (n = 0; n < 4; n++) { i = g_status[m][n]; if (i == 1) { // Ignore seventh // g_formMain.text++m++n.hide(); eval ("g_formMain.text" + (m + 1) + (n + 1) + ".hide();"); continue; } // g_formMain.text++m++n.show(); eval ("g_formMain.text" + (m + 1) + (n + 1) + ".show();"); // Set the text of the note displayNoteText(m, n, 0); // Position the note y = g_staffFirstPosY + (g_staffPosGapY * pos[g_seqPosition[n]]); // g_formMain.text++m++n.move(g_staffPosX + 10 + (g_staffPosGapX * m), y); evalString = "g_formMain.text" + (m + 1) + (n + 1) + ".move(g_staffPosX + 10 + (g_staffPosGapX * m), y);"; eval (evalString); // Show ledger lines eg middle C // g_formMain.staffLedger++nx.show(); evalString = "g_formMain.staffLedger" + (m + 1); if (pos[n] < 2) {eval (evalString + 1 + ".show();");} if (pos[n] < 4) {eval (evalString + 2 + ".show();");} if (pos[n] > 14) {eval (evalString + 3 + ".show();");} if (pos[n] > 16) {eval (evalString + 4 + ".show();");} } } // Set the button text with scale degrees for (m = 0; m < 7; m++) { // g_formMain.pushDegree++m.text = g_Degrees[g_keyType][m]; evalString = "g_formMain.pushDegree" + (m + 1) + ".text = " + "g_Degrees[g_keyType][m]"; eval (evalString); } return; }; function displayNoteText(myDegree, myNote, myMode) { // Display the note text in either the main chord chart or the change chord window // myDegree The degree 0 to 7 // myNote The note 0 to 3 // myMode 0 - main chord chart, 1 - change chordwindow var i; var colour; var bold; i = g_status[myDegree][myNote]; colour = g_colour[i]; if (i == 1) {bold = false;} else {bold = true;} if (myMode == 0) { // Main form chord chart jheQColouredText(eval ("g_formMain.text" + (myDegree + 1) + (myNote + 1)), colour, jheGetNoteText(g_keySig + 7, g_chords[myDegree][myNote], g_keyType), bold); } else { // Chord change form jheQColouredText(eval ("g_formDegree.textNote" + (myNote + 1)), colour, jheGetNoteText(g_keySig + 7, g_cChord[myNote], g_keyType), bold); } return; }; function degree1Wanted() { // Degree 1 button selected g_degreeToChange = 0; degreeChange(); return; }; function degree2Wanted() { // Degree 2 button selected g_degreeToChange = 1; degreeChange(); return; }; function degree3Wanted() { // Degree 3 button selected g_degreeToChange = 2; degreeChange(); return; }; function degree4Wanted() { // Degree 4 button selected g_degreeToChange = 3; degreeChange(); return; }; function degree5Wanted() { // Degree 5 button selected g_degreeToChange = 4; degreeChange(); return; }; function degree6Wanted() { // Degree 6 button selected g_degreeToChange = 5; degreeChange(); return; } function degree7Wanted() { // Degree 7 button selected g_degreeToChange = 6; degreeChange(); return; }; function degreeChange() { // Change notes for a degree via a new window // g_degreeToChange contains the degree nummber from 0 to 6 var type; var start; var end; var evalString; var n, m, s; // Copy the chord for the degree (one to change and original for comparison) for (n = 0; n < 4; n++) { g_cChord[n] = g_chords[g_degreeToChange][n]; // Copy chord g_cChordOrig[n] = g_chords[g_degreeToChange][n]; // Copy chord } // Load change form g_formDegree = jheLoadForm(pluginPath, "createchordschange.ui"); for (n = 0; n < 4; n++) { // Set the text of the note and the semitone changeNoteText(g_seqPosition[n]); } // Up/down buttons g_formDegree.pushUp1.clicked.connect(upSemitone1); g_formDegree.pushUp2.clicked.connect(upSemitone2); g_formDegree.pushUp3.clicked.connect(upSemitone3); g_formDegree.pushUp4.clicked.connect(upSemitone4); g_formDegree.pushDown1.clicked.connect(downSemitone1); g_formDegree.pushDown2.clicked.connect(downSemitone2); g_formDegree.pushDown3.clicked.connect(downSemitone3); g_formDegree.pushDown4.clicked.connect(downSemitone4); set7thUpDown(); // Stop use of 7th up/down if not in use // triad to 7th to triad button g_formDegree.push7th.clicked.connect(triadTo7th); rootChanged(); // Show details if root changed // Degree chosen to change // g_formDegree.textDegree.setText(g_degreeName[g_keyType][g_degreeToChange]); evalString = "g_formDegree.textDegree.setText(" + "g_degreeName[g_keyType][g_degreeToChange]);"; eval (evalString); // OK and Cancel g_formDegree.buttonBox.accepted.connect(degreeChangeOK); // Connect the OK Button function g_formDegree.buttonBox.rejected.connect(degreeChangeNo); // Connect the OK Button function g_formDegree.rejected.connect(degreeChangeNo); // Connect the Close function // Show change form and disable main g_formDegree.show(); g_formMain.setEnabled(false);; // Stop use of main form return; }; function changeNoteText(myNote) { // Set the text of the change note // myNote Note position (0 to 3) var evalString; displayNoteText(g_degreeToChange, myNote, 1); // Set the semitone // g_formDegree.textSemi++n.setText(g_cChord[myNote]); evalString = "g_formDegree.textSemi" + (myNote + 1) + ".setText(g_cChord" + "[" + myNote + "]);"; eval (evalString); rootChanged(); // Show details if root changed return; }; function upSemitone1() { // Up semitone button 1 g_cChord[g_seqPosition[0]]++; // Change note changeNoteText(g_seqPosition[0]); return; }; function upSemitone2() { // Up semitone button 2 g_cChord[g_seqPosition[1]]++; // Change note changeNoteText(g_seqPosition[1]); return; }; function upSemitone3() { // Up semitone button 3 g_cChord[g_seqPosition[2]]++; // Change note changeNoteText(g_seqPosition[2]); return; }; function upSemitone4() { // Up semitone button 4 g_cChord[g_seqPosition[3]]++; // Change note changeNoteText(g_seqPosition[3]); return; }; function downSemitone1() { // Down semitone button 1 g_cChord[g_seqPosition[0]]--; // Change note changeNoteText(g_seqPosition[0]); return; }; function downSemitone2() { // Down semitone button 2 g_cChord[g_seqPosition[1]]--; // Change note changeNoteText(g_seqPosition[1]); return; }; function downSemitone3() { // Down semitone button 3 g_cChord[g_seqPosition[2]]--; // Change note changeNoteText(g_seqPosition[2]); return; }; function downSemitone4() { // Down semitone button 4 g_cChord[g_seqPosition[3]]--; // Change note changeNoteText(g_seqPosition[3]); return; }; function degreeChangeOK() { // Degree note(s) changed var n; for (n = 0; n < 4; n++) { if (g_cChordOrig[n] == g_cChord[n]) {continue;} // Note has changed so replace and change colour g_chords[g_degreeToChange][n] = g_cChord[n]; g_status[g_degreeToChange][n] = 2; } g_formMain.setEnabled(true);; // Allow use of main form displayChords(); // Show chord chart using g_chordn return; }; function degreeChangeNo() { // Degree note(s) not changed g_formMain.setEnabled(true);; // Allow use of main form return; }; function triadTo7th() { // 7th to triad to 7th button var s; s = g_status[g_degreeToChange][3]; if (s == 1) { // Currently triad so switch to seventh g_status[g_degreeToChange][3] = 0; g_formDegree.push7th.text = "Triad"; } else { // Currently seventh so switch to triad g_status[g_degreeToChange][3] = 1; g_formDegree.push7th.text = "7th"; } set7thUpDown(); // Set use of 7th up/down buttons return; } function set7thUpDown() { // Set use of 7th up/down buttons var s; s = g_status[g_degreeToChange][3]; if (s == 1) { g_formDegree.pushUp4.setEnabled(false); g_formDegree.pushDown4.setEnabled(false); } else { g_formDegree.pushUp4.setEnabled(true); g_formDegree.pushDown4.setEnabled(true); } return; }; function rootChanged() { // Display original root if it has changed var s; s = g_status[g_degreeToChange][0]; if (s == 2) { // Root changed so show // Root note and semitone g_formDegree.textRoot.setText(jheGetNoteText(g_keySig + 7, g_roots[g_degreeToChange], g_keyType)); g_formDegree.textRootSemi.setText(g_roots[g_degreeToChange]); g_formDegree.labelRoot.show(); g_formDegree.textRoot.show(); g_formDegree.textRootSemi.show(); } else { // Hide g_formDegree.labelRoot.hide(); g_formDegree.textRoot.hide(); g_formDegree.textRootSemi.hide(); } return; }; function chordOK() { // OK button pressed on main form - so apply chord changes // Go Through The Current Selection (selected staff, one voice) var i,m, n; var c = new Cursor(curScore); var endTick; var chord; var curNote; var root; var noteNotFound = false; var newChord = [0, 0, 0, 0]; var status = [0, 0, 0, 0]; // Chord status: 0 Normal, 1 Not used (seventh) or 2 Changed c.voice = 0; c.goToSelectionEnd(); endTick = c.tick(); // If no selection, this will go to end of score c.goToSelectionStart(); if (c.eos()) {c.rewind();} // If no selection, start at beginning of score curScore.startUndo(); while (c.tick() < endTick) { if (c.isChord()) { // For each chord within the selection chord = c.chord(); curNote = chord.note(0); // Assume only one note in the chord root = jheHalfNotePosition(curNote.pitch, g_keySig, g_keyType); for (m = 0; m < 7; m++) { if (root == g_roots[m] % 12) {break;} } if (m == 7) { // Note is not the root note of a chord in the key noteNotFound = true; c.next(); continue; } // Copy chord and status for (n = 0; n < 4; n++) { newChord[n] = g_chords[m][g_seqPosition[n]]; status[n] = g_status[m][g_seqPosition[n]]; } // Adjust root note curNote.pitch = curNote.pitch - root + newChord[g_seqPosition[0]]; // Add rest of chord for (n = 1; n < 4; n++) { if (status[n] == 1) {continue;} jheNewNote(chord, curNote.pitch - root + newChord[g_seqPosition[n]]); } } c.next(); } curScore.endUndo(); if(noteNotFound) { jheMessage( "WARNING: at least one note has been found that isn't the root of a chord in this key!"); } return; }; /******************************************************************************************** My Function Library ********************************************************************************************/ function jheChordType(myInterval) { // Returns the type of triad chord: 0 major, 1 minor, 2 diminished or 3 other // myInterval is the position from the root/tonic if (myInterval == 0 || myInterval == 5 || myInterval == 7) {return 0;} if (myInterval == 2 || myInterval == 4 || myInterval == 9) {return 1;} if (myInterval == 11) {return 2;} return 3; }; function jheGetNoteText(myRoot, myPlus, myKeyType) { // Return the text of a note // myRoot Root note e.g. g_keySig + 7 // myPlus Plus semitones // myKeyType 0 - major, 1 minor commom, 2 minor natural // ..KeyStart is where the root (g_KeySig + 7) is in ...SemiTones // curScore.keysig: Cb Gb Db Ab Eb Bb F C G D A E B F# C# // ab eb bb f c g d a e b f# c# g# d# a# var keyStart = [[11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1], // Major [ 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1, 8, 3, 10]]; // Minor common/natural // semiTones holds the text to be returned: Sharp where the key contains sharps etc. var semiTones = [["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"], // Sharp ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"]]; // Flat var n, x; if (myKeyType == 2) {x = 1;} else {x = myKeyType;} n = keyStart[x][myRoot]; while (myPlus < 0) {myPlus = myPlus + 12;} n = n + myPlus; n = n % 12; if (myRoot < 7) {return semiTones[1][n];} return semiTones[0][n]; }; function jheGetValueFromColour(myNote) { // Return a value stored in the note colour (by jheStoreInColour) // myNote The note return (myNote.color.red() * 25) + (myNote.color.green() * 5) + (myNote.color.blue()); }; function jheHalfNotePosition(myPitch, myKeySig, myKeyType) { // Returns the half note position in the key (0 is the root/tonic, 2 supertonic, etc ) // myPitch is the MIDI pitch of the note // myKeySig is the key signature as -7 (Cb/g#) to +7 (C#/bb) // myKeyType is 0 major, 1 minor (common), 2 minor (natural) // Needs: jheKeyNote var n = myPitch % 12; // 0 is C, 1 is C# .... 11 is B var r; r = jheKeyNote(myKeySig, myKeyType); if (n < r) {n = n + 12;} return (n - r); }; function jheKeyNote(myKeySig, myKeyType) { // Returns the key as a MIDI pitch with C major being 0 // myKeySig is the key signature as -7 (Cb/g#) to +7 (C#/bb) // myKeyType is 0 major, 1 minor (common), 2 minor (natural) // Major keys Cb Gb Db G# D# A# F C G D A E B F# C# var keyMaj = [11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1]; // Minor keys g# d# a# f c g d a e b f# c# ab eb bb var keyMin = [ 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1, 8, 3, 10]; if (myKeyType == 0) {return(keyMaj[myKeySig + 7]);} else {return(keyMin[myKeySig + 7]);} }; function jheLoadForm(myPath, myFormName) { // Returns a form after opening and loading it // myPath Path to form e.g. pluginPath // myFormName Name and extension of form e.g. "name.iu" var loader = new QUiLoader(null); var file = new QFile(myPath + "/"+myFormName); file.open(QIODevice.OpenMode(QIODevice.ReadOnly, QIODevice.Text)); return loader.load(file, null); }; function jheMessage(myText) { // Output a message // myText Main text var mb = new QMessageBox(); mb.setWindowTitle(g_title); mb.text = myText; mb.exec(); return; }; function jheNewNote(myChord, myPitch) { // Add a new note (does not move the cursor) // myChord Chord to contain the note // myPitch Note MIDI pitch var note = new Note(); note.pitch = myPitch; myChord.addNote(note); return; }; function jheQColouredText(myTextObject, myCol, myText, myBold) { // Add coloured text to form object (QLabel) // myTextObject Object e.g. globalformMain.textOne // myColour Colour as 6 didgit hexadecimal text string // e.g. "00007f" Dark blue, "0000ff" Blue, "376f00" Dark Green // "008080" Turquoise, "800040" Dark Purple, "ff0040" Dark Red, // "ff8000" Orange, "ff0000" Red, "000000" Black // myText Text // myBold true if colour to be bold var string1a = ""; var string3 = ""; if (myBold) {myTextObject.setText(string1b+myCol+string2+myText+string3);} else {myTextObject.setText(string1a+myCol+string2+myText+string3);} return; }; function jheSelectedMeasureNumbers(myCursor, mySave) { // Puts the number of the first and last selected measures into global variables // myCursor Cursor // mySave If true the first tick of each measure is saved in array g_selectedMeasures // Needs global variables: var g_firstMeasure; var g_lastMeasure; g_selectedMeasures (optional) // Warning: the cursor is left at the measure after the last selected one var firstTick; // First selected measure tick position var lastTick; // Last selected measure tick position var measureNumber; // Measure number from 1 var measuresSaved; // Number of measures saved so far in g_selectedMeasures myCursor.staff = 0; myCursor.voice = 0; g_firstMeasure = 1; g_lastMeasure = curScore.measures; myCursor.goToSelectionEnd(); lastTick = myCursor.tick(); myCursor.goToSelectionStart(); if (myCursor.eos()) {myCursor.rewind();} // If no selection then full score firstTick = myCursor.tick(); // Get first measure number measuresSaved = 0; measureNumber = 1; myCursor.rewind(); // Beginning of score/staff 0 while (myCursor.tick() < firstTick) { measureNumber = measureNumber + 1; myCursor.nextMeasure(); } g_firstMeasure = measureNumber; // Get last measure number while (myCursor.tick() < lastTick) { if (mySave) {g_selectedMeasures[measuresSaved++] = myCursor.tick();} // Save the first tick measureNumber = measureNumber + 1; myCursor.nextMeasure(); } g_lastMeasure = measureNumber - 1; if (mySave) {g_selectedMeasures[measuresSaved] = myCursor.tick();} // Set the last to the next tick return; }; function jheSetColour(myNote, myRed, myGreen, myBlue) { // Set the colour of a note // myNote Note // myRed Red as 0 to 255 // myGreen Green as 0 to 255 // myBlue Blue as 0 to 255 myNote.color = new QColor(myRed, myGreen, myBlue); return; }; function jheSetCombo(myBox, myClear, myArray, mySize, myPosition) { // Set up the combo box items // myBox Combo box // myClear true if combo box to be cleared of current items // myArray Array of items // mySize Number of items in the array // myPosition Number of selected item (from zero) if (myClear) {myBox.clear();} // Remove all items for (var n = 0; n < mySize; n++) { myBox.addItem(myArray[n]); // Add an item } myBox.setCurrentIndex(myPosition); // Select combo box item (from zero) return; }; function jheStaffPosition(myKey, myType, myNote) { // Returns the the position on the staff lines of a note // G is -3, A -2, B -1, Middle C 0, D 1, E 2, F 3, G 4, A 5, B 6 etc // myKey Key (globalKeySig + 7) // myType Key type: 0 Major, 1 Minor (common), 2 Minor (natural) // myNote Note as semitones above myKey // myKey Cb Gb Db Ab Eb Bb F C G D A E B F# C# // ab eb bb f c g d a e b f# c# g# d# a# var majorKeyStart = [ 0, -3, 1, -2, 2, -1, 3, 0, -3, 1, -2, 2, -1, 3, 0]; var minorKeyStart = [-2, 2, -1, 3, 0, -3, 1, -2, 2, -1, 3, 0, -3, 1, -2]; // Following are 12 entries for the semitones and the numbers are the line they should be on // W W H W W W H var majorQuantity = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6]; // W H W W H W W var minorQuantity = [0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6]; var n, m; n = 0; while (myNote < 0) { myNote = myNote + 12; n--; } n = n + Math.floor(myNote / 12); m = myNote % 12; if (myType == 0) {return majorKeyStart[myKey] + (majorQuantity[m] + (7 * n));} return minorKeyStart[myKey] + (minorQuantity[m] + (7 * n)); }; function jheStoreInColour(myNote, myValue) { // Store a value in the rgb colour // myNote The note // myValue A value // If value is not greater than 255 the resulting colour is still black var n; n = myValue % 25; // Gives 0 to 24 jheSetColour(myNote, Math.floor(myValue / 25), Math.floor(n / 5), n % 5); return; }; /********************************* End of My Function Library ****************/ var mscorePlugin = { // Defines were the function will be placed in the MuseScore menu structure menu: 'Plugins.Create Chords', init: init, run: run }; mscorePlugin;