Quellcode durchsuchen

feat: 合奏切换声轨

TIANYONG vor 1 Jahr
Ursprung
Commit
d3c3afa66e

+ 1 - 1
dev-dist/sw.js

@@ -82,7 +82,7 @@ define(['./workbox-5357ef54'], (function (workbox) { 'use strict';
     "revision": "3ca0b8505b4bec776b69afdba2768812"
   }, {
     "url": "index.html",
-    "revision": "0.or3kugpmq98"
+    "revision": "0.mvanbje1bt"
   }], {});
   workbox.cleanupOutdatedCaches();
   workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

+ 1 - 1
public/version.json

@@ -1 +1 @@
-{"version":1706691199878}
+{"version":1706853166573}

+ 275 - 0
src/utils/index.ts

@@ -373,3 +373,278 @@ export function checkUrlType(urlType: string) {
   }
   return 'video';
 }
+
+
+const instruments: any = {
+  'Acoustic Grand Piano': '大钢琴',
+  'Bright Acoustic Piano': '明亮的钢琴',
+  'Electric Grand Piano': '电钢琴',
+  'Rhodes Piano': '柔和的电钢琴',
+  'Chorused Piano': '加合唱效果的电钢琴',
+  Harpsichord: '羽管键琴',
+  Clavichord: '科拉维科特琴',
+  Celesta: '钢片琴',
+  Glockenspiel: '钢片琴',
+  'Music box': '八音盒',
+  Vibraphone: '颤音琴',
+  Marimba: '马林巴',
+  Xylophone: '木琴',
+  'Tubular Bells': '管钟',
+  Dulcimer: '大扬琴',
+  'Hammond Organ': '击杆风琴',
+  'Percussive Organ': '打击式风琴',
+  'Rock Organ': '摇滚风琴',
+  'Church Organ': '教堂风琴',
+  'Reed Organ': '簧管风琴',
+  Accordian: '手风琴',
+  Harmonica: '口琴',
+  'Tango Accordian': '探戈手风琴',
+  'Acoustic Guitar': '钢弦吉他',
+  'Electric Guitar': '闷音电吉他',
+  'Overdriven Guitar': '加驱动效果的电吉他',
+  'Distortion Guitar': '加失真效果的电吉他',
+  'Guitar Harmonics': '吉他和音',
+  'Acoustic Bass': '大贝司',
+  'Electric Bass': '电贝司',
+  'Fretless Bass': '无品贝司',
+  'Slap Bass': '掌击',
+  'Synth Bass': '电子合成',
+  Violin: '小提琴',
+  Viola: '中提琴',
+  Cello: '大提琴',
+  Contrabass: '低音大提琴',
+  'Tremolo Strings': '弦乐群颤音音色',
+  'Pizzicato Strings': '弦乐群拨弦音色',
+  'Orchestral Harp': '竖琴',
+  Timpani: '定音鼓',
+  'String Ensemble': '弦乐合奏音色',
+  'Synth Strings': '合成弦乐合奏音色',
+  'Choir Aahs': '人声合唱',
+  'Voice Oohs': '人声',
+  'Synth Voice': '合成人声',
+  'Orchestra Hit': '管弦乐敲击齐奏',
+  Trumpet: '小号',
+  Trombone: '长号',
+  Tuba: '大号',
+  'Muted Trumpet': '加弱音器小号',
+  'French Horn': '法国号',
+  'Brass Section': '铜管组',
+  'Synth Brass': '合成铜管音色',
+  'Soprano Sax': '高音萨克斯管',
+  'Alto Sax': '中音萨克斯管',
+  'Tenor Sax': '次中音萨克斯管',
+  'Baritone Sax': '低音萨克斯管',
+  Oboe: '双簧管',
+  'English Horn': '英国管',
+  Bassoon: '巴松',
+  Clarinet: '单簧管',
+  'Soprano Saxophone': '高音萨克斯管',
+  'Alto Saxophone': '中音萨克斯管',
+  'Tenor Saxophone': '次中音萨克斯管',
+  'Baritone Saxophone': '低音萨克斯管',
+  Piccolo: '短笛',
+  Flute: '长笛',
+  Recorder: '竖笛',
+  'Soprano Recorder': '高音竖笛',
+  'Pan Flute': '排箫',
+  'Bottle Blow': '瓶木管',
+  Whistle: '口哨声',
+  Ocarina: '陶笛',
+  Lead: '合成主音',
+  'Lead lead': '合成主音',
+  'Pad age': '合成音色',
+  Pad: '合成音色',
+  FX: '合成效果  科幻',
+  Sitar: '西塔尔',
+  Banjo: '班卓琴',
+  Shamisen: '三昧线',
+  Koto: '十三弦筝',
+  Kalimba: '卡林巴',
+  Bagpipe: '风笛',
+  Fiddle: '民族提琴',
+  Shanai: '山奈',
+  'Tinkle Bell': '叮当铃',
+  Agogos: '阿戈戈铃',
+  'Steel Drums': '钢鼓',
+  'Taiko Drum': '太鼓',
+  'Melodic Toms': '嗵嗵鼓',
+  'Synth Drums': '合成鼓',
+  'Reverse Cymbals': '反向镲',
+  'Agogo Bells': '阿戈戈铃',
+  'Taiko Drums': '太鼓',
+  Bongos: '邦戈鼓',
+  'Bongo Bell': '邦戈铃',
+  Congas: '康加鼓',
+  Guiro: '刮壶',
+  'Guitar Fret Noise': '吉他换把杂音',
+  'Breath Noise': '呼吸声',
+  Seashore: '海浪声',
+  'Bird Tweet': '鸟鸣',
+  'Telephone Ring': '电话铃',
+  Helicopter: '直升机',
+  Applause: '鼓掌声',
+  Gunshot: '枪声',
+  'Acoustic Bass Drum': '大鼓',
+  'Bass Drum': '大鼓',
+  'Side Drum': '小鼓鼓边',
+  'Acoustic Snare': '小鼓',
+  'Hand Claps': '拍手',
+  'Electric Snare': '小鼓',
+  'Low Floor Tom': '低音嗵鼓',
+  'Closed Hi-Hat': '闭合踩镲',
+  'High Floor Tom': '高音落地嗵鼓',
+  'Pedal Hi-Hat': '脚踏踩镲',
+  'Low Tom': '低音嗵鼓',
+  'Open Hi-Hat': '开音踩镲',
+  'Low-Mid Tom': '中低音嗵鼓',
+  'Hi Mid Tom': '高音鼓',
+  'Crash Cymbals': '对镲',
+  'High Tom': '高音嗵鼓',
+  'Ride Cymbals': '叮叮镲',
+  'Chinese Cymbals': '中国镲',
+  'Ride Bell': '圆铃',
+  Tambourine: '铃鼓',
+  'Splash Cymbal': '溅音镲',
+  Cowbell: '牛铃',
+  'Crash Cymbal': '强音钹',
+  'Vibra-Slap': '颤音器',
+  'Ride Cymbal': '打点钹',
+  'Hi Bongo': '高音邦戈鼓',
+  'Low Bongo': '低音邦戈鼓',
+  'Mute Hi Conga': '弱音高音康加鼓',
+  'Open Hi Conga': '强音高音康加鼓',
+  'Low Conga': '低音康加鼓',
+  'High Timbale': '高音天巴鼓',
+  'Low Timbale': '低音天巴鼓',
+  'High Agogo': '高音阿戈戈铃',
+  'Low Agogo': '低音阿戈戈铃',
+  Cabasa: '卡巴萨',
+  Maracas: '沙锤',
+  'Short Whistle': '短口哨',
+  'Long Whistle': '长口哨',
+  'Short Guiro': '短刮壶',
+  'Long Guiro': '长刮壶',
+  Claves: '响棒',
+  'Hi Wood Block': '高音木鱼',
+  'Low Wood Block': '低音木鱼',
+  'Mute Triangle': '弱音三角铁',
+  'Open Triangle': '强音三角铁',
+  'Drum Set': '架子鼓',
+  'Hulusi flute': '葫芦丝',
+  Melodica: '口风琴',
+  Nai: '口风琴',
+  'Snare Drum': '小军鼓',
+  Cymbal: '镲',
+  Cymbals: '镲',
+  'Horn in F': '圆号',
+  Triangle: '三角铁',
+  Vibrato: '颤音琴',
+  'Suspend Cymbals': '吊镲',
+  'Suspended Cymbals': '吊镲',
+  'Tom-Toms': '嗵嗵鼓',
+  Bell: '铃铛',
+  Bells: '铃铛',
+  'Alto Clarinet': '中音单簧管',
+  'Bass Clarinet': '低音单簧管',
+  Cornet: '短号',
+  Euphonium: '上低音号',
+  'crash cymbals': '对镲',
+  Castanets: '响板',
+  Shaker: '沙锤',
+  'Mark tree': '音树',
+  Chimes: '管钟',
+  'Mark Tree': '音树',
+  'Tom-toms': '嗵嗵鼓',
+  'Hi-Hat': '踩镲',
+  'Sleigh Bells': '雪橇铃',
+  Flexatone: '弹音器',
+  'Brake drum': '闸鼓',
+  Gong: '锣',
+  'concert tom': '音乐会嗵嗵鼓',
+  'brake drum': '车轮鼓',
+  'finger cymbal': '指钹',
+  'ride cymbal': '叮叮镲',
+  'Concert Toms': '音乐会嗵嗵鼓',
+  Vibraslap: '弹音器',
+  'Wood Blocks': '木鱼',
+  'Temple Blocks': '木鱼',
+  'Wood Block': '木鱼',
+  'Field Drum': '军鼓',
+  'Quad-Toms': '筒鼓',
+  Quads: '筒鼓',
+  'Drums set': '架子鼓',
+  'High Bongo': '邦戈',
+  Timbales: '天巴鼓'
+};
+
+/** 获取分轨名称 */
+export const getInstrumentName = (name = '') => {
+  name = name.toLocaleLowerCase().replace(/ /g, '');
+  if (!name) return '';
+  for (let key in instruments) {
+    const _key = key.toLocaleLowerCase().replace(/ /g, '');
+    if (_key.includes(name)) {
+      return instruments[key];
+    }
+  }
+  for (let key in instruments) {
+    const _key = key.toLocaleLowerCase().replace(/ /g, '');
+    if (name.includes(_key)) {
+      return instruments[key];
+    }
+  }
+  return '';
+};
+
+/**
+ * 乐器排序
+ * 排序顺序:长笛、单簧管、中音单簧管、低音单簧管、高音萨克斯风、中音萨克斯风、次中音萨克斯风、低音萨克斯风、小号、长号、圆号、大号、上低音号
+ * */
+export const sortMusical = (name: string, index: number) => {
+  let sortId = 0;
+  switch (name) {
+    case '长笛':
+      sortId = 1;
+      break;
+    case '单簧管':
+      sortId = 2;
+      break;
+    case '中音单簧管':
+      sortId = 3;
+      break;
+    case '低音单簧管':
+      sortId = 4;
+      break;
+    case '高音萨克斯风':
+      sortId = 5;
+      break;
+    case '中音萨克斯风':
+      sortId = 6;
+      break;
+    case '次中音萨克斯风':
+      sortId = 7;
+      break;
+    case '低音萨克斯风':
+      sortId = 8;
+      break;
+    case '小号':
+      sortId = 9;
+      break;
+    case '长号':
+      sortId = 10;
+      break;
+    case '圆号':
+      sortId = 11;
+      break;
+    case '大号':
+      sortId = 12;
+      break;
+    case '上低音号':
+      sortId = 13;
+      break;
+    default:
+      sortId = index + 14;
+      break;
+  }
+  return sortId;
+};

