diff --git a/public/index.html b/public/index.html index 9ea85f6..4ec36ca 100644 --- a/public/index.html +++ b/public/index.html @@ -14,6 +14,7 @@ +
diff --git a/src/menu/editor.js b/src/menu/editor.js index 8047d0f..6daaa51 100644 --- a/src/menu/editor.js +++ b/src/menu/editor.js @@ -21,6 +21,7 @@ import { createParagraph, createParagraphWithNotations, } from "../util/paragraph"; +import { convertTone, convertToneTo } from "../util/tone-convert"; import { exportFile, loadFile, saveFile } from "../util/file"; import { findParagraphAndNotation, resetGlobalData } from "../util/editor"; import { @@ -74,9 +75,28 @@ const handleEditMenu = wrappedAction(({ key }) => { break; } }); -const handleConvertMenu = wrappedAction(() => { - console.log(111); -}); +const handleConvertMenu = ({ key }) => { + switch (true) { + case key.startsWith("convertTo"): + const destTone = key.split("-", 2)[1]; + convertToneTo(destTone); + break; + case key === "convert-up": + convertTone(2); + break; + case key === "convert-down": + convertTone(-2); + break; + case key === "convert-up8": + convertTone(12); + break; + case key === "convert-down8": + convertTone(-12); + break; + default: + break; + } +}; // 画布上的点击事件 const handleClick = wrappedAction((ev) => { @@ -94,6 +114,9 @@ const handleKeyPress = wrappedAction((ev) => { if (state.selectedNotationKey) { // 仅选中符号时作用 + if (state.helpDialogVisible || state.configDialogVisible) { + return; + } const { paragraph, notation, @@ -404,33 +427,27 @@ const editMenu = ( const convertMenu = ( - - 1 = C - 1 = D - 1 = E - 1 = F - 1 = G - 1 = A - 1 = B - - 1 = C - - + + 1 = C + 1 = D + 1 = E + 1 = F + 1 = G + 1 = A + 1 = B + 1 = D - + 1 = E - - 1 = F - - + 1 = G - + 1 = A - + 1 = B diff --git a/src/util/notation.js b/src/util/notation.js index e42d3ce..50cc285 100644 --- a/src/util/notation.js +++ b/src/util/notation.js @@ -27,6 +27,9 @@ const separators = ["│", "‖"]; function isNote(notationOrNote) { return notes.includes(notationOrNote?.note || notationOrNote); } +function isPauseNote(notationOrNote) { + return notations.zero === (notationOrNote?.note || notationOrNote); +} function isSeparator(notationOrNote) { return separators.includes(notationOrNote?.note || notationOrNote); } @@ -56,4 +59,11 @@ function cloneNotation(notation) { return n; } -export { notations, isNote, isSeparator, createNotation, cloneNotation }; +export { + notations, + isNote, + isPauseNote, + isSeparator, + createNotation, + cloneNotation, +}; diff --git a/src/util/tone-convert.js b/src/util/tone-convert.js new file mode 100644 index 0000000..34b7c52 --- /dev/null +++ b/src/util/tone-convert.js @@ -0,0 +1,106 @@ +import store from "../store/global"; +import { wrappedAction } from "../store/history"; +import { isNote, isPauseNote } from "./notation"; + +// 音符到音数 +const noteToLevelMap = { + 1: 0, + 2: 2, + 3: 4, + 4: 5, + 5: 7, + 6: 9, + 7: 11, +}; +// 音数到音符 +const levelToNoteMap = { + 0: 1, + 1: 1, + 2: 2, + 3: 2, + 4: 3, + 5: 4, + 6: 4, + 7: 5, + 8: 5, + 9: 6, + 10: 7, + 11: 7, +}; +// 升降到音数 +const bumpSharpToLevelMap = { + "♭": -1, + "♯": 1, +}; +// 音数到升降 +const levelToBumpSharpMap = { + 1: "♯", + 3: "♯", + 6: "♯", + 8: "♯", + 10: "♭", +}; + +const toneToLevelMap = { + C: 0, + D: 2, + E: 4, + F: 5, + G: 7, + A: 9, + B: 11, +}; + +/** + * 整体转调 + * @param {number} deltaVolume 要调整的音数 + */ +const convertTone = wrappedAction(function (deltaVolume) { + deltaVolume = deltaVolume | 0; + store.paragraphs.forEach((p) => { + p.notations.forEach((n) => { + if (isNote(n) && !isPauseNote(n)) { + n.octave += Math.trunc(deltaVolume / 12); + let newLevel = + noteToLevelMap[n.note] + + (deltaVolume % 12) + + (n.prefixSups || []).reduce( + (prev, next) => prev + (bumpSharpToLevelMap[next] | 0), + 0 + ); + if (newLevel >= 12) { + n.octave += 1; + newLevel -= 12; + } else if (newLevel < 0) { + n.octave -= 1; + newLevel += 12; + } + n.note = String(levelToNoteMap[newLevel]); + n.prefixSups = (n.prefixSups || []).filter( + (c) => c !== "♯" && c !== "♭" + ); + if (levelToBumpSharpMap[newLevel]) { + n.prefixSups.unshift(levelToBumpSharpMap[newLevel]); + } + } + }); + }); +}); + +// 转到1 = * +const convertToneTo = wrappedAction((destTone) => { + let deltaVolume = -( + toneToLevelMap[destTone.split("").at(-1)] - + toneToLevelMap[store.tone.split("").at(-1)] + ); + if (destTone.startsWith("♭") || store.tone.startsWith("♯")) { + deltaVolume -= 1; + } + if (destTone.startsWith("♯") || store.tone.startsWith("♭")) { + deltaVolume += 1; + } + convertTone(deltaVolume); + store.tone = destTone; +}); + +export { convertTone, convertToneTo };