/** * 曲谱节拍器 * auth: lsq * time: 2022.11.14 */ import { reactive, watch } from "vue"; import { Howl } from "howler"; import tockAndTick from "/src/constant/tockAndTick.json"; type IOptions = { speed: number; }; 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 unknown as number, activeMetro: {} as any, }); 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 = []; } initPlayer() { if (!this.source) { this.source = this.loadAudio1(); } this.source.volume(metronomeData.disable ? 0 : 1); // if (!this.source2) { // this.source2 = this.loadAudio2(); // } metronomeData.initPlayerState = true; } // 播放 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("播放", index); // metronomeData.activeMetro = this.getStep(activeMetro); // console.log("🚀 ~ metronomeData.activeMetro",metronomeData.activeMetro.measureNumberIndex, metronomeData.activeMetro.index) this.playAudio(); return; } }; // 播放 playAudio = () => { if (!metronomeData.initPlayerState) return; this.source.play(); }; // 切换 selectPlay() {} loadAudio1 = () => { return new Howl({ src: tockAndTick.tick, }); }; loadAudio2 = () => { return new Howl({ src: tockAndTick.tock, }); }; 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 measureListIndex = -1; for (let i = 0; i < times.length; i++) { const note = times[i]; const measureNumberXML = note?.timeNote?.measureNumber; // console.log("🚀 ~ note?.noteElement?.sourceMeasure", note?.noteElement?.sourceMeasure) // console.log("🚀 ~ measureNumberXML", measureNumberXML , xmlNumber) // console.log("🚀 ~ measureNumberXML", note) if (measureNumberXML > -1) { if (measureNumberXML != measureListIndex) { const m = { measureNumberXML: measureNumberXML, numerator: note?.measure?.numerator || 0, time: note?.timeNote?.millisecondsPerMeasure, stepList: [] as number[], notes: [note] as any[], }; xmlNumber++; measures[xmlNumber] = m; measureListIndex = measureNumberXML; } else { measures[xmlNumber].notes.push(note); } } } // console.log(measures, measures.length); const metroList: number[] = []; const metroMeasure: any[] = []; // 4.按照拍数将时长平均分配 try { for (let i = 0; i < measures.length; i++) { const measure = measures[i]; const calp = 1 / measure.numerator; // console.log("🚀 ~ measure.numerator:", measure.numerator) const totalMeasureTime = measure.notes.reduce((total: number, note: any) => { return total + note?.abcNote?.duration; }, 0); const step = Math.floor(totalMeasureTime / calp); // console.log('🚀 ~ calp:', calp, 'total', totalMeasureTime, 'step', step) const startTime = measure.notes[0].timeNote.milliseconds; const stepTime = measure.notes[0].timeNote.millisecondsPerMeasure / measure.numerator; for (let j = 0; j < step; j++) { const time = stepTime * j + startTime; metroList.push(time); } } } catch (error) { console.log(error); } // console.log(metroList, metroList.length); // 5.得到所有的节拍时间 metronomeData.metroList = metroList; metronomeData.metroMeasure = metroMeasure; metronomeData.activeMetro = metroMeasure[0]?.[0] || {}; } } export default Metronome;