Sfoglia il codice sorgente

Merge branch 'feature-tianyong' into gym-dev

TIANYONG 6 mesi fa
parent
commit
c5d88ebb04

+ 10 - 0
dist/instrument.html

@@ -41,8 +41,13 @@
       })
     }
   </script>
+<<<<<<< HEAD
   <script type="module" crossorigin src="./js/instrument-718ba3ce.js"></script>
   <link rel="stylesheet" href="./css/instrument-9723cd86.css">
+=======
+  <script type="module" crossorigin src="./js/instrument-2958d7ec.js"></script>
+  <link rel="stylesheet" href="./css/instrument-f4164af0.css">
+>>>>>>> feature-tianyong
   <script type="module">import.meta.url;import("_").catch(()=>1);async function* g(){};window.__vite_is_modern_browser=true;</script>
   <script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy chunks, syntax error above and the same error below should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
 </head>
@@ -65,8 +70,13 @@
     var vConsole = new window.VConsole();
   </script>   -->
   <script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
+<<<<<<< HEAD
   <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-1f76d399.js"></script>
   <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-67edbb78.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+  <script nomodule crossorigin id="vite-legacy-polyfill" src="./js/polyfills-legacy-7ea27e1e.js"></script>
+  <script nomodule crossorigin id="vite-legacy-entry" data-src="./js/instrument-legacy-2a2ddf2b.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> feature-tianyong
 </body>
 
 </html>

File diff suppressed because it is too large
+ 1 - 0
dist/js/index-46c8da27.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-4d51d294.js


+ 5 - 0
dist/js/index-579920f7.js

@@ -0,0 +1,5 @@
+<<<<<<<< HEAD:dist/js/index-8792264e.js
+import{d as i,g as l,r as d,E as e,o as r,s as c,b as s,M as u}from"./instrument-718ba3ce.js";const f="_skeleton_vtlsh_1",m="_detail_vtlsh_12",p="_container_vtlsh_20",a={skeleton:f,detail:m,container:p},y=i({name:"music-list",setup(){const n=l(),t=d({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:e.staff,base64:""},{state:!1,name:"首调",type:e.firstTone,base64:""},{state:!1,name:"固定调",type:e.fixedTone,base64:""}]});r(()=>{window.appName="colexiu",c.xmlUrl=n.xmlUrl,t.isLoading=!1});const o=async()=>{console.log("渲染完成")};return()=>s("div",{class:a.detail},[s("div",{id:"scrollContainer",class:[a.container,"hideCursor"]},[!t.isLoading&&s(u,{onRendered:o},null)])])}});export{y as default};
+========
+import{d as i,g as l,r as d,E as e,o as r,s as c,b as s,M as u}from"./instrument-2958d7ec.js";const f="_skeleton_vtlsh_1",m="_detail_vtlsh_12",p="_container_vtlsh_20",a={skeleton:f,detail:m,container:p},y=i({name:"music-list",setup(){const n=l(),t=d({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:e.staff,base64:""},{state:!1,name:"首调",type:e.firstTone,base64:""},{state:!1,name:"固定调",type:e.fixedTone,base64:""}]});r(()=>{window.appName="colexiu",c.xmlUrl=n.xmlUrl,t.isLoading=!1});const o=async()=>{console.log("渲染完成")};return()=>s("div",{class:a.detail},[s("div",{id:"scrollContainer",class:[a.container,"hideCursor"]},[!t.isLoading&&s(u,{onRendered:o},null)])])}});export{y as default};
+>>>>>>>> feature-tianyong:dist/js/index-579920f7.js

File diff suppressed because it is too large
+ 1 - 0
dist/js/index-58b220bc.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-592807b1.js


+ 4 - 0
dist/js/index-8792264e.js

@@ -1 +1,5 @@
+<<<<<<<< HEAD:dist/js/index-8792264e.js
 import{d as i,g as l,r as d,E as e,o as r,s as c,b as s,M as u}from"./instrument-718ba3ce.js";const f="_skeleton_vtlsh_1",m="_detail_vtlsh_12",p="_container_vtlsh_20",a={skeleton:f,detail:m,container:p},y=i({name:"music-list",setup(){const n=l(),t=d({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:e.staff,base64:""},{state:!1,name:"首调",type:e.firstTone,base64:""},{state:!1,name:"固定调",type:e.fixedTone,base64:""}]});r(()=>{window.appName="colexiu",c.xmlUrl=n.xmlUrl,t.isLoading=!1});const o=async()=>{console.log("渲染完成")};return()=>s("div",{class:a.detail},[s("div",{id:"scrollContainer",class:[a.container,"hideCursor"]},[!t.isLoading&&s(u,{onRendered:o},null)])])}});export{y as default};
