|
@@ -25,7 +25,6 @@ import TheIcon from "/src/components/The-icon";
|
|
|
import { cloneDeep } from "lodash";
|
|
|
import TheSpeed from "./component/the-speed";
|
|
|
import { getImage } from "./images";
|
|
|
-import { Dropdown, Dsubmenu, Doption, Trigger, Input, Select, Option } from "@arco-design/web-vue";
|
|
|
import {
|
|
|
NButton,
|
|
|
NDropdown,
|
|
@@ -50,6 +49,9 @@ import TheSetting from "./component/the-setting";
|
|
|
import { useRoute } from "vue-router";
|
|
|
import { api_musicSheetCreationDetail, api_musicSheetCreationUpdate } from "../api";
|
|
|
import instrumentsNames from "/src/constant/instrmentsNames.json";
|
|
|
+import { ALL_NOTES, NOTE_DOT } from "./noteData";
|
|
|
+import { Close } from "@vicons/ionicons5";
|
|
|
+import { UseDraggable } from "@vueuse/components";
|
|
|
|
|
|
const allPitches = [
|
|
|
"C,,,,",
|
|
@@ -185,6 +187,7 @@ export default defineComponent({
|
|
|
mearseDeleteShow: false,
|
|
|
staffShow: false, // 五线谱弹窗
|
|
|
settingShow: false, // 设置弹窗
|
|
|
+ selectMearesShow: false, // 选择小节弹窗
|
|
|
});
|
|
|
const data = reactive({
|
|
|
drawCount: 0,
|
|
@@ -205,9 +208,16 @@ export default defineComponent({
|
|
|
isClickNote: false,
|
|
|
/** 音符类型 */
|
|
|
noteType: "",
|
|
|
- selectMeasure: {
|
|
|
- start: "",
|
|
|
- end: "",
|
|
|
+ /** 选择小节范围 */
|
|
|
+ selectMeasures: {
|
|
|
+ state: false,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ start: 1,
|
|
|
+ startNote: null as any,
|
|
|
+ end: 0,
|
|
|
+ endNote: null as any,
|
|
|
+ max: 30,
|
|
|
},
|
|
|
slide: ["note", "meter", "dynamics"],
|
|
|
|
|
@@ -274,7 +284,7 @@ export default defineComponent({
|
|
|
const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
|
|
|
if (totalTime) {
|
|
|
const progress = (data.active as any).currentTrackMilliseconds / 1000 / totalTime;
|
|
|
- // console.log("🚀 ~ data.active:", data.active, progress);
|
|
|
+ // console.log("🚀 ~ data.active:", progress);
|
|
|
(abcData.synthControl as any).seek(progress);
|
|
|
}
|
|
|
}
|
|
@@ -282,7 +292,7 @@ export default defineComponent({
|
|
|
// abcData.visualObj.engraver.rangeHighlight(abcElem.startChar, abcElem.endChar);
|
|
|
// }
|
|
|
if (drag && drag.step) {
|
|
|
- handleMoveNote("drag", drag.step);
|
|
|
+ handleChange({ type: "move", value: { action: "drag", step: drag.step } });
|
|
|
return;
|
|
|
}
|
|
|
if (!abcElem?.midiPitches) return;
|
|
@@ -295,6 +305,7 @@ export default defineComponent({
|
|
|
midiBuffer: null as unknown as ABCJS.MidiBuffer,
|
|
|
abcOptions: {
|
|
|
selectionColor: "#0f81ff",
|
|
|
+ jazzchords: true,
|
|
|
add_classes: true,
|
|
|
clickListener: clickListener,
|
|
|
responsive: "resize",
|
|
@@ -302,8 +313,8 @@ export default defineComponent({
|
|
|
selectTypes: ["note"],
|
|
|
visualTranspose: 0,
|
|
|
wrap: {
|
|
|
- minSpacing: 2,
|
|
|
- maxSpacing: 10,
|
|
|
+ minSpacing: 1.8,
|
|
|
+ maxSpacing: 2.7,
|
|
|
preferredMeasuresPerLine: 4,
|
|
|
},
|
|
|
staffwidth: 800,
|
|
@@ -335,27 +346,6 @@ export default defineComponent({
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * 分词
|
|
|
- * @param str 字符串
|
|
|
- * @returns
|
|
|
- * @description
|
|
|
- */
|
|
|
- const tokenize = (str: string) => {
|
|
|
- const arr = str.split(/(!.+?!|".+?")/);
|
|
|
- let output: string[] = [];
|
|
|
- for (let i = 0; i < arr.length; i++) {
|
|
|
- const token = arr[i];
|
|
|
- if (token.length > 0) {
|
|
|
- if (token[0] !== '"' && token[0] !== "!") {
|
|
|
- const arr2 = arr[i].split(/([A-Ga-gz][,']*)/);
|
|
|
- output = output.concat(arr2);
|
|
|
- } else output.push(token);
|
|
|
- }
|
|
|
- }
|
|
|
- return output;
|
|
|
- };
|
|
|
-
|
|
|
const hideCursor = () => {
|
|
|
const cursor = document.querySelector("#paper svg .ABCJS-cursor");
|
|
|
if (cursor) {
|
|
@@ -367,13 +357,7 @@ export default defineComponent({
|
|
|
};
|
|
|
|
|
|
const cursorControl = {
|
|
|
- // self.onReady = function () {
|
|
|
- // var downloadLink = document.querySelector(".download");
|
|
|
- // downloadLink.addEventListener("click", download);
|
|
|
- // downloadLink.setAttribute("style", "");
|
|
|
- // var clickEl = document.querySelector(".click-explanation");
|
|
|
- // clickEl.setAttribute("style", "");
|
|
|
- // };
|
|
|
+ onReady: function () {},
|
|
|
onStart: function () {
|
|
|
console.log("开始");
|
|
|
data.playState = true;
|
|
@@ -386,12 +370,28 @@ export default defineComponent({
|
|
|
cursor.setAttributeNS(null, "y2", "0");
|
|
|
svg?.appendChild(cursor);
|
|
|
},
|
|
|
- // self.beatSubdivisions = 2;
|
|
|
onBeat: function (beatNumber: any, totalBeats: any, totalTime: any) {},
|
|
|
onEvent: (ev: any) => {
|
|
|
+ // console.log("🚀 ~ ev:", ev);
|
|
|
if (!data.playState) return;
|
|
|
if (ev.measureStart && ev.left === null) return; // this was the second part of a tie across a measure line. Just ignore it.
|
|
|
-
|
|
|
+ if (popup.selectMearesShow) {
|
|
|
+ const startTime = data.selectMeasures.startNote?.currentTrackMilliseconds || 0;
|
|
|
+ const endNote: any = data.selectMeasures.endNote ? data.selectMeasures.endNote : null;
|
|
|
+ // console.log("🚀 ~ endNote:", ev.milliseconds , endNote.currentTrackMilliseconds)
|
|
|
+ if (
|
|
|
+ ev.milliseconds < startTime ||
|
|
|
+ (endNote && ev.milliseconds > endNote.currentTrackMilliseconds)
|
|
|
+ ) {
|
|
|
+ const totalTime = (abcData.synthControl as any).visualObj.getTotalTime();
|
|
|
+ if (totalTime) {
|
|
|
+ const progress = startTime / 1000 / totalTime;
|
|
|
+ nextTick(() => {
|
|
|
+ (abcData.synthControl as any).seek(progress);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
var cursor = document.querySelector("#paper svg .ABCJS-cursor");
|
|
|
if (cursor) {
|
|
|
cursor.setAttribute("x1", ev.left + ev.width / 2);
|
|
@@ -411,14 +411,24 @@ export default defineComponent({
|
|
|
},
|
|
|
};
|
|
|
|
|
|
- const resetMidi = (useActive = false) => {
|
|
|
+ const staffNotes = ALL_NOTES();
|
|
|
+ const loadMiniMp3 = async () => {
|
|
|
data.loadingAudioSrouce = true;
|
|
|
const midiBuffer = new ABCJS.synth.CreateSynth();
|
|
|
- // console.log(midiBuffer);
|
|
|
+ const str = `X: 1\nM:4/4\nL:1/4\n${staffNotes}`;
|
|
|
+ const visualObj = ABCJS.parseOnly(str);
|
|
|
+ await midiBuffer.init({
|
|
|
+ visualObj: visualObj[0],
|
|
|
+ options: { ...abcData.synthOptions },
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const resetMidi = (useActive = false) => {
|
|
|
+ const midiBuffer = new ABCJS.synth.CreateSynth();
|
|
|
midiBuffer
|
|
|
.init({
|
|
|
visualObj: abcData.visualObj,
|
|
|
- options: { ...abcData.synthOptions, midiTranspose: abcData.abc.visualTranspose },
|
|
|
+ options: { ...abcData.synthOptions },
|
|
|
})
|
|
|
.then(() => {
|
|
|
abcData.synthControl
|
|
@@ -428,7 +438,7 @@ export default defineComponent({
|
|
|
})
|
|
|
.then(function (response) {
|
|
|
data.loadingAudioSrouce = false;
|
|
|
- // console.log("Audio successfully loaded.");
|
|
|
+ // console.log("Audio successfully loaded.", {...abcData.synthControl});
|
|
|
// console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
|
|
|
})
|
|
|
.catch((err) => {
|
|
@@ -438,7 +448,6 @@ export default defineComponent({
|
|
|
};
|
|
|
|
|
|
const togglePlay = (type: "play" | "pause" | "reset") => {
|
|
|
- console.log("🚀 ~ abcData.synthControl:", abcData.synthControl);
|
|
|
if (type === "play") {
|
|
|
abcData.synthControl.play();
|
|
|
data.playState = true;
|
|
@@ -449,6 +458,7 @@ export default defineComponent({
|
|
|
} else {
|
|
|
abcData.synthControl.restart();
|
|
|
}
|
|
|
+ // console.log("🚀 ~ abcData.synthControl:", abcData.synthControl.timer.noteTimings);
|
|
|
};
|
|
|
|
|
|
const renderSvg = () => {
|
|
@@ -456,19 +466,26 @@ export default defineComponent({
|
|
|
...abcData.abcOptions,
|
|
|
visualTranspose: abcData.abc.visualTranspose,
|
|
|
})[0];
|
|
|
- console.log("🚀 ~ visualObj:", abcData.visualObj);
|
|
|
+ if (data.drawCount < 3) {
|
|
|
+ console.log("🚀 ~ visualObj:", abcData.visualObj);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const renderBoxRect = () => {
|
|
|
const svg = document.querySelector("#paper svg");
|
|
|
const padding = 4;
|
|
|
+ let measureNumber = 0;
|
|
|
for (let i = 0; i < abcData.visualObj.lines.length; i++) {
|
|
|
const line = abcData.visualObj.lines[i];
|
|
|
+ // console.log("🚀 ~ line:", line);
|
|
|
for (let j = 0; j < line.staff.length; j++) {
|
|
|
const staff = line.staff[j];
|
|
|
const voices = [...staff.voices].flat();
|
|
|
for (let l = 0; l < voices.length; l++) {
|
|
|
const item = voices[l];
|
|
|
+ if (item.el_type === "bar") {
|
|
|
+ measureNumber++;
|
|
|
+ }
|
|
|
// console.log(item.el_type);
|
|
|
if (["note", "keySignature", "clef", "timeSignature"].includes(item.el_type)) {
|
|
|
const box = item.abselem.elemset?.[0]?.getBBox?.() || null;
|
|
@@ -489,25 +506,26 @@ export default defineComponent({
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- // const annotation = document.querySelectorAll("#paper .abcjs-annotation");
|
|
|
- // annotation.forEach((n) => {
|
|
|
- // n.setAttribute("color", "rgba(0,0,0,0)");
|
|
|
- // })
|
|
|
+ console.log(measureNumber);
|
|
|
+ data.selectMeasures.max = measureNumber;
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* @param isProduct 是否是生成曲谱
|
|
|
*/
|
|
|
- const handleResetRender = (isProduct = true) => {
|
|
|
+ const handleResetRender = () => {
|
|
|
return new Promise((resolve) => {
|
|
|
nextTick(() => {
|
|
|
- data.music = isProduct ? renderMeasures(abcData.abc) : data.music;
|
|
|
+ data.music = renderMeasures(abcData.abc);
|
|
|
renderSvg();
|
|
|
resetMidi(data.drawCount > 0 ? true : false);
|
|
|
renderBoxRect();
|
|
|
resolve(1);
|
|
|
textAreaRef.value && (textAreaRef.value.value = data.music);
|
|
|
data.drawCount++;
|
|
|
+
|
|
|
+ // const times = new ABCJS.TimingCallbacks(abcData.visualObj);
|
|
|
+ // console.log("🚀 ~ times:", times)
|
|
|
});
|
|
|
});
|
|
|
};
|
|
@@ -546,11 +564,15 @@ export default defineComponent({
|
|
|
* @returns
|
|
|
*/
|
|
|
const handleChange = async (params: { type: string; value: any }) => {
|
|
|
+ abcData.synthControl.disable(true);
|
|
|
+ if (data.playState) {
|
|
|
+ data.playState = false;
|
|
|
+ }
|
|
|
const type = params.type;
|
|
|
const value = params.value;
|
|
|
const activeNote =
|
|
|
abcData.abc.measures[data.active?.measureIndex]?.notes[data.active?.noteIndex] || null;
|
|
|
- console.log(params, activeNote);
|
|
|
+ // console.log(params, activeNote);
|
|
|
if (type === "type") {
|
|
|
// 设置音符类型
|
|
|
data.noteType = value;
|
|
@@ -595,59 +617,78 @@ export default defineComponent({
|
|
|
if (_values[1]) {
|
|
|
activeNote.accidental = _values[1] || "";
|
|
|
}
|
|
|
+ data.active.isFirstChecked = false;
|
|
|
+ }
|
|
|
+ await handleResetRender();
|
|
|
+ const oldNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex}`);
|
|
|
+ if (oldNote?.abselem?.beam?.elems?.length) {
|
|
|
+ // 判断是否需要分割beam
|
|
|
+ const elems: AbcElem[] = oldNote.abselem.beam.elems;
|
|
|
+ const beatDuration = abcData.visualObj.getBeatLength();
|
|
|
+ const beamLength = elems.map((n) => n.duration).reduce((a, b) => a + b);
|
|
|
+ if (beamLength >= beatDuration) {
|
|
|
+ abcData.abc.measures[data.active.measureIndex].notes[data.active.noteIndex].segno = " ";
|
|
|
+ await handleResetRender();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (oldNote?.midiPitches) {
|
|
|
+ ABCJS.synth.playEvent(oldNote.midiPitches, oldNote.midiGraceNotePitches, 1000);
|
|
|
+ }
|
|
|
+ const nextNote =
|
|
|
+ abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex + 1];
|
|
|
+ if (nextNote) {
|
|
|
+ const abcNextElem: AbcElem = useIndexGetNote(
|
|
|
+ `${data.active.measureIndex}.${data.active.noteIndex + 1}`
|
|
|
+ );
|
|
|
+ rangeHighlight(abcNextElem.startChar);
|
|
|
+ data.active = {
|
|
|
+ ...abcNextElem,
|
|
|
+ measureIndex: data.active.measureIndex,
|
|
|
+ noteIndex: data.active.noteIndex + 1,
|
|
|
+ isFirstChecked: true,
|
|
|
+ };
|
|
|
} else {
|
|
|
const notes = getMeasureNotes(data.active.measureIndex);
|
|
|
const duration = notes.map((n) => n.duration).reduce((a, b) => a + b);
|
|
|
if (duration >= 1) {
|
|
|
- message.warning("小节内音符总时值过长");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- handleCreateNote(
|
|
|
- data.active.measureIndex,
|
|
|
- data.active.noteIndex,
|
|
|
- createNote({
|
|
|
- content: _values[0],
|
|
|
- noteType: data.noteType,
|
|
|
- accidental: _values[1] || "",
|
|
|
- })
|
|
|
- );
|
|
|
- }
|
|
|
- await handleResetRender();
|
|
|
- let _abcElem: AbcElem;
|
|
|
- if (data.active.isFirstChecked) {
|
|
|
- data.active.isFirstChecked = false;
|
|
|
- _abcElem = rangeHighlight(data.active.startChar);
|
|
|
- } else {
|
|
|
- const oldElem: AbcElem = abcData.visualObj.getElementFromChar(data.active.startChar);
|
|
|
- const abcElem: AbcElem = abcData.visualObj.getElementFromChar(oldElem.endChar);
|
|
|
- if (abcElem) {
|
|
|
- let indexStr: any = abcElem.chord?.find((n) => n.position === "left")?.name || "";
|
|
|
- indexStr = indexStr.split(".").map((n: string) => Number(n));
|
|
|
+ // 小节内音符总时值过长,自动跳转到下一小节
|
|
|
+ const nextMeasureNote = abcData.abc.measures[data.active.measureIndex + 1]?.notes[0];
|
|
|
+ if (nextMeasureNote) {
|
|
|
+ const abcNextElem: AbcElem = useIndexGetNote(`${data.active.measureIndex + 1}.${0}`);
|
|
|
+ rangeHighlight(abcNextElem.startChar);
|
|
|
+ data.active = {
|
|
|
+ ...abcNextElem,
|
|
|
+ measureIndex: data.active.measureIndex + 1,
|
|
|
+ noteIndex: 0,
|
|
|
+ isFirstChecked: true,
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ // 到最后一个小节的最后一个音符了
|
|
|
+ rangeHighlight(data.active.startChar);
|
|
|
+ data.active.isFirstChecked = true;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ handleCreateNote(
|
|
|
+ data.active.measureIndex,
|
|
|
+ data.active.noteIndex,
|
|
|
+ createNote({
|
|
|
+ content: "z",
|
|
|
+ noteType: data.noteType,
|
|
|
+ })
|
|
|
+ );
|
|
|
+ await handleResetRender();
|
|
|
+ const newNote = useIndexGetNote(
|
|
|
+ `${data.active.measureIndex}.${data.active.noteIndex + 1}`
|
|
|
+ );
|
|
|
+ rangeHighlight(newNote.startChar);
|
|
|
data.active = {
|
|
|
- ...abcElem,
|
|
|
- measureIndex: indexStr[0],
|
|
|
- noteIndex: indexStr[1],
|
|
|
- isFirstChecked: false,
|
|
|
+ ...newNote,
|
|
|
+ measureIndex: data.active.measureIndex,
|
|
|
+ noteIndex: data.active.noteIndex + 1,
|
|
|
+ isFirstChecked: true,
|
|
|
};
|
|
|
- const beam = (abcElem.abselem as any).beam;
|
|
|
- if (beam) {
|
|
|
- const elems: AbcElem[] = beam.elems;
|
|
|
- if (elems.length) {
|
|
|
- const beatDuration = abcData.visualObj.getBeatLength();
|
|
|
- const beamLength = elems.map((n) => n.duration).reduce((a, b) => a + b);
|
|
|
- if (beamLength >= beatDuration) {
|
|
|
- abcData.abc.measures[data.active.measureIndex].notes[data.active.noteIndex].segno =
|
|
|
- " ";
|
|
|
- await handleResetRender();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
- _abcElem = rangeHighlight(abcElem.startChar);
|
|
|
}
|
|
|
- if (!_abcElem?.midiPitches) return;
|
|
|
- ABCJS.synth.playEvent(_abcElem.midiPitches, _abcElem.midiGraceNotePitches, 1000);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -705,6 +746,8 @@ export default defineComponent({
|
|
|
if (!activeNote) return;
|
|
|
activeNote.meter = `[${value}]`;
|
|
|
await handleResetRender();
|
|
|
+ const oldNote = useIndexGetNote(`${data.active.measureIndex}.${data.active.noteIndex}`);
|
|
|
+ rangeHighlight(oldNote.startChar);
|
|
|
} else {
|
|
|
abcData.abc.meter = value;
|
|
|
await handleResetRender();
|
|
@@ -870,10 +913,9 @@ export default defineComponent({
|
|
|
showToast("请先选择音符");
|
|
|
return;
|
|
|
}
|
|
|
- const activeNote =
|
|
|
- abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
|
|
|
if (!activeNote) return;
|
|
|
activeNote.dot = activeNote.dot ? "" : value;
|
|
|
+ // activeNote.dot = activeNote.dot ? "" : NOTE_DOT[activeNote.noteType];
|
|
|
await handleResetRender();
|
|
|
rangeHighlight(data.active.startChar);
|
|
|
}
|
|
@@ -887,6 +929,31 @@ export default defineComponent({
|
|
|
await handleResetRender();
|
|
|
rangeHighlight(data.active.startChar);
|
|
|
}
|
|
|
+
|
|
|
+ // 移动音符
|
|
|
+ if (type === "move") {
|
|
|
+ const step = value._step ? value._step : value.action === "up" ? -1 : 1;
|
|
|
+ if (!activeNote) return;
|
|
|
+ activeNote.content = moveNote(activeNote.content, step);
|
|
|
+ // arr now contains elements that are either a chord, a decoration, a note name, or anything else. It can be put back to its original string with .join("").
|
|
|
+
|
|
|
+ await handleResetRender();
|
|
|
+ const _abcElem = rangeHighlight(data.active.startChar);
|
|
|
+ if (!_abcElem?.midiPitches) return;
|
|
|
+ ABCJS.synth.playEvent(_abcElem.midiPitches, _abcElem.midiGraceNotePitches, 1000);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除音符
|
|
|
+ if (type === "delete") {
|
|
|
+ if (!data.active) return;
|
|
|
+ if (data.active.startChar === 0) return;
|
|
|
+ abcData.abc.measures[data.active.measureIndex].notes.splice(data.active.noteIndex, 1);
|
|
|
+ if (abcData.abc.measures[data.active.measureIndex].notes.length === 0) {
|
|
|
+ abcData.abc.measures.splice(data.active.measureIndex, 1);
|
|
|
+ }
|
|
|
+ handleResetRender();
|
|
|
+ data.active = null as unknown as INoteActive;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const getNextNote = (index: number): AbcElem => {
|
|
@@ -1007,38 +1074,19 @@ export default defineComponent({
|
|
|
await handleResetRender();
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * 移动音符
|
|
|
- * @param note 音符
|
|
|
- * @param step 移动步数
|
|
|
- */
|
|
|
- const handleMoveNote = async (type: "up" | "donw" | "drag", _step?: number) => {
|
|
|
- if (!data.active) return;
|
|
|
- const step = _step ? _step : type === "up" ? -1 : 1;
|
|
|
- const activeNote =
|
|
|
- abcData.abc.measures[data.active.measureIndex]?.notes[data.active.noteIndex] || null;
|
|
|
- if (!activeNote) return;
|
|
|
- activeNote.content = moveNote(activeNote.content, step);
|
|
|
- // arr now contains elements that are either a chord, a decoration, a note name, or anything else. It can be put back to its original string with .join("").
|
|
|
-
|
|
|
- await handleResetRender();
|
|
|
- const _abcElem = rangeHighlight(data.active.startChar);
|
|
|
- if (!_abcElem?.midiPitches) return;
|
|
|
- console.log(_abcElem, abcData.visualObj.millisecondsPerMeasure());
|
|
|
- ABCJS.synth.playEvent(_abcElem.midiPitches, _abcElem.midiGraceNotePitches, 1000);
|
|
|
- };
|
|
|
-
|
|
|
const handleKeyUp = (e: KeyboardEvent) => {
|
|
|
if (!data.active) return false;
|
|
|
console.log(e.key);
|
|
|
- if (e.key === "Backspace") handleDeleteNote();
|
|
|
+ if (e.key === "Backspace") {
|
|
|
+ handleChange({ type: "delete", value: "" });
|
|
|
+ }
|
|
|
if (/^[A-Ga-g]$/.test(e.key)) {
|
|
|
handleChange({ type: "note", value: e.key.toLocaleUpperCase() });
|
|
|
}
|
|
|
if (["ArrowUp", "ArrowDown"].includes(e.key)) {
|
|
|
e.preventDefault();
|
|
|
e.stopPropagation();
|
|
|
- handleMoveNote(e.key === "ArrowUp" ? "up" : "donw");
|
|
|
+ handleChange({ type: "move", value: { action: e.key === "ArrowUp" ? "up" : "donw" } });
|
|
|
return false;
|
|
|
}
|
|
|
};
|
|
@@ -1079,7 +1127,7 @@ export default defineComponent({
|
|
|
const _instruments = ABCJS.synth.instrumentIndexToName.indexOf(abcData.abc.subjectCode as any);
|
|
|
abcData.synthOptions.program = _instruments > -1 ? _instruments : 0;
|
|
|
abcData.abc.measures = abc.measures || initMusic(30);
|
|
|
- console.log("🚀 ~ abcData.abc:", abcData.abc);
|
|
|
+ // console.log("🚀 ~ abcData.abc:", abcData.abc);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
@@ -1097,8 +1145,10 @@ export default defineComponent({
|
|
|
};
|
|
|
onMounted(async () => {
|
|
|
await getDetailData();
|
|
|
+ loadMiniMp3();
|
|
|
if (ABCJS.synth.supportsAudio()) {
|
|
|
abcData.synthControl = new ABCJS.synth.SynthController();
|
|
|
+ // console.log("🚀 ~ abcData.synthControl:", abcData.synthControl)
|
|
|
abcData.synthControl.load("#audio", cursorControl, {
|
|
|
displayLoop: true,
|
|
|
displayRestart: true,
|
|
@@ -1116,8 +1166,15 @@ export default defineComponent({
|
|
|
e.returnValue = "还有没保存的";
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
- formateAbc(abcData.visualObj);
|
|
|
+ abcData.synthControl.restart();
|
|
|
+ // formateAbc(abcData.visualObj);
|
|
|
+ const selectMearesBtn = document.querySelector("#selectMearesBtn");
|
|
|
+ if (selectMearesBtn) {
|
|
|
+ const rect = selectMearesBtn.getBoundingClientRect();
|
|
|
+ data.selectMeasures.x = document.body.clientWidth - 320;
|
|
|
+ data.selectMeasures.y = rect.top + 70;
|
|
|
+ data.selectMeasures.state = true;
|
|
|
+ }
|
|
|
});
|
|
|
onUnmounted(() => {
|
|
|
document.removeEventListener("keyup", handleKeyUp);
|
|
@@ -1273,14 +1330,49 @@ export default defineComponent({
|
|
|
console.log("🚀 ~ abc:", abc);
|
|
|
abc = (window as any).vertaal(abc, { p: "f", t: 1, u: 0, v: 3, mnum: 0 });
|
|
|
console.log(abc);
|
|
|
- data.music = abc[0];
|
|
|
- handleResetRender(false);
|
|
|
+ // data.music = abc[0];
|
|
|
+ // handleResetRender(false);
|
|
|
};
|
|
|
reader.readAsText(file);
|
|
|
};
|
|
|
input.click();
|
|
|
};
|
|
|
|
|
|
+ /** 设置选段小节 */
|
|
|
+ const handleSetSelectMeares = (index: number | null, type: "start" | "end") => {
|
|
|
+ console.log("🚀 ~ index:", index);
|
|
|
+ if (type === "start") {
|
|
|
+ const note = index ? useIndexGetNote(`${index - 1}.0`) : null;
|
|
|
+ // console.log("🚀 ~ note:", note);
|
|
|
+ data.selectMeasures.start = index ? index - 1 : 0;
|
|
|
+ data.selectMeasures.startNote = note;
|
|
|
+ if (
|
|
|
+ data.selectMeasures.start &&
|
|
|
+ data.selectMeasures.end &&
|
|
|
+ data.selectMeasures.end < data.selectMeasures.start
|
|
|
+ ) {
|
|
|
+ data.selectMeasures.end = 0;
|
|
|
+ data.selectMeasures.endNote = null;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const note = index
|
|
|
+ ? useIndexGetNote(`${index - 1}.${abcData.abc.measures[index - 1]?.notes.length - 1}`)
|
|
|
+ : null;
|
|
|
+ // console.log("🚀 ~ note:", note);
|
|
|
+ data.selectMeasures.end = index ? index - 1 : 0;
|
|
|
+ data.selectMeasures.endNote = note;
|
|
|
+ if (
|
|
|
+ data.selectMeasures.start &&
|
|
|
+ data.selectMeasures.end &&
|
|
|
+ data.selectMeasures.start > data.selectMeasures.end
|
|
|
+ ) {
|
|
|
+ // console.log(data.selectMeasures.start, data.selectMeasures.end);
|
|
|
+ data.selectMeasures.start = 0;
|
|
|
+ data.selectMeasures.startNote = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
return () => (
|
|
|
<div class={styles.container}>
|
|
|
<div class={styles.containerTop} onKeyup={(e: Event) => e.stopPropagation()}>
|
|
@@ -1298,6 +1390,19 @@ export default defineComponent({
|
|
|
} else if (["png", "midi", "wav"].includes(val)) {
|
|
|
handleDownFile(val);
|
|
|
} else if (val === "print") {
|
|
|
+ } else if (val === "exit") {
|
|
|
+ // 判断是否在应用中
|
|
|
+ if (window.matchMedia("(display-mode: standalone)").matches) {
|
|
|
+ window.onbeforeunload = null;
|
|
|
+ window.parent.postMessage(
|
|
|
+ {
|
|
|
+ api: "notation_exit",
|
|
|
+ },
|
|
|
+ "*"
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ window.close();
|
|
|
+ }
|
|
|
}
|
|
|
}}
|
|
|
/>
|
|
@@ -1328,7 +1433,7 @@ export default defineComponent({
|
|
|
>
|
|
|
<img class={styles.topBtnIcon} src={item.icon} />
|
|
|
</div>
|
|
|
- <div>{item.name}</div>
|
|
|
+ <div class={styles.btnName}>{item.name}</div>
|
|
|
</div>
|
|
|
))}
|
|
|
|
|
@@ -1474,9 +1579,10 @@ export default defineComponent({
|
|
|
filterable
|
|
|
options={instruments.value}
|
|
|
v-model:value={abcData.synthOptions.program}
|
|
|
- onUpdate:value={() => {
|
|
|
+ onUpdate:value={async () => {
|
|
|
abcData.synthControl.disable(true);
|
|
|
data.playState = false;
|
|
|
+ await loadMiniMp3();
|
|
|
resetMidi(true);
|
|
|
popup.selectSubjectShow = false;
|
|
|
}}
|
|
@@ -1826,12 +1932,17 @@ export default defineComponent({
|
|
|
</NSpin>
|
|
|
<div>{data.playState ? "暂停" : "播放"}</div>
|
|
|
</div>
|
|
|
- <div class={[styles.topBtn, styles.btnDisabled]}>
|
|
|
- <div class={styles.btnImg}>
|
|
|
+ <div
|
|
|
+ id="selectMearesBtn"
|
|
|
+ class={[styles.topBtn]}
|
|
|
+ onClick={() => (popup.selectMearesShow = !popup.selectMearesShow)}
|
|
|
+ >
|
|
|
+ <div class={[styles.btnImg, popup.selectMearesShow && styles.btnImgActive]}>
|
|
|
<img class={styles.topBtnIcon} src={getImage("icon_22.png")} />
|
|
|
</div>
|
|
|
<div>选段</div>
|
|
|
</div>
|
|
|
+
|
|
|
<div class={[styles.topBtn, styles.btnDisabled]}>
|
|
|
<div class={styles.btnImg}>
|
|
|
<img class={styles.topBtnIcon} src={getImage("icon_23.png")} />
|
|
@@ -2044,18 +2155,7 @@ export default defineComponent({
|
|
|
<div id="paper"></div>
|
|
|
<Keys show={data.active ? true : false} onClick={(val) => handleChange(val)} />
|
|
|
|
|
|
- {/* <div>
|
|
|
- <textarea
|
|
|
- ref={textAreaRef}
|
|
|
- class={styles.value}
|
|
|
- id="abc"
|
|
|
- onChange={() => {
|
|
|
- console.log(textAreaRef.value.value);
|
|
|
- data.music = textAreaRef.value.value;
|
|
|
- handleResetRender();
|
|
|
- }}
|
|
|
- ></textarea>
|
|
|
- </div> */}
|
|
|
+ {/* <textarea ref={textAreaRef} class={styles.value} id="abc"></textarea> */}
|
|
|
<div id="audio" style={{ opacity: 0 }}></div>
|
|
|
<div id="warnings"></div>
|
|
|
|
|
@@ -2068,6 +2168,62 @@ export default defineComponent({
|
|
|
<div ref={downRef}></div>
|
|
|
|
|
|
<TheSetting v-model:show={popup.settingShow} />
|
|
|
+
|
|
|
+ {data.selectMeasures.state && (
|
|
|
+ <UseDraggable
|
|
|
+ initialValue={{ x: data.selectMeasures.x, y: data.selectMeasures.y }}
|
|
|
+ class={[styles.selectMearesBox, !popup.selectMearesShow && styles.selectMearesHidden]}
|
|
|
+ >
|
|
|
+ <div onKeyup={(e: Event) => e.stopPropagation()}>
|
|
|
+ <NSpace justify="space-between">
|
|
|
+ <div class={styles.btnLineTitle}>输入小节范围</div>
|
|
|
+ <NButton circle quaternary size="small" onClick={() => (popup.selectMearesShow = false)}>
|
|
|
+ <NIcon size={16} component={<Close />} />
|
|
|
+ </NButton>
|
|
|
+ </NSpace>
|
|
|
+ <NSpace align="center" wrap={false} wrapItem={false}>
|
|
|
+ <div class={styles.mearesInput}>
|
|
|
+ <NInputNumber
|
|
|
+ min={1}
|
|
|
+ max={data.selectMeasures.max}
|
|
|
+ bordered={false}
|
|
|
+ placeholder="开始小节"
|
|
|
+ showButton={false}
|
|
|
+ onUpdate:value={(val) => handleSetSelectMeares(val, "start")}
|
|
|
+ ></NInputNumber>
|
|
|
+ -
|
|
|
+ <NInputNumber
|
|
|
+ min={data.selectMeasures.start}
|
|
|
+ max={data.selectMeasures.max}
|
|
|
+ bordered={false}
|
|
|
+ placeholder="结束小节"
|
|
|
+ showButton={false}
|
|
|
+ onUpdate:value={(val) => handleSetSelectMeares(val, "end")}
|
|
|
+ ></NInputNumber>
|
|
|
+ </div>
|
|
|
+ <div class={styles.topBtn}>
|
|
|
+ <NSpin show={data.loadingAudioSrouce} size="small">
|
|
|
+ <div
|
|
|
+ class={styles.btnImg}
|
|
|
+ onClick={() => togglePlay(data.playState ? "pause" : "play")}
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ style={{ display: data.playState ? "" : "none" }}
|
|
|
+ class={styles.topBtnIcon}
|
|
|
+ src={getImage("icon_21_1.png")}
|
|
|
+ />
|
|
|
+ <img
|
|
|
+ style={{ display: data.playState ? "none" : "" }}
|
|
|
+ class={styles.topBtnIcon}
|
|
|
+ src={getImage("icon_21.png")}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </NSpin>
|
|
|
+ </div>
|
|
|
+ </NSpace>
|
|
|
+ </div>
|
|
|
+ </UseDraggable>
|
|
|
+ )}
|
|
|
</div>
|
|
|
);
|
|
|
},
|