+ 3 - 3
src/views/natural-resources/components/my-resources/upload-modal/index.tsx

@@ -243,14 +243,14 @@ export default defineComponent({
                     rule={[
                       {
                         required: true,
-                        message: '请选择素材可用乐器',
+                        message: '请选择素材可用声部',
                         trigger: 'change',
                         type: 'array'
                       }
                     ]}>
                     <NSelect
                       v-model:value={item.subjectIds}
-                      placeholder="请选择素材可用乐器(可多选)"
+                      placeholder="请选择素材可用声部(可多选)"
                       options={catchStore.getSubjectList}
                       labelField="name"
                       valueField="id"
@@ -332,7 +332,7 @@ export default defineComponent({
                   <NFormItem showFeedback={false}>
                     <NSelect
                       v-model:value={uploadForms.subjectIds}
-                      placeholder="请选择素材可用乐器(可多选)"
+                      placeholder="请选择素材可用声部(可多选)"
                       options={catchStore.getSubjectList}
                       labelField="name"
                       valueField="id"

Datei-Diff unterdrückt, da er zu groß ist
+ 10 - 0
src/views/xiaoku-music/images/icon_jianpuActive.svg


BIN
src/views/xiaoku-music/images/icon_trans.png


+ 21 - 1
src/views/xiaoku-music/index.module.less

@@ -374,6 +374,11 @@
 
 
     }
+    .transBtn {
+      width: 45px;
+      cursor: pointer;
+      margin-right: 15px;
+    }
   }
 
   .favitor {
@@ -453,7 +458,9 @@
 
 .popSelect {
   font-size: 16px;
-  width: 200px;
+  width: 300px;
+  height: 500px;
+  overflow-y: scroll;
   box-shadow: 0px 2 16px 0px rgba(0, 0, 0, 0.08);
   border-radius: 11px;
   --n-option-height: 34px;
@@ -464,7 +471,20 @@
     }
   }
 }
+.popTrans {
+  font-size: 16px;
+  width: 200px;
+  overflow-y: scroll;
+  box-shadow: 0px 2 16px 0px rgba(0, 0, 0, 0.08);
+  border-radius: 11px;
+  --n-option-height: 34px;
 
+  :global {
+    .n-base-select-option__content {
+      width: 80% !important;
+    }
+  }
+}
 .textBtn {
   .iconArrow {
     display: inline-block;

+ 171 - 12
src/views/xiaoku-music/index.tsx

@@ -7,7 +7,8 @@ import {
   onMounted,
   onUnmounted,
   reactive,
-  ref
+  ref,
+  watch
 } from 'vue';
 import styles from './index.module.less';
 import icon_back from './images/icon_back.png';
@@ -21,7 +22,8 @@ import {
   NModal,
   NPopselect,
   NSpace,
-  NSpin
+  NSpin,
+  NPopover
 } from 'naive-ui';
 import TheSearch from '/src/components/TheSearch';
 import { IMusicItem } from './type';
@@ -33,6 +35,7 @@ import icon_favitor from '/src/common/images/icon-collect-default.png';
 import icon_favitorActive from '/src/common/images/icon-collect-active.png';
 import icon_default from './images/icon_default.png';
 import icon_close from './images/icon-close.png';
+import icon_trans from './images/icon_trans.png'
 import { useRoute, useRouter } from 'vue-router';
 import PlayItem from './component/play-item';
 import PlayLoading from './component/play-loading';
@@ -50,6 +53,8 @@ import TheEmpty from '/src/components/TheEmpty';
 import { state } from '/src/state';
 import { useResizeObserver } from '@vueuse/core';
 import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
+import { getInstrumentName, sortMusical } from '/src/utils'
+
 export default defineComponent({
   name: 'XiaokuMusic',
   setup() {
@@ -79,7 +84,11 @@ export default defineComponent({
       showPreivew: false,
       previewUrl: '',
       showCloseBtn: true,
-      iframeSrc: ''
+      iframeSrc: '',
+      showMusicImg: 'staff' as 'staff' | 'first' | 'fixed', // 显示哪种曲谱
+      trackList: [] as any, // 可筛选的分轨信息
+      showTransBtn: true, // 是否显示转谱按钮
+      trackName: '切换声轨' as any, // 分轨名字
     });
     const showGuide = ref(false);
     const userStore = useUserStore();
@@ -183,6 +192,7 @@ export default defineComponent({
         }
       });
       obv.observe(spinRef.value);
+      analyzeXml();
       window.addEventListener('message', iframeHandle);
     });
     onUnmounted(() => {
@@ -296,11 +306,19 @@ export default defineComponent({
       //   'origin',
       //   `${origin}/instrument/?id=${details.id}&modelType=practise&modeType=json&Authorization=${token}&isPreView=true&part-index=${data.musicInstrumentIndex}`
       // );
+      const musicRenderType =
+        data.showMusicImg === 'first'
+          ? 'firstTone'
+          : data.showMusicImg === 'fixed'
+          ? 'fixedTone'
+          : data.showMusicImg === 'staff'
+          ? 'staff'
+          : 'firstTone';      
       data.iframeSrc = `${vaildMusicScoreUrl()}/instrument/?id=${
         details.id
       }&modelType=practise&modeType=json&Authorization=${token}&isPreView=true&part-index=${
         data.musicInstrumentIndex
-      }`;
+      }&musicRenderType=${musicRenderType}`;
     };
 
     /** 音频控制 */
@@ -323,6 +341,123 @@ export default defineComponent({
         handleFavitor();
       }
     };
+
+    const _actions = [
+      {
+        value: 'staff',
+        label: '五线谱'
+      },
+      {
+        value: 'first',
+        label: '首调'
+      },
+      {
+        value: 'fixed',
+        label: '固定调'
+      }
+    ];
+
+    // 解析xml,获取分轨信息
+    const analyzeXml = async () => {
+      const details: any = data.list[data.listActive];
+      if (details.musicalInstruments?.length > 1) {
+        if (details.xmlFileUrl) {
+          const res = await fetch(details.xmlFileUrl).then((response) => response.text());
+          filterTracks(res)
+        }
+      } else {
+        // showMusicImg: 'first' as 'staff' | 'first' | 'fixed',
+        const { defaultScore, transferFlag } = details.musicalInstruments[0] || {}
+        let musicImgType: 'staff' | 'first' | 'fixed' = 'staff';
+        musicImgType = defaultScore === 'STAVE' ? 'staff' : defaultScore === 'JIAN' ? 'fixed' : defaultScore === 'FIRST' ? 'first' : 'staff'
+        data.showMusicImg = musicImgType
+        data.showTransBtn = transferFlag
+      }
+    };
+
+    // 过滤出能切换的分轨
+    const filterTracks = (xml: any) => {
+      const xmlParse = new DOMParser().parseFromString(xml, 'text/xml');
+      const partList =
+        xmlParse
+          .getElementsByTagName('part-list')?.[0]
+          ?.getElementsByTagName('score-part') || [];
+      const partListNames = Array.from(partList).map(
+        item =>
+          item.getElementsByTagName('part-name')?.[0]?.textContent?.trim() || ''
+      );
+      const parts: any = xmlParse.getElementsByTagName('part');
+
+      /** 第一分谱如果是约定的配置分谱则跳过 */
+      if (partListNames[0]?.toLocaleUpperCase?.() === 'COMMON') {
+        partListNames.shift();
+      }
+      // 根据后台已选择的分轨筛选出能切换的声轨
+      const multiTracksSelection: any =
+        data.list[data.listActive]?.multiTracksSelection;
+      const canSelectTracks = multiTracksSelection
+        ? multiTracksSelection?.split(',')
+        : [];
+      const arr = partListNames
+        .map((item: any, index: number) => {
+          // 该声轨能否被选
+          const canselect =
+            canSelectTracks.length == 0 || canSelectTracks.includes(item)
+              ? true
+              : false;
+          // console.log(canselect,index)
+          const instrumentName = getInstrumentName(item);
+          const sortId = sortMusical(instrumentName, index);
+          return {
+            label: item + (instrumentName ? `(${instrumentName})` : ''),
+            value: index,
+            sortId,
+            canselect,
+            track: item
+          };
+        })
+        .filter((item: any) => item.canselect)
+        .sort((a: any, b: any) => a.sortId - b.sortId);
+      data.trackList = arr;
+      let track = arr.find(
+        (item: any) => item.value === data.musicInstrumentIndex
+      )?.track;
+      track = track
+        .replace(/[0-9]+/g, '')
+        .replace(/\s/g, '')
+        .toLocaleLowerCase();
+      let musicRenderType: 'staff' | 'first' | 'fixed' = 'first';
+      let canTrans = true;
+      data.list[data.listActive]?.musicalInstruments.forEach((item: any) => {
+        if (item.code.toLocaleLowerCase() === track) {
+          musicRenderType =
+            item.defaultScore === 'STAVE'
+              ? 'staff'
+              : item.defaultScore === 'JIAN'
+              ? 'fixed'
+              : item.defaultScore === 'FIRST'
+              ? 'first'
+              : 'first';
+          canTrans = item.transferFlag;
+        }
+      });
+      data.showTransBtn = canTrans;
+      data.showMusicImg = musicRenderType;
+    };
+    watch(
+      () => data.listActive,
+      async () => {
+        data.musicInstrumentIndex = 0;
+        analyzeXml();
+      }
+    );    
+    watch(
+      () => data.musicInstrumentIndex,
+      async () => {
+        data.trackName = data.trackList.find((item: any) => item.value === data.musicInstrumentIndex)?.label || '切换声轨'
+        musicIframeLoad();
+      }
+    );  
     return () => (
       <div class={styles.container}>
         <NSpace align="center" wrapItem={false} size={16}>
@@ -549,6 +684,7 @@ export default defineComponent({
                 <div class={styles.musicName}>
                   {activeItem.value.musicSheetName}
                 </div>
+
                 <img
                   id="music-2"
                   style={{
@@ -561,9 +697,20 @@ export default defineComponent({
                     // const origin = /(localhost|192)/.test(location.host)
                     //   ? 'https://test.lexiaoya.cn'
                     //   : location.origin;
+                    // 默认进页面显示对应的曲谱
+                    let lineType = 'staff';
+                    if (data.showMusicImg === 'first') {
+                      lineType = 'firstTone';
+                    } else if (data.showMusicImg === 'fixed') {
+                      lineType = 'fixedTone';
+                    } else if (data.showMusicImg === 'staff') {
+                      lineType = 'staff';
+                    }        
                     const src = `${vaildMusicScoreUrl()}/instrument?v=${+new Date()}&platform=pc&showGuide=true&id=${
                       activeItem.value.id
-                    }&Authorization=${user.getToken}`;
+                    }&Authorization=${user.getToken}&musicRenderType=${lineType}&showGuide=true&part-index=${
+                      data.musicInstrumentIndex
+                    }`;
                     if (
                       window.matchMedia('(display-mode: standalone)').matches
                     ) {
@@ -583,23 +730,35 @@ export default defineComponent({
                   style={{ display: activeItem.value.id ? '' : 'none' }}>
                   {isEnsemble.value && (
                     <NPopselect
-                      options={musicalInstruments.value}
+                      options={data.trackList}
                       trigger="hover"
                       v-model:value={data.musicInstrumentIndex}
-                      onUpdate:value={(val: any) => {
-                        if (val === data.musicInstrumentIndex) return;
-
-                        musicIframeLoad();
+                      onUpdate:value={async (val: any) => {
+                        //
                       }}
                       // key={item.id}
                       class={[styles.popSelect]}>
                       <NButton round class={[styles.textBtn]}>
-                        切换声轨
+                        {data.trackName}
                         <i class={styles.iconArrow}></i>
                       </NButton>
                     </NPopselect>
                   )}
-
+                  {/* 转谱按钮 */}
+                  { data.showTransBtn && 
+                    <NPopselect
+                      options={_actions}
+                      trigger="hover"
+                      v-model:value={data.showMusicImg}
+                      onUpdate:value={async (val: any) => {
+                        data.showMusicImg = val;
+                        musicIframeLoad();
+                      }}
+                      // key={item.id}
+                      class={[styles.popTrans]}>
+                      <img class={[styles.transBtn]} src={icon_trans} />
+                    </NPopselect>                    
+                  }
                   <div class={styles.favitor} onClick={() => handleFavitor()}>
                     <Transition name="favitor" mode="out-in">
                       {activeItem.value.favitor ? (

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.