+========
+import{d as i,g as l,r as d,E as e,o as r,s as c,b as s,M as u}from"./instrument-2958d7ec.js";const f="_skeleton_vtlsh_1",m="_detail_vtlsh_12",p="_container_vtlsh_20",a={skeleton:f,detail:m,container:p},y=i({name:"music-list",setup(){const n=l(),t=d({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:e.staff,base64:""},{state:!1,name:"首调",type:e.firstTone,base64:""},{state:!1,name:"固定调",type:e.fixedTone,base64:""}]});r(()=>{window.appName="colexiu",c.xmlUrl=n.xmlUrl,t.isLoading=!1});const o=async()=>{console.log("渲染完成")};return()=>s("div",{class:a.detail},[s("div",{id:"scrollContainer",class:[a.container,"hideCursor"]},[!t.isLoading&&s(u,{onRendered:o},null)])])}});export{y as default};
+>>>>>>>> feature-tianyong:dist/js/index-579920f7.js

File diff suppressed because it is too large
+ 1 - 0
dist/js/index-c1d09b80.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-cd8df457.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-legacy-12cfdb62.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-legacy-19204220.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-legacy-8e239c95.js


+ 4 - 0
dist/js/index-legacy-9afa0613.js

@@ -1 +1,5 @@
+<<<<<<<< HEAD:dist/js/index-legacy-9afa0613.js
 System.register(["./instrument-legacy-67edbb78.js"],(function(e,t){"use strict";var i,n,a,o,r,s,l,d,c=document.createElement("style");return c.textContent="._skeleton_vtlsh_1{position:fixed;left:0;top:0;width:100vw;height:100vh;padding:.53333rem .8rem;background-color:#fff;z-index:1000;--van-skeleton-paragraph-height: .8rem}._detail_vtlsh_12{width:100vw;height:100vh;overflow:hidden;overflow-y:auto;--header-height: 1.65333rem;background:var(--container-background)}._detail_vtlsh_12 ._container_vtlsh_20{margin:0 .26667rem;border-radius:.26667rem}._detail_vtlsh_12 #musicAndSelection{overflow:initial!important;height:initial!important;max-height:initial!important}\n",document.head.appendChild(c),{setters:[e=>{i=e.d,n=e.g,a=e.r,o=e.E,r=e.o,s=e.s,l=e.b,d=e.M}],execute:function(){const t="_detail_vtlsh_12",c="_container_vtlsh_20";e("default",i({name:"music-list",setup(){const e=n(),i=a({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:o.staff,base64:""},{state:!1,name:"首调",type:o.firstTone,base64:""},{state:!1,name:"固定调",type:o.fixedTone,base64:""}]});r((()=>{window.appName="colexiu",s.xmlUrl=e.xmlUrl,i.isLoading=!1}));const h=async()=>{console.log("渲染完成")};return()=>l("div",{class:t},[l("div",{id:"scrollContainer",class:[c,"hideCursor"]},[!i.isLoading&&l(d,{onRendered:h},null)])])}}))}}}));
+========
+System.register(["./instrument-legacy-2a2ddf2b.js"],(function(e,t){"use strict";var i,n,a,o,r,s,l,d,c=document.createElement("style");return c.textContent="._skeleton_vtlsh_1{position:fixed;left:0;top:0;width:100vw;height:100vh;padding:.53333rem .8rem;background-color:#fff;z-index:1000;--van-skeleton-paragraph-height: .8rem}._detail_vtlsh_12{width:100vw;height:100vh;overflow:hidden;overflow-y:auto;--header-height: 1.65333rem;background:var(--container-background)}._detail_vtlsh_12 ._container_vtlsh_20{margin:0 .26667rem;border-radius:.26667rem}._detail_vtlsh_12 #musicAndSelection{overflow:initial!important;height:initial!important;max-height:initial!important}\n",document.head.appendChild(c),{setters:[e=>{i=e.d,n=e.g,a=e.r,o=e.E,r=e.o,s=e.s,l=e.b,d=e.M}],execute:function(){const t="_detail_vtlsh_12",c="_container_vtlsh_20";e("default",i({name:"music-list",setup(){const e=n(),i=a({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:o.staff,base64:""},{state:!1,name:"首调",type:o.firstTone,base64:""},{state:!1,name:"固定调",type:o.fixedTone,base64:""}]});r((()=>{window.appName="colexiu",s.xmlUrl=e.xmlUrl,i.isLoading=!1}));const h=async()=>{console.log("渲染完成")};return()=>l("div",{class:t},[l("div",{id:"scrollContainer",class:[c,"hideCursor"]},[!i.isLoading&&l(d,{onRendered:h},null)])])}}))}}}));
+>>>>>>>> feature-tianyong:dist/js/index-legacy-f65aef84.js

File diff suppressed because it is too large
+ 1 - 0
dist/js/index-legacy-c5adc661.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-legacy-c7a08155.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/index-legacy-d2b596be.js


+ 5 - 0
dist/js/index-legacy-f65aef84.js

