runtime.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. import { reactive } from "vue";
  2. import { IAbc, IMeasure, INote } from "../types";
  3. import { getImage } from "./images";
  4. import { ABC_KEYS, ABC_NOTE_DATA } from "./noteData";
  5. import { TuneObject } from "abcjs";
  6. export const ABC_DATA = {
  7. /** 音符 */
  8. types: [
  9. { name: "全音符", value: "4", icon: "icon-quanyinfu" },
  10. { name: "2分音符", value: "2", icon: "icon-a-2fenyinfu" },
  11. { name: "4分音符", value: "", icon: "icon-a-4fenyinfu" },
  12. { name: "8分音符", value: "/", icon: "icon-a-8fenyinfu" },
  13. { name: "16分音符", value: "//", icon: "icon-a-16fenyinfu" },
  14. { name: "32音符", value: "///", icon: "icon-a-32fenyinfu" },
  15. ],
  16. /** 休止符 */
  17. reset: [{ name: "休止符", value: "z", icon: "icon-a-4fenxiuzhifu" }],
  18. /** 临时升降记号 */
  19. accidentals: [
  20. { name: "重降号", value: "__", icon: getImage("icon_2.png") },
  21. { name: "降号", value: "_", icon: getImage("icon_3.png") },
  22. { name: "还原号", value: "=", icon: getImage("icon_4.png") },
  23. { name: "升号", value: "^", icon: getImage("icon_5.png") },
  24. { name: "重升号", value: "^^", icon: getImage("icon_6.png") },
  25. ],
  26. /** 谱号 */
  27. clef: [
  28. { name: "低音谱号", value: "K:bass", icon: "icon-puhao-diyinpuhao" },
  29. { name: "高音谱号", value: "K:treble", icon: "icon-puhao-gaoyinpuhao" },
  30. { name: "次中音谱号", value: "K:tenor", icon: "icon-puhao-cizhongyinpuhao" },
  31. { name: "中音谱号", value: "K:alto", icon: "icon-puhao-zhongyinpuhao" },
  32. { name: "打击乐谱号", value: "K:perc", icon: "icon-puhao-gupu" },
  33. ],
  34. /** 调号 */
  35. key: [
  36. { name: "C大调", value: "K:C", step: 0, icon: "icon-a-diaohao-cdadiaoaxiaodiao1" },
  37. { name: "F#大调", value: "K:F#", step: 6, icon: "icon-a-diaohao-fdadiaodxiaodiao" },
  38. { name: "F大调", value: "K:F", step: 5, icon: "icon-a-diaohao-fdadiaodxiaodiao1" },
  39. { name: "E大调", value: "K:E", step: 4, icon: "icon-a-diaohao-edadiaocxiaodiao" },
  40. { name: "Eb大调", value: "K:Eb", step: 3, icon: "icon-a-diaohao-ebdadiaocxiaodiao" },
  41. { name: "D大调", value: "K:D", step: 2, icon: "icon-a-diaohao-Ddaxiaoexiaodiao" },
  42. { name: "C#大调", value: "K:C#", step: 1, icon: "icon-a-diaohao-cdadiaoaxiaodiao" },
  43. { name: "B大调", value: "K:B", step: -1, icon: "icon-a-diaohao-bdadiaogxiaodiao" },
  44. { name: "Cb大调", value: "K:Cb", step: -1, icon: "icon-a-diaohao-cbdadiaoabxiaodiao" },
  45. { name: "Db大调", value: "K:Db", step: -1, icon: "icon-a-diaohao-dbdadiaobbxiaodiao" },
  46. { name: "Bb大调", value: "K:Bb", step: -2, icon: "icon-a-diaohao-bbdadiaogxiaodiao" },
  47. { name: "A大调", value: "K:A", step: -3, icon: "icon-a-diaohao-Adadiaofxiaodiao" },
  48. { name: "Ab大调", value: "K:Ab", step: -4, icon: "icon-a-diaohao-abdadiaofxiaodiao" },
  49. { name: "G大调", value: "K:G", step: -5, icon: "icon-a-diaohao-Gdadiaoexiaodiao" },
  50. { name: "Gb大调", value: "K:Gb", step: -6, icon: "icon-a-diaohao-gbdadiaoebxiaodiao" },
  51. ],
  52. /** 拍号 */
  53. meter: [
  54. { name: "4/4", value: "M:4/4", icon: "icon-paihao-44" },
  55. { name: "2/2", value: "M:2/2", icon: "icon-paihao-22" },
  56. { name: "2/4", value: "M:2/4", icon: "icon-paihao-24" },
  57. { name: "3/4", value: "M:3/4", icon: "icon-paihao-34" },
  58. { name: "3/8", value: "M:3/8", icon: "icon-paihao-38" },
  59. { name: "6/8", value: "M:6/8", icon: "icon-paihao-68" },
  60. { name: "9/8", value: "M:9/8", icon: "icon-paihao-98" },
  61. { name: "12/8", value: "M:12/8", icon: "icon-a-paihao-128" },
  62. ],
  63. /** 演奏技法 */
  64. play: [
  65. { name: "加强音", value: "!marcato!", icon: getImage("icon_9.png") },
  66. { name: "重音", value: "!>!", icon: getImage("icon_10.png") },
  67. { name: "保持音", value: "!tenuto!", icon: getImage("icon_11.png") },
  68. { name: "断音", value: "!wedge!", icon: getImage("icon_12.png") },
  69. { name: "花型重复记号", value: "S", icon: "icon-fanfuyutiaoyue-sbiao" },
  70. { name: "Coda", value: "O", icon: "icon-fanfuyutiaoyue-weisheng" },
  71. { name: "波音", value: "P", icon: "icon-e1" },
  72. { name: "逆波音", value: "M", icon: "icon-d1" },
  73. { name: "换气符号(逗号)", value: "!breath!", icon: "icon-c1" },
  74. { name: "回音", value: "!turn!", icon: "icon-b" },
  75. // { name: "逆回音", value: "!turnx!", icon: "icon-b" },
  76. { name: "颤音", value: "T", icon: "icon-a1" },
  77. { name: "跳音", value: ".", icon: "icon-a-zoufajihao-duanzouhaoshang" },
  78. { name: "延迟音记号", value: "!fermata!", icon: "icon-f1" },
  79. ],
  80. /** 小节线 */
  81. bar: [
  82. { name: "单小节线", value: "|", icon: "icon-xiaojiexian-danxiaojiexian" },
  83. { name: "双小节线", value: "||", icon: "icon-xiaojiexian-shuangxiaojiexian" },
  84. { name: "结束线", value: "|]", icon: "icon-xiaojiexian-zhongzhixiaojiexian" },
  85. { name: "重复线开始", value: "|:", icon: "icon-a-xiaojiexian-zuoqishifanfuhao" },
  86. { name: "重复线结束", value: ":|", icon: "icon-a-xiaojiexian-youzhongzhifanfuhao" },
  87. { name: "双重复", value: "::", icon: "icon-xiaojiexian-jieshuyuqishifanfubiaozhi" },
  88. ],
  89. /** 连线 */
  90. tie: [
  91. { name: "延音线", value: "-", icon: getImage("icon_7.png") }, // 延音必须同音高
  92. { name: "连音线", value: ["(", ")"], icon: getImage("icon_8.png") }, // 需要是音符和结束音符,最低两个音符
  93. ],
  94. /** 8度线 */
  95. octave: [
  96. // 暂不支持
  97. { name: "高8度开始", value: ["!8va(!", "!8va)!"] },
  98. { name: "低8度", value: ["!8vb(!", "!8vb)!"] },
  99. ],
  100. /** 力度记号 */
  101. dynamics: [
  102. { name: "极弱", value: "!ppp!", icon: "icon-lidujihao-ppp" },
  103. { name: "很弱", value: "!pp!", icon: "icon-lidujihao-pp" },
  104. { name: "弱", value: "!p!", icon: "icon-lidujihao-p" },
  105. { name: "中弱", value: "!mp!", icon: "icon-lidujihao-mp" },
  106. { name: "中强", value: "!mf!", icon: "icon-lidujihao-mf" },
  107. { name: "强", value: "!f!", icon: "icon-lidujihao-f" },
  108. { name: "很强", value: "!ff!", icon: "icon-lidujihao-ff" },
  109. { name: "极强", value: "!fff!", icon: "icon-lidujihao-fff" },
  110. { name: "渐强", value: ["!<(!", "!<)!"], icon: "icon-lidujihao-jianqianghao" }, // 需要是音符范围,最低两个音
  111. { name: "渐弱", value: ["!>(!", "!>)!"], icon: "icon-lidujihao-jianruohao" }, //需要是音符范围,最低两个音
  112. ],
  113. repeat: [
  114. { name: "第一跳跃", value: "1", icon: "icon-fanfuyutiaoyue-diyitiaoyuehao" },
  115. { name: "第二跳跃", value: "2", icon: "icon-fanfuyutiaoyue-di2kaifangtiaoyuehao" },
  116. // { name: "重复3房", value: "3", icon: "" },
  117. // { name: "重复4房", value: "4", icon: "" },
  118. ],
  119. speeds: [
  120. { name: "60", value: "Q:1/4=60", icon: "" },
  121. { name: "70", value: "Q:1/4=70", icon: "" },
  122. { name: "80", value: "Q:1/4=80", icon: "" },
  123. { name: "90", value: "Q:1/4=90", icon: "" },
  124. { name: "100", value: "Q:1/4=100", icon: "" },
  125. { name: "120", value: "Q:1/4=120", icon: "" },
  126. ],
  127. slus: [
  128. { name: "3连音", value: "(3", icon: "" },
  129. { name: "4连音", value: "(4", icon: "" },
  130. { name: "5连音", value: "(5", icon: "" },
  131. { name: "6连音", value: "(6", icon: "" },
  132. { name: "7连音", value: "(7", icon: "" },
  133. ],
  134. /** 3连音等多连音
  135. * 1. 将连音的音符小括号起来,并在括号前加上数字表示连音的音符数
  136. */
  137. };
  138. export const settings = reactive({
  139. /** 光标跟随 音符, 节拍 */
  140. cursorType: "note" as "note" | "beat",
  141. });
  142. export const createNote = (options: Partial<INote>): INote => {
  143. return {
  144. accidental: options.accidental || "",
  145. content: options.content || "",
  146. noteType: options.noteType || "",
  147. clef: options.clef || "",
  148. play: options.play || [],
  149. key: options.key || "",
  150. speed: options.speed || "",
  151. dynamics: options.dynamics || "",
  152. dCode: options.dCode || "",
  153. tie: options.tie || "",
  154. tCode: options.tCode || "",
  155. dot: options.dot || "",
  156. slus: options.slus || "",
  157. tieline: options.tieline || "",
  158. segno: options.segno || "",
  159. };
  160. };
  161. export const createMeasure = (): IMeasure => {
  162. return {
  163. notes: [
  164. createNote({
  165. content: "z",
  166. noteType: "4",
  167. }),
  168. ],
  169. barline: "|",
  170. repeat: "",
  171. measureNumber: 0,
  172. celf: "",
  173. key: "",
  174. meter: "",
  175. };
  176. };
  177. interface IRenderMeasuresOption {
  178. /** 是否生成index */
  179. hiddenIndex?: boolean;
  180. showTitle?: boolean;
  181. showCreator?: boolean;
  182. jianpu?: boolean;
  183. }
  184. /**
  185. * 生成小节
  186. * @param abc
  187. *
  188. * @returns
  189. */
  190. export const renderMeasures = (abc: IAbc, option?: IRenderMeasuresOption) => {
  191. // console.log("🚀 ~ abc:", abc)
  192. let wrap = 1;
  193. let text = `X:1\n`;
  194. if (option?.jianpu){
  195. text += '%%jianpu 1 \n'
  196. }
  197. if (option?.showTitle) {
  198. abc.title && (text += `T:${abc.title}` + "\n");
  199. }
  200. if (option?.showCreator) {
  201. abc.creator && (text += `C:${abc.creator}` + "\n");
  202. }
  203. if (!option?.hiddenIndex) {
  204. text += "%%barnumbers 1" + "\n";
  205. }
  206. if (abc.isrhythm === 'rhythm') {
  207. text += "K:perc" + "\n";
  208. } else {
  209. abc.celf && (text += abc.celf + "\n");
  210. }
  211. abc.meter && (text += abc.meter + "\n");
  212. abc.minUnit && (text += abc.minUnit + "\n");
  213. abc.speed && (text += abc.speed + "\n");
  214. if (abc.key) {
  215. text += abc.key + " ";
  216. // text += "style=harmonic";
  217. if (abc.isrhythm === 'rhythm'){
  218. text += "style=x";
  219. }
  220. // text += "style=x";
  221. // text += "style=triangle";
  222. text += "\n";
  223. }
  224. // text += "V:1 style=jianpu" + "\n";
  225. const measures = abc.measures;
  226. for (let i = 0; i < measures.length; i++) {
  227. const measure = measures[i];
  228. text += measure.repeat ?? ""; // 重复
  229. text += measure.meter ?? ""; // 拍号
  230. for (let j = 0; j < measure.notes.length; j++) {
  231. const note = measure.notes[j];
  232. const playStr = note.play?.join("") ?? "";
  233. text += note.clef ?? ""; // 谱号
  234. text += note.key ?? ""; // 调号
  235. text += note.speed ?? ""; // 速度
  236. text += note.slus ?? ""; // 3连音
  237. if (note.tie?.includes("(")) {
  238. // 连音线 前
  239. text += note.tie ?? "";
  240. }
  241. if (!option?.hiddenIndex) {
  242. text += `"<${i + "." + j}"`; // 音符 id
  243. }
  244. text += playStr ?? ""; // 演奏技法
  245. text += note.dynamics ?? ""; // 力度符号
  246. if (abc.isrhythm !== 'rhythm') {
  247. text += note.accidental ?? ""; // 临时升降记号
  248. }
  249. if (!note.content.includes('z') && abc.isrhythm === 'rhythm'){
  250. text += "B";
  251. } else {
  252. text += note.content ?? ""; // 音符
  253. }
  254. // 音符时值
  255. text += note.noteType ?? "";
  256. text += note.dot ?? ""; // 点
  257. text += note.tieline ?? ""; // 延音
  258. if (note.tie?.includes(")")) {
  259. // 连音线 后
  260. text += note.tie ?? "";
  261. }
  262. text += note.segno ?? ""; // 分割
  263. }
  264. // let _i = i + 1;
  265. // if (!option?.hiddenIndex) {
  266. // text += `"<${_i}"`;
  267. // }
  268. text += measure.barline ?? "";
  269. if (wrap % 4 === 0) {
  270. text += "\n";
  271. }
  272. wrap++;
  273. }
  274. // console.log(text)
  275. return text;
  276. };
  277. export const getKeyStep = (key: string, oldKey: string, keyType: "inset" | "up" | "down") => {
  278. let step = 0;
  279. const _key = ABC_KEYS[oldKey][key];
  280. if (keyType === "down") {
  281. step = _key.down;
  282. } else if (keyType === "up") {
  283. step = _key.up;
  284. } else {
  285. step = Math.abs(_key.up) > Math.abs(_key.down) ? _key.down : _key.up;
  286. }
  287. return { step, move: _key.move };
  288. };
  289. export const moveNoteKey = (note: string, moveData: { step: number; move: number }) => {
  290. let x = -1;
  291. for (let i = 0; i < ABC_NOTE_DATA.length; i++) {
  292. const notes = ABC_NOTE_DATA[i];
  293. if (Array.isArray(notes) && notes.includes(note)) {
  294. x = i;
  295. break;
  296. }
  297. if (note === notes) {
  298. x = i;
  299. break;
  300. }
  301. }
  302. console.log(note, moveData.step, x);
  303. if (x >= 0) {
  304. const _note = ABC_NOTE_DATA[x + moveData.step];
  305. if (Array.isArray(_note)) {
  306. return _note[moveData.move];
  307. } else {
  308. return _note ? _note : note;
  309. }
  310. }
  311. return note;
  312. };
  313. const formateGetData = {
  314. getNoteType: (duration: number) => {
  315. const type = 0.25 / duration;
  316. // console.log(type, duration);
  317. const noteType = [
  318. { name: 0.25, value: "4" },
  319. { name: 0.5, value: "2" },
  320. { name: 1, value: "" },
  321. { name: 2, value: "/" },
  322. { name: 4, value: "//" },
  323. { name: 8, value: "///" },
  324. ];
  325. let _duration = noteType.find((n) => n.name === type)?.value || "";
  326. const noteDuration: any = {
  327. "0.046875": "1/6",
  328. "0.09375": "1/3",
  329. "0.1875": "2/3",
  330. "0.375": "3/2",
  331. "0.75": "3",
  332. };
  333. if (_duration === "") {
  334. _duration = noteDuration[duration] || "";
  335. }
  336. return _duration;
  337. },
  338. getSegno(ele: any, nextEle: any) {
  339. let segno = " ";
  340. if (nextEle && ele) {
  341. // console.log("🚀 ~ nextEle:", nextEle)
  342. if (nextEle?.abselem?.beam?.elems && Array.isArray(nextEle.abselem.beam.elems)) {
  343. const _ele = nextEle.abselem.beam.elems.find((n: any) => n.abcelem.startChar === ele.startChar);
  344. // console.log("🚀 ~ elems:", _ele)
  345. if (_ele) {
  346. segno = "";
  347. }
  348. }
  349. }
  350. return segno;
  351. },
  352. };
  353. export const formateAbc = (visualObj: TuneObject, option: any) => {
  354. let speed = visualObj?.metaText?.tempo?.bpm ? visualObj.metaText.tempo.bpm : visualObj.getBpm();
  355. const abc = {
  356. celf: "K:treble",
  357. minUnit: "L:1/4",
  358. meter: "M:4/4",
  359. speed: `Q:1/4=${speed}`,
  360. key: "K:C",
  361. visualTranspose: 0,
  362. subjectCode: option.subjectCode ?? "acoustic_grand_piano",
  363. title: visualObj?.metaText?.title ?? "",
  364. creator: visualObj?.metaText?.composer ?? "",
  365. };
  366. const list = [];
  367. let notes = [];
  368. let measureIndex = 0;
  369. const get_accidental = (noteItem: INote) => {
  370. let accidental = "";
  371. if (noteItem.content.includes("_")) {
  372. accidental = "_";
  373. }
  374. if (noteItem.content.includes("__")) {
  375. accidental = "__";
  376. }
  377. if (noteItem.content.includes("=")) {
  378. accidental = "=";
  379. }
  380. if (noteItem.content.includes("^")) {
  381. accidental = "^";
  382. }
  383. if (noteItem.content.includes("^^")) {
  384. accidental = "^^";
  385. }
  386. return accidental;
  387. };
  388. for (let i = 0; i < visualObj.lines.length; i++) {
  389. const line = visualObj.lines[i];
  390. if (line.staff) {
  391. for (let j = 0; j < line.staff.length; j++) {
  392. const staff = line.staff[j];
  393. if (i === 0) {
  394. if (staff.clef) {
  395. abc.celf = `K:${staff.clef.type}`;
  396. }
  397. if (staff.key) {
  398. abc.key = `K:${staff.key.root}${staff.key.acc}`;
  399. }
  400. if (staff.meter?.value?.[0]) {
  401. abc.meter = `M:${staff.meter.value[0].num}/${staff.meter.value[0].den}`;
  402. }
  403. }
  404. if (staff.voices) {
  405. let measure = {
  406. notes: [] as INote[],
  407. barline: "|",
  408. repeat: "",
  409. measureNumber: measureIndex,
  410. celf: "",
  411. key: "",
  412. meter: "",
  413. };
  414. for (let k = 0; k < staff.voices.length; k++) {
  415. const voice = staff.voices[k];
  416. for (let l = 0; l < voice.length; l++) {
  417. const element = voice[l];
  418. const nextElement = voice[l + 1];
  419. if (element.el_type === "bar") {
  420. measureIndex++;
  421. list.push(measure);
  422. notes = [];
  423. measure = {
  424. notes: [] as INote[],
  425. barline: "|",
  426. repeat: "",
  427. measureNumber: measureIndex,
  428. celf: "",
  429. key: "",
  430. meter: "",
  431. };
  432. }
  433. if (element.el_type === "note") {
  434. // const abcEle = visualObj.getElementFromChar(element.startChar);
  435. // console.log("🚀 ~ abcEle:", element);
  436. let noteItem = {
  437. clef: "", //// 谱号
  438. key: "", // 调号
  439. speed: "", // 速度
  440. slus: "", // 3连音
  441. tie: "", // 连音线 前,连音线 后
  442. content: "", // 音符
  443. noteType: formateGetData.getNoteType(element.duration), // 音符时值
  444. play: [],
  445. dynamics: "", // 力度符号
  446. accidental: "", // 临时升降记号
  447. dot: "", //附点
  448. tieline: "", // 延音线
  449. segno: formateGetData.getSegno(element, nextElement), // 分割
  450. };
  451. if (element.rest) {
  452. noteItem.content = "z";
  453. } else {
  454. noteItem.content = element.pitches?.[0]?.name ?? "";
  455. }
  456. noteItem.accidental = get_accidental(noteItem as any);
  457. if (noteItem.accidental) {
  458. noteItem.content = noteItem.content.replace(noteItem.accidental, "");
  459. }
  460. const note = createNote(noteItem);
  461. measure.notes.push(note);
  462. }
  463. }
  464. }
  465. }
  466. }
  467. }
  468. }
  469. console.log(measureIndex, list);
  470. return {
  471. ...abc,
  472. measures: list,
  473. };
  474. };