123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- /**
- * 曲谱节拍器
- * auth: lsq
- * time: 2022.11.14
- */
- import { reactive, watch } from "vue";
- import { tickUrl as tick, tockUrl as tock } from "/src/constant/audios";
- import { browser } from "/src/utils/index";
- import state from "/src/state";
- import { Howl } from "howler";
- import tockAndTick from "/src/constant/tockAndTick.json";
- import tickWav from "/src/assets/tick.mp3";
- import tockWav from "/src/assets/tock.mp3";
- import { audioData as audioDataState } from "../view/audio-list"
- type IOptions = {
- speed: number;
- };
- const browserInfo = browser();
- let tipsTimer: any = null; // 光标提示定时器
- // HTMLAudioElement 音频
- const audioData = reactive({
- tick: null as unknown as HTMLAudioElement,
- tock: null as unknown as HTMLAudioElement,
- });
- let tickTockPlayTime = 0;
- export const metronomeData = reactive({
- disable: true,
- initPlayerState: false,
- lineShow: false,
- isClick: false,
- metro: null as unknown as Metronome,
- metroList: [] as number[],
- activeList: [] as number[],
- metroMeasure: [] as any[],
- activeIndex: null as any,
- activeMetro: {} as any,
- cursorMode: 2 as number, // 光标模式:1:音符指针;2:节拍指针;3:关闭指针
- cursorTips: '' as string, // 光标模式提示文字
- followAudioIndex: 1, // 当前的拍数
- totalNumerator: 2, // 总拍数
- firstBeatTypeArr:[] as number[] // 第一小节的节拍
- });
- watch(
- () => metronomeData.cursorMode,
- () => {
- const img: HTMLElement = document.querySelector("#cursorImg-0")!;
- if (img) {
- switch (metronomeData.cursorMode) {
- case 1:
- img.classList.remove("lineHide");
- img.style.opacity = 'inherit'
- metronomeData.cursorTips = '您已切换到指针跟随音符播放';
- img.style.opacity = 'inherit'
- break;
- case 2:
- img.classList.add("lineHide");
- img.style.opacity = 'inherit'
- metronomeData.cursorTips = '您已切换到指针跟随节拍播放';
- // console.log('光标',img)
- break;
- case 3:
- img.style.opacity = '0'
- metronomeData.cursorTips = '您已关闭指针显示';
- // console.log('隐藏光标')
- break;
- default:
- break;
- }
- hideCursorTip()
- }
- }
- );
- // 切换隐藏光标
- const toggleLine = () => {
- if (!metronomeData.lineShow) return;
- const img: HTMLElement = document.querySelector("#cursorImg-0")!;
- if (img) {
- if (state.times[state.activeNoteIndex].multipleRestMeasures) {
- img.classList.remove("lineHide");
- } else {
- img.classList.add("lineHide");
- }
- }
- };
- watch(
- () => metronomeData.lineShow,
- () => {
- const img: HTMLElement = document.querySelector("#cursorImg-0")!;
- if (img) {
- if (metronomeData.lineShow) {
- img.classList.add("lineHide");
- } else {
- img.classList.remove("lineHide");
- }
- }
- }
- );
- class Metronome {
- playType = "tick";
- source = null as any; // 创建音频源头
- source1 = null as any;
- source2 = null as any;
- constructor(option?: IOptions) {}
- init(times: any[]) {
- this.calculation(times);
- metronomeData.activeList = [];
- this.initPlayer()
- }
- initPlayer() {
- // if (!this.source1) {
- // this.source1 = this.loadAudio1();
- // }
- // if (!this.source2) {
- // this.source2 = this.loadAudio2();
- // }
- // metronomeData.initPlayerState = true;
- if(metronomeData.initPlayerState) return
- Promise.all([this.createAudio(tickWav), this.createAudio(tockWav)]).then(
- ([tick, tock]) => {
- if (tick) {
- audioData.tick = tick;
- }
- if (tock) {
- audioData.tock = tock;
- }
- metronomeData.initPlayerState = true;
- }
- );
- }
- createAudio = (src: string): Promise<HTMLAudioElement | null> => {
- return new Promise((resolve) => {
- // const a = new Audio(src + '?v=' + Date.now());
- const a = new Audio(src);
- a.load();
- a.onloadedmetadata = () => {
- resolve(a);
- };
- a.onerror = () => {
- resolve(null);
- };
- });
- };
- // 播放
- sound = (currentTime: number) => {
- let index = -1;
- let activeMetro = -1;
- for (let i = 0; i < metronomeData.metroList.length; i++) {
- const item = metronomeData.metroList[i];
- if (currentTime >= item) {
- // console.log(currentTime , item)
- index = i;
- activeMetro = item;
- } else {
- break;
- }
- }
- if (index > -1 && metronomeData.activeIndex !== index) {
- metronomeData.activeIndex = index;
- // console.log("播放", metronomeData.activeIndex);
- metronomeData.activeMetro = this.getStep(activeMetro);
- // console.log("🚀 ~ metronomeData.activeMetro",metronomeData.activeMetro.measureNumberIndex, metronomeData.activeMetro.index)
- tickTockPlayTime = currentTime
- this.playAudio();
- metronomeData.isClick = false;
- return;
- }
- // toggleLine()
- metronomeData.isClick = false;
- };
-
- // 暂停的时候,点击音符,需要找到对应的节拍器的位置
- findMetronomePosition = (currentTime: number) => {
- console.log('取消选段1111',currentTime)
- const originTime = currentTime;
- // if (!state.sectionStatus){
- // currentTime = setCurrentTime(currentTime);
- // }
- let index = -1;
- let activeMetro = -1;
- for (let i = 0; i < metronomeData.metroList.length; i++) {
- const item = metronomeData.metroList[i];
- if (currentTime >= item) {
- // console.log(currentTime , item)
- index = i;
- activeMetro = item;
- } else {
- break;
- }
- }
- if (index > -1 && metronomeData.activeIndex !== index) {
- metronomeData.activeIndex = index;
- // console.log("节拍器播放", '节拍器索引:',metronomeData.activeIndex, '节拍器时间:',currentTime, '实际时间:',originTime,'xml计算的时间:',metronomeData.metroList[metronomeData.activeIndex]);
- metronomeData.activeMetro = this.getStep(activeMetro);
- console.log("🚀 ~ metronomeData.activeMetro",metronomeData.activeMetro.measureNumberIndex, metronomeData.activeMetro.index)
- tickTockPlayTime = currentTime
- // this.playAudio();
- metronomeData.isClick = false;
- return;
- }
- metronomeData.isClick = false;
- if (currentTime === 0) {
- metronomeData.activeMetro = {}
- }
- };
- // 播放
- playAudio = () => {
- // 关闭定时器节拍器
- return
- /* 如果是 评测模式且不为MIDI并且节拍器资源加载成功的时候 不运行节拍器播放*/
- if (state.modeType === "practise" && state.playMode !== "MIDI") {
- if(state.playType === "play" && state.playSource === "music" && audioDataState.songCollection.beatSongEle){
- return
- }
- if(state.playType === "play" && state.playSource === "background" && audioDataState.songCollection.beatBackgroundEle){
- return
- }
- if(state.playType === "sing" && state.playSource === "music" && audioDataState.songCollection.beatFanSongEle){
- return
- }
- if(state.playType === "sing" && state.playSource === "background" && audioDataState.songCollection.beatBanSongEle){
- return
- }
- if(state.playType === "sing" && state.playSource === "mingSong" && audioDataState.songCollection.beatMingSongEle){
- return
- }
- }
- // console.log("播放自带的节拍器 233333")
- if (!metronomeData.initPlayerState || state.playState === 'paused') return;
- const beatVolume = state.setting.beatVolume / 100
- // this.source = metronomeData.activeMetro?.index === 0 ? this.source1 : this.source2;
- // this.source.volume(metronomeData.disable || state.playState === 'paused' ? 0 : beatVolume);
- // Audio 播放音频
- this.source = metronomeData.activeMetro?.index === 0 ? audioData.tick : audioData.tock;
- this.source.volume = metronomeData.disable ? 0 : beatVolume;
- if (this.source.volume <= 0) {
- this.source.muted = true
- } else {
- this.source.muted = false
- }
- // console.log('节拍器播放的时间',tickTockPlayTime)
- this.source.play();
- };
- /**
- * 跟练模式播放,跟练模式没有曲子音频播放器
- */
- simulatePlayAudio = () => {
- // console.log(333, metronomeData.followAudioIndex)
- if (!metronomeData.initPlayerState) return;
- const beatVolume = state.setting.beatVolume / 100
- // this.source = metronomeData.followAudioIndex === 1 ? this.source1 : this.source2;
- // Audio 播放音频
- this.source = metronomeData.followAudioIndex === 1 ? audioData.tick : audioData.tock;
- // this.source.volume(metronomeData.disable ? 0 : beatVolume);
- this.source.volume = metronomeData.disable ? 0 : beatVolume
- /**
- * https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLMediaElement/volume
- * volume属性在部分ios手机的Safari浏览器不被支持
- */
- if (this.source.volume <= 0) {
- this.source.muted = true
- } else {
- this.source.muted = false
- }
- // console.log('音量',this.source,this.source.volume)
- this.source.play();
- metronomeData.followAudioIndex += 1;
- metronomeData.followAudioIndex = metronomeData.followAudioIndex > metronomeData.totalNumerator ? 1 : metronomeData.followAudioIndex;
- };
- // 切换
- selectPlay() {}
- loadAudio1 = () => {
- return new Howl({
- src: tockAndTick.tick,
- // 如果是ios手机,需要强制使用audio,不然部分系统版本第一次播放没有声音
- // html5: browserInfo.ios,
- });
- };
- loadAudio2 = () => {
- return new Howl({
- src: tockAndTick.tock,
- // html5: browserInfo.ios,
- });
- };
- getStep(time: number) {
- for (let i = 0; i < metronomeData.metroMeasure.length; i++) {
- const list = metronomeData.metroMeasure[i];
- const item = list.find((n: any) => n.time === time);
- if (item) {
- // console.log('index',item)
- return item;
- }
- }
- return {};
- }
- // 计算 所有的拍子的时间
- calculation(times: any[]) {
- // console.log("🚀 ~ times", times);
- // 1.统计有多少小节
- const measures: any[] = [];
- let xmlNumber = -1;
- let isDiff = false //弱起时间不够补的时候
- for (let i = 0; i < times.length; i++) {
- const note = times[i];
- const measureNumberXML = note.MeasureNumberXML;
- // console.log("🚀 ~ note?.noteElement?.sourceMeasure", note?.noteElement?.sourceMeasure)
- // console.log("🚀 ~ measureNumberXML", measureNumberXML, note)
- // console.log("🚀 ~ measureNumberXML", note)
- const measureListIndex = measureNumberXML - 1;
- // 当渐快渐慢的时候 不播节拍器
- if(isWithinRange(state.gradual, measureListIndex)){
- xmlNumber = measureNumberXML;
- continue
- }
- if (measureNumberXML > -1) {
- if (measureNumberXML != xmlNumber) {
- // 弱起的时候 根据音符结尾时间减去音符开头时间,得到的不是正常小节的时间,然后平均分配节拍之后,当前节拍间隔会非常短 这里弱起取整个小节的时间
- // 当小节时间 减去音符时间,前奏没有预留时间时候,从歌词开始唱的那里开始响节拍器
- let startTime = note.measures[0].time
- if(i === 0 && note.measures[0].difftime>0){
- startTime = note.measures[note.measures.length - 1].endtime - note.measures[0].measureLength
- if(startTime < 0){
- isDiff = true
- }
- }
- if(isDiff) {
- // 当前小节有歌词,开放弱起节拍器
- let isLyric = false
- let noteIndex = 0
- while(!isLyric && noteIndex<note.measures.length){
- isLyric = !!note.measures[noteIndex]?.formatLyricsEntries?.length
- noteIndex ++
- }
- isDiff = !isLyric
- }
- if(isDiff){
- xmlNumber = measureNumberXML;
- continue
- }
- // 最后一小节的时间 有可能和下一小节开始时间接不上
- const { time, endtime, noteLengthTime } = note.measures[note.measures.length - 1]
- let nextNoteStartTime = times[note.measures[note.measures.length - 1].i + 1]?.time
- let noteEndTime = 0
- if(!nextNoteStartTime){
- // 当不够的时候补上时值
- noteEndTime = time + noteLengthTime > endtime ? time + noteLengthTime : endtime
- }else{
- if(Math.abs(nextNoteStartTime - endtime)*1000< 10){
- // 当首位本来就是相连的
- noteEndTime = endtime
- }else{
- // 当首位不相连,差值大于这个音符的持续时间的时候取这个音符的时间(防止有间奏的连起来时间太长),否则直接取后一个音符的开始时间
- noteEndTime = nextNoteStartTime - time > noteLengthTime ? time + noteLengthTime : nextNoteStartTime
- }
- }
- const m = {
- measureNumberXML: measureNumberXML,
- measureNumberIndex: measureListIndex,
- CompoundTempo: note?.noteElement?.sourceMeasure?.CompoundTempo || "",
- numerator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.numerator || 0,
- denominator: note?.noteElement?.sourceMeasure?.ActiveTimeSignature?.denominator || 0,
- start: startTime,
- end: noteEndTime,
- time: noteEndTime - startTime,
- stave_x: note?.noteElement?.sourceMeasure?.verticalMeasureList?.[0]?.stave?.x || 0,
- end_x: note?.stave?.end_x || 0 || 0,
- stepList: [] as number[],
- svgs: [] as any[],
- isRestFlag: note.isRestFlag,
- };
- /**
- * bug:#9877
- * 多分轨合并显示,不同分轨的音符数量可能不同
- */
- let measureArr = note.measures;
- if (state.isCombineRender) {
- measureArr = measureArr.filter((item: any) => item.MeasureNumberXML === m.measureNumberXML)
- }
- m.stepList = calculateMetroStep(measureArr, m);
- measures.push(m);
- xmlNumber = measureNumberXML;
- }
- }
- }
- //console.log(measures, measures.length,'小节汇总');
- let metroList: number[] = [];
- const metroMeasure: any[] = [];
- console.log("节拍器 每一小节时间:", measures)
- console.log("节拍器 间隔:", measures.map(item => {
- return {
- time: item.time,
- measureNumberXML: item.measureNumberXML
- }
- }))
- try {
- for (let i = 0; i < measures.length; i++) {
- const measure = measures[i];
- // 87拍和45拍要根据小节返回的CompoundTempo特殊处理
- const beatTypeArr = getBeatTypeArr(measure.numerator, measure.denominator, measure.CompoundTempo)
- const CompoundTempoArr = beatTypeArr.map((beatType:number) => {
- return Math.abs(beatType*measure.numerator)
- })
- if(i===0){
- metronomeData.firstBeatTypeArr = beatTypeArr
- }
- metroMeasure[i] = [] as number[];
- // 根据有几个拍子,划分成几份
- const widthStep = 100 / (beatTypeArr.length+1);
- // 当前拍子的组合数(2+3+2,3+2)中的数字
- let beatNum = 0;
- // if (measure.measureNumberXML == 98) {
- // debugger
- // }
- for (let j = 0; j < beatTypeArr.length; j++) {
- // 累加
- const beatMuit = Array(j).fill("").reduce((num:number,v:any,i:number) => {
- return num += Math.abs(beatTypeArr[i])
- }, 0) || 0
- const time = measure.time * beatMuit + measure.start;
- metroList.push(time);
- let left = "";
- // 当前拍子数对应的节拍位置索引
- let currentIdx = 0;
- if (j == 0) {
- currentIdx = 0
- } else {
- beatNum += CompoundTempoArr[j-1];
- currentIdx = beatNum
- }
- // 如果是87拍,并且是3+2+2的组合,第二拍的节拍指针需要定位到第四个音符的位置
- // if (measure.numerator === 7 && measure.denominator === 8 && measure.CompoundTempo === '3+2+2' && j === 1) {
- // currentIdx += 1;
- // }
- if (measure.stepList[currentIdx]) {
- left = measure.stepList[currentIdx] + "px";
- } else {
- const preLeft = measure.stepList[j - 1];
- left = !preLeft || preLeft.toString().indexOf("%") > -1 ? `${widthStep*(j+1)}%` : `${preLeft}px + ${widthStep}%`;
- measure.stepList[j] = left;
- }
- metroMeasure[i].push({
- isTick: beatTypeArr[j] < 0,
- index: j,
- time,
- left: left?.indexOf("%") > -1 ? `calc(${left})` : left,
- measureNumberXML: measure.measureNumberXML,
- isRestFlag: measure.isRestFlag,
- stepList: measure.stepList
- });
- }
- }
- } catch (error) {
- console.log(error);
- }
- console.log('节拍器',metroList, metroMeasure);
- // 5.得到所有的节拍时间
- metronomeData.metroList = metroList;
- metronomeData.metroMeasure = metroMeasure;
- // console.log(9999,metroList,7777,metroMeasure)
- metronomeData.activeMetro = metroMeasure[0]?.[0] || {};
- }
- }
- /** 获取节拍类型数组 */
- export function getBeatTypeArr(numerator?:number, denominator?:number, CompoundTempo?:string){
- const speedBeatUnit = state.speedBeatUnit
- const Numerator = numerator || state.osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Numerator || 4
- const Denominator = denominator || state.osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Denominator || 4
- let loopArr = []
- // 规则 负数代表重声,正数代表轻声
- switch (`${Numerator}/${Denominator}`) {
- case "2/2":
- loopArr = [-1/2, 1/2]
- break;
- case "3/2":
- loopArr = [-1/3, 1/3, 1/3]
- break;
- case "5/4":
- //5/4拍根据谱面的CompoundTempo来特殊处理
- if(CompoundTempo==="2+3"){
- loopArr = [-1/5, 1/5, -1/5, 1/5, 1/5]
- }else{
- loopArr = [-1/5, 1/5, 1/5, -1/5, 1/5]
- }
- break;
- case "3/8":
- // 3/8拍 速度为浮点4分音符时候特殊处理
- if(speedBeatUnit==="1/4."){
- loopArr = [-1/1]
- }else{
- loopArr = [-1/3, 1/3, 1/3]
- }
- break;
- case "6/8":
- loopArr = [-1/2, 1/2]
- break;
- case "7/8":
- //7/8拍根据谱面的CompoundTempo来特殊处理
- if(CompoundTempo==="2+2+3"){
- loopArr = [-2/7, 2/7, 3/7]
- }else if(CompoundTempo==="2+3+2"){
- loopArr = [-2/7, 3/7, 2/7]
- }else{
- loopArr = [-3/7, 2/7, 2/7]
- }
- break;
- case "9/8":
- loopArr = [-3/9, 3/9, 3/9]
- break;
- default:
- loopArr.push(-1/Numerator);
- for (let i = 1; i < Numerator; i++) {
- loopArr.push(1/Numerator);
- }
- break;
- }
- // console.log(loopArr, "loopArr")
- return loopArr
- }
- // 计算拍子的时值
- function calculateMetroStep(arr: any[], m: any): number[] {
- const measureLength = arr.reduce((total: number, item: any) => {
- total += item._noteLength;
- return total;
- }, 0);
- const clap = measureLength / m.numerator;
- if (arr.length === 1) {
- const wholeNote = arr[0].svgElement;
- if (wholeNote && !wholeNote.isRest()) {
- const measure_bbox = wholeNote?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0, right: 0 };
- let bbox = wholeNote?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
- let stepWidth = Math.abs(measure_bbox.right - bbox.x) / m.numerator;
- let stepList: number[] = [];
- for (let i = 0; i < m.numerator; i++) {
- stepList.push(bbox.x - measure_bbox.x + i * stepWidth);
- }
- // console.log("🚀 ~ stepList:", stepList, m.measureNumberXML)
- return stepList;
- }
- try {
- // 开头是休止符
- if (m.measureNumberXML === 1 && wholeNote && wholeNote.isRest()) {
- const measure_bbox = wholeNote?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0, right: 0 };
- let bbox = wholeNote?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
- let stepWidth = Math.abs(measure_bbox.right - bbox.x) / m.numerator;
- let stepList: any[] = [];
- // 第一小节是休止符,节拍指针应该等分宽度
- const widthStep = 100 / (m.numerator + 1);
- // for (let i = -1; i < m.numerator - 1; i++) {
- // stepList.push(bbox.x - measure_bbox.x + i * stepWidth);
- // }
- // for (let i = 1; i <= m.numerator; i++) {
- // stepList.push(widthStep * i + '%');
- // }
- // console.log(wholeNote?.attrs?.el, m.measureNumberXML)
- // console.log("🚀 ~ stepList:", stepList, m.measureNumberXML)
- return stepList;
- }
- } catch (error) {
- console.log("🚀 ~ error:", error);
- }
- return [];
- }
- // console.log("🚀 ~ arr", [...arr],`小节总时值: ${measureLength}`, clap, m.measureNumberXML);
- let totalLength = 0;
- let notes: any[] = [];
- let stepList: number[] = [];
- for (let i = 0; i < arr.length; i++) {
- const item = arr[i];
- item.index = i;
- const noteLength = item._noteLength;
- totalLength += noteLength;
- // 大于一拍
- const exceedStep = Math.floor(totalLength / clap);
- // console.log(`note`, item?.svgElement?.attrs?.el,notes.length,{noteLength, exceedStep,clap}, m.measureNumberXML)
- if (exceedStep >= 1) {
- totalLength -= clap;
- // 一拍
- let measure_bbox = item?.svgElement?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0 };
- /**
- * bug: #9875
- * 简谱,渲染有点问题,先使用vf-stave的位置
- */
- if (state.musicRenderType !== "staff") {
- measure_bbox = item?.svgElement?.attrs?.el?.parentElement?.parentElement?.querySelector('.vf-stave')?.getBoundingClientRect?.() || { x: 0 };
- }
- /**
- * 如果measure_bbox不存在(多分轨合并显示可能会出现),则用note再获取一次
- */
- if (!measure_bbox.width && notes.length > 0) {
- measure_bbox = state.musicRenderType !== "staff" ? notes[0]?.svgElement?.attrs?.el?.parentElement?.parentElement?.querySelector('.vf-stave')?.getBoundingClientRect?.() || { x: 0 } :
- notes[0]?.svgElement?.attrs?.el?.parentElement?.parentElement?.getBoundingClientRect?.() || { x: 0 }
- }
- if (notes.length > 0) {
- let bbox = notes[0]?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
- let x: any = bbox.x - measure_bbox.x;
- if (notes[0]._noteLength / clap >= 1) {
- const nextNote = arr[notes[0].index + 1]?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: measure_bbox.right } || { x: 0 };
- const stepWidth = Math.abs(bbox.x - nextNote.x) / 2;
- x = bbox.x - measure_bbox.x + stepWidth;
- // console.log(`音符超一拍`, notes[0]?.svgElement?.attrs?.el, arr[notes[0].index + 1]?.svgElement?.attrs?.el, bbox.x - nextNote.x, stepWidth, m.measureNumberXML);
- }
- // console.log(`一拍`, notes[0]?.svgElement?.attrs?.el, m.measureNumberXML, notes[0]._noteLength , clap, 'aa')
- stepList.push(x);
- } else {
- let bbox = item?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
- let x: any = bbox.x - measure_bbox.x;
- // console.log(`一拍`, item?.svgElement?.attrs?.el, m.measureNumberXML)
- stepList.push(x);
- }
- notes = [];
- let bbox = item?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: 0 };
- let x: any = bbox.x - measure_bbox.x;
- let stepWidth = 0;
- if (exceedStep > 1) {
- // 二拍以上
- const nextNote = arr[i + 1]?.svgElement?.attrs?.el?.getBoundingClientRect?.() || { x: measure_bbox.right } || { x: 0 };
- stepWidth = Math.abs(bbox.x - nextNote.x) / exceedStep;
- // console.log("二拍以上 ~ nextNote:",bbox.x , nextNote.x,stepWidth, item?.svgElement?.attrs?.el,arr[i + 1]?.svgElement?.attrs?.el, exceedStep);
- }
- for (let j = 1; j < exceedStep; j++) {
- totalLength -= clap;
- // console.log(`超一拍`,item?.svgElement?.attrs?.el, m.measureNumberXML)
- stepList.push(x + stepWidth * j);
- }
- }
- //有时值就将音符加入
- if (totalLength > Number.EPSILON && totalLength > 0) {
- notes.push(item);
- }
- }
- stepList = stepList.reduce((list: any[], n: number) => {
- if (list.includes(n)) {
- list.push(undefined as any);
- } else {
- list.push(n);
- }
- return list;
- }, []);
- // console.log("stepList", [...stepList], m.measureNumberXML);
- return stepList;
- }
- // 延迟兼容处理
- function setCurrentTime(time: number) {
- if (browserInfo.huawei || browserInfo.xiaomi) {
- time += 0.125;
- } else if (browserInfo.android) {
- time += 0.11;
- } else if (browserInfo.ios) {
- time += 0.01;
- }
- return time;
- }
- // 自动隐藏光标提示
- function hideCursorTip() {
- if (!tipsTimer) {
- tipsTimer = setTimeout(() => {
- metronomeData.cursorTips = ''
- clearTimeout(tipsTimer)
- tipsTimer = null
- }, 2000);
- } else {
- clearTimeout(tipsTimer)
- tipsTimer = setTimeout(() => {
- metronomeData.cursorTips = ''
- clearTimeout(tipsTimer)
- tipsTimer = null
- }, 2000);
- }
- }
- function isWithinRange(ranges:any[], index:number) {
- for (const range of ranges) {
- const start = range[0].measureIndex;
- const end = range[1].measureIndex;
- if (index >= start && index < end) {
- return true;
- }
- }
- return false;
- }
- export default Metronome;
|