@@ -0,0 +1,5 @@
+<<<<<<<< HEAD:dist/js/index-legacy-9afa0613.js
+System.register(["./instrument-legacy-67edbb78.js"],(function(e,t){"use strict";var i,n,a,o,r,s,l,d,c=document.createElement("style");return c.textContent="._skeleton_vtlsh_1{position:fixed;left:0;top:0;width:100vw;height:100vh;padding:.53333rem .8rem;background-color:#fff;z-index:1000;--van-skeleton-paragraph-height: .8rem}._detail_vtlsh_12{width:100vw;height:100vh;overflow:hidden;overflow-y:auto;--header-height: 1.65333rem;background:var(--container-background)}._detail_vtlsh_12 ._container_vtlsh_20{margin:0 .26667rem;border-radius:.26667rem}._detail_vtlsh_12 #musicAndSelection{overflow:initial!important;height:initial!important;max-height:initial!important}\n",document.head.appendChild(c),{setters:[e=>{i=e.d,n=e.g,a=e.r,o=e.E,r=e.o,s=e.s,l=e.b,d=e.M}],execute:function(){const t="_detail_vtlsh_12",c="_container_vtlsh_20";e("default",i({name:"music-list",setup(){const e=n(),i=a({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:o.staff,base64:""},{state:!1,name:"首调",type:o.firstTone,base64:""},{state:!1,name:"固定调",type:o.fixedTone,base64:""}]});r((()=>{window.appName="colexiu",s.xmlUrl=e.xmlUrl,i.isLoading=!1}));const h=async()=>{console.log("渲染完成")};return()=>l("div",{class:t},[l("div",{id:"scrollContainer",class:[c,"hideCursor"]},[!i.isLoading&&l(d,{onRendered:h},null)])])}}))}}}));
+========
+System.register(["./instrument-legacy-2a2ddf2b.js"],(function(e,t){"use strict";var i,n,a,o,r,s,l,d,c=document.createElement("style");return c.textContent="._skeleton_vtlsh_1{position:fixed;left:0;top:0;width:100vw;height:100vh;padding:.53333rem .8rem;background-color:#fff;z-index:1000;--van-skeleton-paragraph-height: .8rem}._detail_vtlsh_12{width:100vw;height:100vh;overflow:hidden;overflow-y:auto;--header-height: 1.65333rem;background:var(--container-background)}._detail_vtlsh_12 ._container_vtlsh_20{margin:0 .26667rem;border-radius:.26667rem}._detail_vtlsh_12 #musicAndSelection{overflow:initial!important;height:initial!important;max-height:initial!important}\n",document.head.appendChild(c),{setters:[e=>{i=e.d,n=e.g,a=e.r,o=e.E,r=e.o,s=e.s,l=e.b,d=e.M}],execute:function(){const t="_detail_vtlsh_12",c="_container_vtlsh_20";e("default",i({name:"music-list",setup(){const e=n(),i=a({isLoading:!0,isProductLoading:!1,product:[{state:!1,name:"五线谱",type:o.staff,base64:""},{state:!1,name:"首调",type:o.firstTone,base64:""},{state:!1,name:"固定调",type:o.fixedTone,base64:""}]});r((()=>{window.appName="colexiu",s.xmlUrl=e.xmlUrl,i.isLoading=!1}));const h=async()=>{console.log("渲染完成")};return()=>l("div",{class:t},[l("div",{id:"scrollContainer",class:[c,"hideCursor"]},[!i.isLoading&&l(d,{onRendered:h},null)])])}}))}}}));
+>>>>>>>> feature-tianyong:dist/js/index-legacy-f65aef84.js

File diff suppressed because it is too large
+ 1 - 0
dist/js/instrument-2958d7ec.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/instrument-718ba3ce.js


File diff suppressed because it is too large
+ 0 - 0
dist/js/instrument-legacy-2a2ddf2b.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/instrument-legacy-67edbb78.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/modeView-cca57189.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/modeView-fa49d224.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/modeView-legacy-5fabaac5.js


File diff suppressed because it is too large
+ 1 - 0
dist/js/modeView-legacy-76473d15.js


+ 1 - 1
src/helpers/calcSpeed.ts

@@ -116,7 +116,7 @@ export type GradualItem = {
  * @param xml 始终按照第一分谱进行减慢速度的计算
  */
 export const getGradualLengthByXml = (xml: string) => {
-	const firstPartXml = onlyVisible(xml, 0)
+	const firstPartXml = onlyVisible(xml, 0, 'calc')
 	console.time('DOMParser 耗时2')
 	const xmlParse = new DOMParser().parseFromString(firstPartXml, "text/xml");
 	console.timeEnd('DOMParser 耗时2')

+ 19 - 1
src/helpers/formateMusic.ts

@@ -361,7 +361,7 @@ export const isRepeatWord = (text: string): boolean => {
 	return false;
 };
 
-export const onlyVisible = (xml: string, partIndex: number): string => {
+export const onlyVisible = (xml: string, partIndex: number, resourceType?: string): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
 	const detailId = state.examSongId + "";
@@ -500,6 +500,16 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
 
 			// 最后一个小节的结束线元素不在最后 调整
 			if (part && part.getAttribute("id") === id) {
+				if (!resourceType) {
+					const backups = Array.from(part.getElementsByTagName('backup')) || []
+					for (let backup of backups) {
+						// @ts-ignore
+						if (backup && backup?.getElementsByTagName('duration')?.length) {
+							state.isSingleMutliTrack = true;
+							break;
+						}
+					}
+				}
 				const barlines = part.getElementsByTagName("barline");
 				const lastParent = barlines[barlines.length - 1]?.parentElement;
 				if (lastParent?.lastElementChild?.tagName !== "barline") {
@@ -901,6 +911,12 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 				return trackName?.trim() === firstTrackName
 			});
 		}
+		// 多轨合并显示,或者单轨多声部的情况,需要过滤掉下半边声部的音符
+		if ( ((!state.isCombineRender && state.isSingleMutliTrack) || state.isCombineRender) && iterator.currentVoiceEntries.length) {
+			iterator.currentVoiceEntries = iterator.currentVoiceEntries.filter((item: any) => {
+				return item.ParentVoice.voiceId === 1
+			});
+		}
 		let minIndex = 0, elRealValue = 0
 		for (let index = 0; index < iterator.currentVoiceEntries.length; index++) {
 			const element = iterator.currentVoiceEntries[index];
@@ -913,6 +929,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			if (element.notes[0].length.realValue < elRealValue) {
 				minIndex = index
 			}
+			// console.log(element.notes[0].SourceMeasure.MeasureNumberXML,element.notes[0].playbackInstrumentId,element.ParentVoice.voiceId)
 			elRealValue = element.notes[0].length.realValue
 		}
 		if (minIndex !== 0 && state.isCombineRender && iterator.currentVoiceEntries[minIndex]) {
@@ -996,6 +1013,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
 			const { beatUnit="quarter", dotted=false, tempoInBpm=state.originSpeed } = currentRealTempo
 			const speedBeatUnit = beatUnitTo(beatUnit, dotted)
 			_notes.push({
+				measureNum: note?.sourceMeasure?.MeasureNumberXML,
 				note,
 				iterator: { ...iterator },
 				currentTime,

+ 10 - 7
src/page-instrument/header-top/index.tsx

@@ -32,6 +32,7 @@ import { smoothAnimationState } from "../view-detail/smoothAnimation";
 import { isMusicList, musicListShow } from "../component/the-music-list";
 import { EvaluatingDriver, FollowDriver, PractiseDriver } from "../custom-plugins/guide-driver";
 import { fingerRef } from "/src/page-instrument/view-detail/index"
+import { handleLoadBeatMusic } from "/src/view/audio-list"
 
 const ModeView = defineAsyncComponent(() =>
   import('./modeView')
@@ -122,7 +123,7 @@ let isClickMode = false;
  * @param oldPlaySource  没改变之前的播放类型
  * @param isforceReset   是否强制刷新播放状态 模式times时值改变时候也刷新
  */
-export function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: IPlayState, isforceReset?: boolean) {
+export async function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: IPlayState, isforceReset?: boolean) {
   const isModeChange = modeChangeHandleTimes(oldPlayType, oldPlaySource);
   // 没有切换的时候 不处理下面的
   if (isModeChange) {
@@ -137,6 +138,8 @@ export function handlerModeChange(oldPlayType: "play" | "sing", oldPlaySource: I
     // 隐藏重播按钮
     resetBtn && (resetBtn.value.display = false);
   }
+  // 节拍器音频加载
+  await handleLoadBeatMusic()
   // 当模式改变的时候  放在这里是因为需要等谱面加载完成之后再提示(点击按钮模式切换才提示)
   if (isClickMode) {
     showToast({
@@ -376,14 +379,14 @@ export default defineComponent({
       // 总谱渲染在播放过程中 不能切换 
       if(state.isCombineRender && state.playState === "play") return { display: true, disabled: true } 
       if (!state.isAppPlay) {
+        // 播放过程中不能切换
+        if (state.playState === "play") {
+          return { display: true, disabled: true };
+        }
         if (state.playType === "play") {
           // 原声, 伴奏 少一个,就不能切换
           if (state.music && state.accompany) return { display: true, disabled: false };
         } else {
-          // 播放过程中不能切换
-          if (state.playState === "play") {
-            return { display: true, disabled: true };
-          }
           // 范唱
           let index = 0;
           state.fanSong && index++;
@@ -808,7 +811,7 @@ export default defineComponent({
               id={state.platform === IPlatform.PC ? "teacherTop-1" : "studnetT-1"}
               style={{ display: originBtn.value.display ? "" : "none" }}
               class={["driver-3", styles.btn, originBtn.value.disabled && styles.disabled, state.playType === "play" ? styles.playSource : styles.songSource]}
-              onClick={() => {
+              onClick={async () => {
                 const oldPlayType = state.playType;
                 const oldPlaySource = state.playSource;
                 if (state.playType === "play") {
@@ -822,7 +825,7 @@ export default defineComponent({
                     state.playSource = state.fanSong ? "music" : "background";
                   }
                 }
-                handlerModeChange(oldPlayType, oldPlaySource);
+                await handlerModeChange(oldPlayType, oldPlaySource);
                 // 总谱 并且开启了单个声轨音频时候
                 if(state.isCombineRender && state.playSource === "background") {
                   audioData.combineIndex = -1

+ 4 - 1
src/page-instrument/header-top/settting/index.tsx

@@ -17,6 +17,7 @@ import Dragbom from "/src/view/plugins/useDrag/dragbom";
 import { storeData } from "/src/store";
 import { getGuidance, setGuidance } from "../../custom-plugins/guide-page/api";
 import { metronomeData } from "/src/helpers/metronome";
+import { handleLoadBeatMusic } from "/src/view/audio-list"
 
 export default defineComponent({
 	name: "settting",
@@ -140,7 +141,7 @@ export default defineComponent({
                             </div>
                         </div>
                         {
-                            state.modeType === 'practise' && state.playType === "sing" && state.mingSong && state.mingSongGirl &&
+                            state.modeType === 'practise' && state.playSource === "mingSong" && state.mingSong && state.mingSongGirl &&
                             <div class={styles.cellBox}>
                                 <div class={styles.tit}>唱名类型</div>
                                 <div class={styles.radioBox}>
@@ -151,6 +152,8 @@ export default defineComponent({
                                                     return
                                                 }
                                                 audioData.mingSongType = item.value as 0|1
+                                                // 加载节拍器音频
+                                                handleLoadBeatMusic()
                                                 changeMingSongType()
                                             } }>{item.name}</div>
                                         })

+ 4 - 0
src/page-instrument/header-top/speed/index.tsx

@@ -9,6 +9,7 @@ import { getQuery } from "/src/utils/queryString";
 import { api_createMusicPlayer, api_updateMusicPlayer, api_checkSocketStatus } from "/src/helpers/communication";
 import { storeData } from "/src/store";
 import { data as workData } from "/src/page-instrument/custom-plugins/work-index";
+import { handleLoadBeatMusic } from "/src/view/audio-list"
 
 export default defineComponent({
 	name: "speed",
@@ -63,7 +64,10 @@ export default defineComponent({
 		})
 		// 切换节拍器
 		const toggleSwitch = async (res: any) => {
+			switchLoading.value = true;
 			metronomeDisable.value = res;
+			await handleLoadBeatMusic()
+			switchLoading.value = false;
 			return
 			switchLoading.value = true;
 			try {

+ 1 - 1
src/page-instrument/view-detail/index.tsx

@@ -613,7 +613,7 @@ export default defineComponent({
           )}
         </div>
         {/* 曲目渲染完成,再去下载mp3资源 */}
-        {!detailData.isLoading && !detailData.skeletonLoading && <AudioList />}
+        {!detailData.isLoading && <AudioList />}
 
         {/* {!detailData.isLoading && <TheAudio src={tickWav} />} */}
 

+ 24 - 3
src/state.ts

@@ -24,6 +24,7 @@ import { api_lessonTrainingTrainingStudentDetail } from "/src/page-instrument/ap
 import { undoData, moveData } from "/src/view/plugins/move-music-score"
 import { HANDLE_WORK_ADD } from "/src/page-instrument/custom-plugins/work-index";
 import { speedBeatTo, unitImgs } from "/src/helpers/beatConfig"
+import IndexedDBService from "/src/utils/indexedDB";
 
 const query: any = getQuery();
 
@@ -606,6 +607,8 @@ const state = reactive({
   speedIcon: 'speed3', // 默认取1/4拍的图片
   /** xml的第一个measure标签的number */
   firstMeasureNumber: 1,
+  /** 是否是单声轨多声部的声轨 */
+  isSingleMutliTrack: false,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -1465,16 +1468,34 @@ export const getMusicDetail = async (id: string, type?: string) => {
 };
 
 
+// 判断有没有xml缓存,有则直接使用
+const queryMusicXml = async (id: string, xmlUr: string) => {
+  let xmlString = ''
+  const dbService = new IndexedDBService("MyDatabase", "MyStore");
+  console.time('缓存获取xml')
+  const storeXmlData = await dbService.get(id).then((data) => data );
+  if (storeXmlData && storeXmlData.xmlString) {
+    xmlString = storeXmlData && storeXmlData.xmlString
+    console.timeEnd('缓存获取xml')
+    // 使用完后删除数据
+    dbService.delete(id)
+  } else {
+    xmlString = await fetch(xmlUr).then((response) => response.text());
+  }
+  return xmlString;
+}
+
 const getMusicInfo = async (res: any) => {
   // 是否支持总谱
   state.isScoreRender = res.data?.isScoreRender
   // 是否默认显示总谱
   state.defaultScoreRender = res.data?.defaultScoreRender
-  /* 获取声轨列表 */
-  let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
+  // let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
+  let xmlString: string = await queryMusicXml(res.data.bizId + "", res.data.xmlFileUrl);
   xmlString = xmlAddPartName(xmlString);
   downloadXmlStr.value = xmlString //给musice-score 赋值xmlString 以免加载2次
-  const tracks = xmlToTracks(xmlString) //获取声轨列表  
+  /* 获取声轨列表 */
+  const tracks = xmlToTracks(xmlString)
   // 是否显示节拍器  (管乐迷 默认显示节拍器)
   //state.isMixBeat = res.data?.isMixBeat  
   /* 设置partIndex */

+ 117 - 0
src/utils/indexedDB.ts

@@ -0,0 +1,117 @@
+interface DataInfo {
+    id: number | string; // 主键
+    xmlString: string;
+    xmlDoc: Document;
+}
+
+export default class IndexedDBService<T> {
+    private dbName: string;
+    private storeName: string;
+    private version: number;
+  
+    constructor(dbName: string, storeName: string, version: number = 1) {
+      this.dbName = dbName;
+      this.storeName = storeName;
+      this.version = version;
+    }
+  
+    // 初始化数据库
+    private async init(): Promise<IDBDatabase> {
+      return new Promise((resolve, reject) => {
+        const request = indexedDB.open(this.dbName, this.version);
+  
+        // 数据库第一次创建或版本号升级时触发
+        request.onupgradeneeded = () => {
+          const db = request.result;
+          // 创建对象存储(类似于表)
+          if (!db.objectStoreNames.contains(this.storeName)) {
+            db.createObjectStore(this.storeName, { keyPath: "id" });
+          }
+        };
+  
+        // 返回数据库实例
+        request.onsuccess = () => resolve(request.result);
+        // 捕获错误
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 添加或者更新数据
+    async save(data: T): Promise<void> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readwrite");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.put(data);
+  
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 获取某条数据
+    async get(id: number | string): Promise<T | undefined> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readonly");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.get(id);
+  
+        request.onsuccess = () => resolve(request.result as T);
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 删除数据
+    async delete(id: number | string): Promise<void> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readwrite");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.delete(id);
+  
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 查询所有数据
+    async getAll(): Promise<T[]> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readonly");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.getAll();
+  
+        request.onsuccess = () => resolve(request.result as T[]);
+        request.onerror = () => reject(request.error);
+      });
+    }
+
+    // 清空某个 Object Store 中的所有数据
+    async clearAllData(): Promise<void> {
+      const db = await this.init();
+    
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readwrite");
+        const store = transaction.objectStore(this.storeName);
+    
+        const request = store.clear(); // 清空数据
+    
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+
+    // 删除整个数据库
+    async deleteDatabase(): Promise<void> {
+      return new Promise((resolve, reject) => {
+        const request = indexedDB.deleteDatabase(this.dbName); // 删除整个数据库
+    
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+}
+  
+  

+ 111 - 20
src/view/audio-list/index.tsx

@@ -214,7 +214,8 @@ const createAudio = (src?: string): Promise<HTMLAudioElement | null> => {
 		return Promise.resolve(null)
 	}
 	return new Promise((resolve) => {
-		const a = new Audio(src + '?v=' + Date.now());
+		// const a = new Audio(src + '?v=' + Date.now());
+		const a = new Audio(src);
 		a.onloadedmetadata = () => {
 			resolve(a);
 		};
@@ -237,45 +238,135 @@ const createAudio = (src?: string): Promise<HTMLAudioElement | null> => {
 };
 
 // 合成节拍器资源
-const crunker = new Crunker()
-async function mergeBeatAudio(music?:string, accompany?:string){
-	let beatMusic, beatAccompany
+let CrunkerInstance: Crunker
+async function mergeBeatAudio(music?:string){
+	let beatMusic
 	if(!state.isMixBeat) {
-		return [beatMusic, beatAccompany]
+		return beatMusic
+	}
+	if(!music){
+		return beatMusic
 	}
 	console.time("音频合成时间")
 	try{
+		/* 音频合成 */
+		if(!CrunkerInstance){
+			CrunkerInstance = new Crunker()
+		}
 		console.time("音频加载时间")
-		const [musicBuff, accompanyBuff, tickBuff, tockBuff] = await crunker.fetchAudio(music?`${music}?v=${Date.now()}`:undefined, accompany?`${accompany}?v=${Date.now()}`:undefined, tickMp3, tockMp3)
+		const [musicBuff, tickBuff, tockBuff] = await CrunkerInstance.fetchAudio(music?`${music}}`:undefined, tickMp3, tockMp3)
 		console.timeEnd("音频加载时间")
 		// 计算音频空白时间
-		const silenceDuration = musicBuff&&!state.isEvxml ? crunker.calculateSilenceDuration(musicBuff) : 0
-		const silenceBgDuration = accompanyBuff&&!state.isEvxml ? crunker.calculateSilenceDuration(accompanyBuff) : 0
-		console.log(`音频空白时间:${silenceDuration};${silenceBgDuration}`)
+		const silenceDuration = musicBuff&&!state.isEvxml ? CrunkerInstance.calculateSilenceDuration(musicBuff) : 0
+		console.log(`音频空白时间:${silenceDuration}`)
 		const beats:AudioBuffer[] = []
 		const beatsTime:number[] = []
-		const beatsBgTime:number[] = []
 		metronomeData.metroMeasure.map(measures=>{
 			measures.map((item:any)=>{
 				beats.push(item.isTick?tickBuff!:tockBuff!)
 				beatsTime.push(item.time + silenceDuration) // xml 计算的时候 加上空白的时间
-				beatsBgTime.push(item.time + silenceBgDuration) // xml 计算的时候 加上空白的时间 没有背景不赋值
 			})
 		})
 		console.time("音频合并时间")
-		const musicBuffMeg = musicBuff && crunker.mergeAudioBuffers([musicBuff,...beats],[0,...beatsTime])
-		const accompanyBuffMeg = accompanyBuff && crunker.mergeAudioBuffers([accompanyBuff,...beats],[0,...beatsBgTime])
+		const musicBuffMeg = musicBuff && CrunkerInstance.mergeAudioBuffers([musicBuff,...beats],[0,...beatsTime])
 		console.timeEnd("音频合并时间")
 		console.time("音频audioDom生成时间")
-		beatMusic = musicBuffMeg && crunker.exportAudioElement(musicBuffMeg)
-		beatAccompany = accompanyBuffMeg && crunker.exportAudioElement(accompanyBuffMeg)
+		beatMusic = musicBuffMeg && CrunkerInstance.exportAudioElement(musicBuffMeg)
 		console.timeEnd("音频audioDom生成时间")
 	}catch(err){
 		console.log(err)
 	}
 	console.timeEnd("音频合成时间")
-	return [beatMusic, beatAccompany]
+	return beatMusic
+}
+
+// 处理加载节拍器音频
+export const handleLoadBeatMusic = async () => {
+	if(metronomeData.disable) {
+		return
+	}
+	const playType = state.playType
+	const playSource = state.playSource
+	const mingSongType = audioData.mingSongType
+	// 当前模式下 如果已经有合成音频了,就不走合成逻辑了
+	let isBeatMusic = false
+	let currentMusic  //当前的音频
+	const beatPlayObj = {
+		"play_music":"beatSongEle",
+		"play_background":"beatBackgroundEle",
+		"sing_music":"beatFanSongEle",
+		"sing_background":"beatBanSongEle"
+	}
+	const playObj = {
+		"play_music":"music",
+		"play_background":"accompany",
+		"sing_music":"fanSong",
+		"sing_background":"banSong",
+	}
+	if(playSource === "mingSong") {
+		// 当男声女声都有的时候 才区分
+		if(state.mingSong && state.mingSongGirl){
+			isBeatMusic = mingSongType === 1 ? !!audioData.mingSongTypeCollection.beatMingSongEle : !!audioData.mingSongTypeCollection.beatMingSongGirlEle
+			currentMusic = mingSongType === 1 ? state.mingSong : state.mingSongGirl
+		}else{
+			isBeatMusic = !!audioData.mingSongTypeCollection.beatMingSongEle
+			currentMusic = state.mingSong
+		}
+	}else{
+		// @ts-ignore
+		isBeatMusic = !!audioData.songCollection[beatPlayObj[`${playType}_${playSource}`]]
+		// @ts-ignore
+		currentMusic = state[playObj[`${playType}_${playSource}`]]
+	}
+	if(isBeatMusic || !currentMusic){
+		return
+	}
+	state.loadingText = "音频资源加载中,请稍后…"
+	state.isLoading = true
+	const musicAudio = await mergeBeatAudio(currentMusic) as any
+	const playEleObj = {
+		"play_music":"beatSongEle",
+		"play_background":"beatBackgroundEle",
+		"sing_music":"beatFanSongEle",
+		"sing_background":"beatBanSongEle"
+	}
+	// 给音频赋值
+	if(playSource === "mingSong"){
+		// 当男声女声都有的时候 才区分
+		if(state.mingSong && state.mingSongGirl){
+			if(mingSongType === 1) {
+				audioData.mingSongTypeCollection.beatMingSongEle = musicAudio
+			}else {
+				audioData.mingSongTypeCollection.beatMingSongGirlEle = musicAudio
+			}
+		}else{
+			audioData.songCollection.beatMingSongEle = musicAudio
+			audioData.mingSongTypeCollection.beatMingSongEle = musicAudio
+		}
+		if(musicAudio){
+			musicAudio.addEventListener("play", onPlay);
+			musicAudio.addEventListener("ended", onEnded);
+		}
+		changeMingSongType()
+	}else{
+		if(playType === "play" && !audioData.songCollection.beatSongEle && !audioData.songCollection.beatBackgroundEle){
+			if(musicAudio){
+				musicAudio.addEventListener("play", onPlay);
+				musicAudio.addEventListener("ended", onEnded);
+			}
+		}		
+		if(playType === "sing" && !audioData.songCollection.beatFanSongEle && !audioData.songCollection.beatBanSongEle){
+			if(musicAudio){
+				musicAudio.addEventListener("play", onPlay);
+				musicAudio.addEventListener("ended", onEnded);
+			}
+		}
+		// @ts-ignore
+		audioData.songCollection[playEleObj[`${playType}_${playSource}`]] = musicAudio
+	}
+	state.isLoading = false
 }
+
 // 切换对应的声轨,并且配置当前的audio
 export async function changeCombineAudio (combineIndex: number){
 	// 重复点击的时候取消选中 原音
@@ -305,7 +396,7 @@ export async function changeCombineAudio (combineIndex: number){
 		audioData.combineMusicEles.push(...itemMusic)
 	}else{
 		const music = await createAudio(musicUrl)
-		const [beatMusic] = await mergeBeatAudio(musicUrl)
+		const beatMusic = await mergeBeatAudio(musicUrl)
 		// 当没有背景音的时候 需要绑定事件
 		if(!state.accompany){
 			if(music){
@@ -455,7 +546,7 @@ export default defineComponent({
 			if(state.isPreView){
 				state.isLoading = false;
 				return
-			}			
+			}
 			if (state.playMode !== "MIDI") {
 				console.time("音频加载时间123")
 				// 处理音源
@@ -501,7 +592,7 @@ export default defineComponent({
 				}
 				// 处理带节拍器的音源
 				//const [beatMusic, beatAccompany, beatFanSong, beatBanSong, beatMingSong, beatMingSongGirl] = await loadBeatAudio()
-				// 客户端合成节拍器
+				/*// 客户端合成节拍器
 				const [beatMusic, beatAccompany, beatFanSong, beatBanSong, beatMingSong, beatMingSongGirl] = await mergeBeatAudio(state.music, state.accompany)
 				Object.assign(audioData.songCollection, {
 					beatSongEle:beatMusic,
@@ -539,7 +630,7 @@ export default defineComponent({
 				if(beatMingSongGirl){
 					beatMingSongGirl.addEventListener("play", onPlay);
 					beatMingSongGirl.addEventListener("ended", onEnded);
-				}
+				} */
 				// 给男声女声赋值
 				const userGender = storeData.user.gender
 				// 当不为null 和undefined的时候 取userGender的值

+ 15 - 2
src/view/plugins/toggleMusicSheet/index.tsx

@@ -10,6 +10,8 @@ import useDrag from "/src/view/plugins/useDrag/index";
 import Dragbom from "/src/view/plugins/useDrag/dragbom";
 import { setGuidance } from "/src/page-instrument/custom-plugins/guide-page/api";
 import { storeData } from "/src/store";
+import { xmlDocRef, downloadXmlStr } from "/src/view/music-score"
+import IndexedDBService from "/src/utils/indexedDB";
 
 export const toggleMusicSheet = reactive({
   show: false,
@@ -51,7 +53,15 @@ export default defineComponent({
       }
     })
 
-    const switchMusic = (partIndexs: number[]) => {
+    // 切换的时候存储处理过后的xml
+    const storeXmlData = async () => {
+      const dbService = new IndexedDBService("MyDatabase", "MyStore");
+      await dbService.save({ id: state.examSongId, xmlString: downloadXmlStr.value })
+        .then(() => dbService.get(state.examSongId))
+        .then((data) => console.log("数据:", data));
+    }
+
+    const switchMusic = async (partIndexs: number[]) => {
       const index = partIndexs.join(",") as any
       // 暂停播放
       togglePlay("paused");
@@ -79,7 +89,10 @@ export default defineComponent({
           'part-index': index,
           'part-name': ''
         })
-      console.log(_url)
+      // const blob2 = new Blob([downloadXmlStr.value], { type: "text/html" });
+      // console.log(_url,xmlDocRef.value,downloadXmlStr.value)
+      
+      await storeXmlData()
       location.href = _url
     }
 

File diff suppressed because it is too large
+ 1 - 0
stats.html


Some files were not shown because too many files changed in this diff