import { closeToast, showToast, showConfirmDialog } from "vant";
import { nextTick, reactive, watch } from "vue";
import { OpenSheetMusicDisplay } from "../osmd-extended/src";
import { metronomeData } from "./helpers/metronome";
import { GradualNote, GradualTimes, GradualVersion } from "./type";
import { handleEndEvaluat, handleStartEvaluat } from "./view/evaluating";
import { IFingering, mappingVoicePart, subjectFingering, matchVoicePart } from "/src/view/fingering/fingering-config";
import { handleStartTick, closeTick } from "./view/tick";
import { audioListStart, getAudioCurrentTime, getAudioDuration, setAudioCurrentTime, setAudioPlaybackRate, audioData } from "./view/audio-list";
import { toggleFollow } from "./view/follow-practice";
import { browser, setStorageSpeed, setGlobalData } from "./utils";
import { api_cloudGetMediaStatus, api_createMusicPlayer, api_cloudChangeSpeed, api_cloudSuspend, api_cloudSetCurrentTime, api_cloudDestroy } from "./helpers/communication";
import { verifyCanRepeat, getDuration, xmlAddPartName } from "./helpers/formateMusic";
import { getMusicSheetDetail } from "./utils/baseApi"
import { getQuery } from "/src/utils/queryString";
import { followData, skipNotePractice } from "/src/view/follow-practice/index"
import { changeSongSourceByBeat } from "/src/view/audio-list"
import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation, calcClientWidth } from "/src/page-instrument/view-detail/smoothAnimation"
import { storeData } from "/src/store";
import { downloadXmlStr } from "./view/music-score"
import { musicScoreRef, headerColumnHide } from "/src/page-instrument/view-detail/index"
import { headTopData } from "/src/page-instrument/header-top/index";
import { api_lessonTrainingTrainingStudentDetail } from "/src/page-instrument/api"
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"

const query: any = getQuery();

/** 入门 | 进阶 | 大师 */
export type IDifficulty = "BEGINNER" | "ADVANCED" | "PERFORMER";
/** 渲染类型: 五线谱,简谱 */
export enum EnumMusicRenderType {
  /** 五线谱 */
  staff = "staff",
  /** 简谱（首调） */
  firstTone = "firstTone",
  /** 固定音高 */
  fixedTone = "fixedTone",
}
export const musicscoresettingKey = "musicscoresetting";
/** 有声音的是那个音源 */
export type IPlayState = "music" | "background" | "mingSong";
/** 播放状态 */
export type IAudioState = "play" | "paused";

/** 来源 */
export enum IPlatform {
  APP = "APP",
  PC = "PC",
}

export type ISonges = {
  background?: string
  music?: string
}

/**
 * 特殊教材分类id
 */
const classids = [1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 30, 31, 35, 36, 38, 108, 150, 151, 152, 153, 154, 155, 156, 157, 158, 178, 179, 180, 181, 182]; // 大雅金唐, 竖笛教程, 声部训练展开的分类ID

// 乐器code码
export const musicalInstrumentCodeInfo = [
  {
    name: '长笛',
    code: 'Flute',
    id: 1
  },
  {
    name: '短笛',
    code: 'Piccolo',
    id: 2
  },
  {
    name: '单簧管',
    code: 'Clarinet',
    id: 3
  },
  {
    name: '低音单簧管',
    code: 'Bass Clarinet',
    id: 4
  },
  {
    name: '中音萨克斯',
    code: 'Alto Saxophone',
    id: 5
  },
  {
    name: '次中音萨克斯',
    code: 'Tenor Saxophone',
    id: 6
  },
  {
    name: '高音萨克斯',
    code: 'Soprano Saxophone',
    id: 7
  },
  {
    name: '上低音萨克斯',
    code: 'Baritone Saxophone',
    id: 8
  },
  {
    name: '双簧管',
    code: 'Oboe',
    id: 9
  },
  {
    name: '大管',
    code: 'Bassoon',
    id: 10
  },
  {
    name: '小号',
    code: 'Trumpet',
    id: 11
  },
  {
    name: '圆号',
    code: 'Horn',
    id: 12
  },
  {
    name: '长号',
    code: 'Trombone',
    id: 13
  },
  {
    name: '上低音号',
    code: 'Baritone',
    id: 14
  },
  {
    name: '次中音号',
    code: 'Euphonium',
    id: 15
  },
  {
    name: '大号',
    code: 'Tuba',
    id: 16
  },
  {
    name: '钢琴',
    code: 'Piano',
    id: 17
  },
  {
    name: '电钢琴',
    code: 'Electronical Piano',
    id: 18
  },
  {
    name: '钢片琴',
    code: 'Glockenspiel',
    id: 19
  },
  {
    name: '小提琴',
    code: 'Violin',
    id: 20
  },
  {
    name: '中提琴',
    code: 'Viola',
    id: 21
  },
  {
    name: '大提琴',
    code: 'Violoncello',
    id: 22
  },
  {
    name: '低音提琴',
    code: 'Contrabass',
    id: 23
  },
  {
    name: '架子鼓',
    code: 'Drum Set',
    id: 24
  },
  {
    name: '小鼓',
    code: 'Snare Drum',
    id: 25
  },
  {
    name: '马林巴',
    code: 'Marimba',
    id: 26
  },
  {
    name: '颤音琴',
    code: 'Vibraphone',
    id: 27
  },
  {
    name: '钟琴',
    code: 'Chimes',
    id: 28
  },
  {
    name: '木琴',
    code: 'Xylophone',
    id: 29
  },
  {
    name: '管钟',
    code: 'Tubular Bells',
    id: 30
  },
  {
    name: '定音鼓',
    code: 'Timpani',
    id: 31
  },
  {
    name: '键盘',
    code: 'Mallets',
    id: 32
  },
  {
    name: '排箫',
    code: 'Panpipes',
    id: 33
  },
  {
    name: '陶笛',
    code: 'Ocarina',
    id: 34
  },
  {
    name: '陶笛',
    code: 'Alto Ocarina',
    id: 34
  },
  {
    name: '葫芦丝',
    code: 'Woodwind',
    id: 35
  },
  {
    name: '葫芦丝',
    code: 'Hulusi',
    id: 35
  },
  {
    name: '口风琴',
    code: 'Nai',
    id: 36
  },
  {
    name: '口风琴',
    code: 'Melodica',
    id: 36
  },
  {
    name: '德式竖笛',
    code: 'Tenor Recorder',
    id: 37
  },
  {
    name: '德式竖笛',
    code: 'German Recorder',
    id: 37
  },
  {
    name: '英式竖笛',
    code: 'Baroque Recorder',
    id: 38
  },
  {
    name: '高音陶笛',
    code: 'Whistling',
    id: 39
  },
  {
    name: '高音陶笛',
    code: 'Soprano Ocarina',
    id: 39
  },
]

const state = reactive({
  systemType: "" as "teacher" | "web" | "student",
  isSchool: !!query.school,
  /** 来源 : PC , app */
  platform: "" as IPlatform,
  appName: "" as "GYM" | "COLEXIU",
  musicRenderType: EnumMusicRenderType.staff as EnumMusicRenderType,
  /**曲谱是否渲染完成 */
  musicRendered: false,
  /** 当前曲谱数据ID, 和曲谱ID不一致 */
  detailId: "",
  /** 曲谱资源URL */
  xmlUrl: "",
  /** 声部ID */
  subjectId: 0 as number,
  trackId: 0 as string | number,
  /** 分类ID */
  categoriesId: 0,
  /** 分类名称 */
  categoriesName: "",
  /** 是否支持评测 */
  enableEvaluation: true,
  /** 是否支持转谱 */
  enableNotation: false,
  /** 后台设置不能转谱，但是默认谱面不是五线谱时，需要显示转谱按钮，此时只能转首调和固定调 */
  specialShowNotation: false,
  /** 曲谱ID */
  examSongId: "",
  /** 内容平台的曲谱ID，可能会和业务端的id不一样 */
  cbsExamSongId: "",
  /** 曲谱名称 */
  examSongName: "",
  /** 曲谱封面 */
  coverImg: "",
  /** 扩展字段 */
  extConfigJson: {} as any,
  /** 扩展样式字段 */
  extStyleConfigJson: {} as any,
  /** 简谱扩展样式字段 */
  extJianStyleConfigJson: {} as any,  
  /** 是否开启节拍器(mp3节拍器) */
  isOpenMetronome: false,
  /** 演唱模式是否开启节拍器(mp3节拍器) */
  isSingOpenMetronome: false,
  /** 是否显示指法 */
  isShowFingering: false,
  /** 原音 */
  music: "",
  /** 伴奏 */
  accompany: "",
  /** 范唱 */
  fanSong: "",
  /** 伴唱 */
  banSong: "",
  /** 唱名 */
  mingSong: "",
  /** 唱名女 如果有男唱名的情况下*/
  mingSongGirl:"",
  /** 节拍器音乐资源 */
  beatSong: {
    music: "",
    accompany: "",
    fanSong: "",
    banSong: "",
    mingSong: "",
    mingSongGirl: ""
  },
  /** midiURL */
  midiUrl: "",
  /** 父分ID */
  parentCategoriesId: 0,
  /** 分类ID */
  musicSheetCategoriesId: 0,
  /** 各产品端的分类ID，（管乐迷、管乐团、酷乐秀、课堂乐器） */
  bizMusicCategoryId: 0,
  /** 资源类型: mp3 | midi */
  playMode: "MP3" as "MP3" | "MIDI",
  /** 谱面的速度节拍 */
  speedBeatUnit: "1/4",
  /** 设置的速度 */
  speed: 0,
  /** 曲谱音频正常的速度 */
  originSpeed: 0,
  /** 播放过程中显示的速度 */
  playIngSpeed: 0,
  /** 分轨名称 */
  track: "",
  /** 当前显示声部索引 */
  partIndex: 0,
  /** 总谱渲染时候 只显示部分声部的值 */
  combinePartIndexs:[],
  /** 演奏是否需要节拍器 */
  needTick: false,
  /** 演唱模式是否需要节拍器 */
  needSingTick: false,
  /** 是否能使用节拍器  */
  isMixBeat: true,
  /** 曲谱实例 */
  osmd: null as unknown as OpenSheetMusicDisplay,
  /**是否是特殊乐谱类型, 主要针对管乐迷  */
  isSpecialBookCategory: false,
  /** 播放状态 */
  playState: "paused" as IAudioState,
  /** 播放结束状态 */
  playEnd: false,
  /** 播放类型 演奏 演唱 */
  playType: "play" as "play" | "sing",
  /** 播放那个: 原音,伴奏 */
  playSource: "music" as IPlayState,
  /** 播放进度 */
  playProgress: 0,
  /** 激活的note index */
  activeNoteIndex: 0,
  /** 激活的小节 */
  activeMeasureIndex: -1,
  /** 选段状态 */
  sectionStatus: false,
  /** 选段数据 */
  section: [] as any[],
  /** 选段背景 */
  sectionBoundingBoxs: [] as any[],
  /** 开启选段预备 */
  isOpenPrepare: false,
  /** 选段预备 */
  sectionFirst: null as any,
  /** 音符数据 */
  times: [] as any[],
  /** 播放模式 */
  modeType: "practise" as "practise" | "follow" | "evaluating",
  /** 设置 */
  setting: {
    /** 效音提醒 */
    soundEffect: true,
    /** 护眼模式 */
    eyeProtection: false,
    /** 摄像头 */
    camera: false,
    /** 摄像头透明度 */
    cameraOpacity: 70,
    /** 循环播放 */
    repeatAutoPlay: true,
    /** 显示指法 */
    displayFingering: true,
    /** 显示光标 */
    displayCursor: true,
    /** 频率 */
    frequency: 0,
    /** 评测难度 */
    evaluationDifficulty: "BEGINNER" as IDifficulty,
    /** 保存到相册 */
    saveToAlbum: true,
    /** 开启伴奏 */
    enableAccompaniment: true,
    /** 反应时间 */
    reactionTimeMs: 0,
    /** 节拍器音量 */
    beatVolume: 50,
    /** 合并休止小节 */
    combineMultipleRest: true,
  },
  /** 后台设置的基准评测频率 */
  baseFrequency: 440,
  /** mp3节拍器的时间，统计拍数、速度计算得出，evxml通过读取xml元素获取 */
  fixtime: 0,
  /** evxml等待播放的时间 */
  evXmlBeginTime: 0,
  /** 第二遍循环evxml等待播放的时间 */
  secondEvXmlBeginTime: 0,
  /** evxml等待播放的时间集合，多遍反复播放，会有多个timegap(前奏)时间 */
  evXmlBeginArr: [] as any,
  /** evxml的曲子是否有times */
  xmlHasTimes: false,  
  /** evxml的曲子是否有timeGap */
  xmlHasTimeGap: false,
  /** 有timeGap的曲子，是从哪个小节开始循环的，默认从第一小节开始循环 */
  timegapRepeatMeasureIndex: 1,
  /** 指法信息 */
  fingeringInfo: {} as IFingering,
  /** 滚动容器的ID */
  scrollContainer: "musicAndSelection",
  /** 是否是打击乐 */
  isPercussion: false,
  /* 特殊打击乐 */
  specialPercussionFlag: false,
  /* 打击乐乐器id集合 */
  musicalInstrumentIds: "",
  /** 评测标准 */
  evaluationStandard: '',
  /** 是否重复节拍器的时间 */
  repeatedBeats: 0,
  /**当前曲谱中所有声部名字 */
  partListNames: [] as any,
  /** 渐变速度信息 */
  gradual: [] as GradualNote[],
  /** 渐变速度版本 */
  gradualVersion: GradualVersion.BASE as GradualVersion,
  /** 渐变时间信息 */
  gradualTimes: null as GradualTimes,
  /** 单声部多声轨 */
  multitrack: 0,
  /** 缩放 */
  zoom: 0.8,
  /** 渲染曲谱比例 */
  musicZoom: 1,
  /** 练习,评测是否是选段模式 */
  isSelectMeasureMode: false,
  /** 是否是评分显示 */
  isReport: false,
  /** 是否隐藏评测报告弹窗，保存演奏按钮，默认不隐藏 */
  isHideEvaluatReportSaveBtn: false,
  /** 是否是合奏 */
  isConcert: false,
  /** 用户选择的结束小节数 */
  userChooseEndIndex: 0,
  /** 重播小节集合信息 */
  repeatInfo: [] as any,
  /** 多分轨的曲子，可支持筛选的分轨 */
  canSelectTracks: [] as any,
  /** 声部codeId */
  subjectCodeId: 0 as number,
  /** 乐器codeId，用于匹配乐器指法、声部转调、特殊声部处理等 */
  musicalCodeId: 0 as number,
  /** 乐器code，用于评测传参 */
  musicalCode: '' as any,
  /** 合奏曲目是否合并展示 */
  isCombineRender: false,  
  /** 是否支持总谱 */
  isScoreRender: false,  
  /** 是否默认显示总谱 */
  defaultScoreRender: false,
  /** 没有音源字段 */
  noMusicSource: false,
  /** 小节的持续时长，以后台设置的播放速度计算 */
  measureTime: 0,
  /** 跟练模式，节拍器播放的时间 */
  beatStartTime: 0,
  /** 是否为详情预览模式 */
  isPreView: false,
  /** 是否为内容平台预览模式 */
  isCbsView: false,  
  /** 是否是管乐迷后台预览模式 */
  isWeb: false,
  /** 是否为评测报告模式 */
  isEvaluatReport: false,
  /** midi播放器是否初始化中 */
  midiPlayIniting: false,
  /** 曲目信息 */
  songs: {} as ISonges,
  isAppPlay: false, // 是否midi音频，midi是app播放
  /** 音频播放器实例 */
  audiosInstance: null as any,
  /** midi音频的时长 */
  durationNum: 0,
  midiSectionStart: 0,
  /** 音频文件是否加载完成 */
  audioDone: false,
  /** 是否为单行谱渲染模式 */
  isSingleLine: false,
  /** 是否是evxml */
  isEvxml: false,
  noTimes: [] as any,
  /** 老师端：功能按钮布局方向 */
  playBtnDirection: "left" as "left" | "right",
  /** 云练习按钮方向，如果有指法并且是竖向的指法，为了防止播放按钮把指法挡住，此时云练习播放按钮方向应该取反 */
  musicScoreBtnDirection: "right" as "left" | "right",
  /** 是否在老师端上课页面 */
  isAttendClass: false,
  /** 引导页信息 */
  guideInfo: null as any,
  noteCoords: [] as any,
  specialPosInit: false,
  /** 曲子付费类型 */
  paymentType: null,
  /** 曲子付费信息 */
  musicBuyInfo: null as any,
  // vip类型 VIP:会员 SVIP:SVIP，PERMANENT_SVIP:永久SVIP，NOT_VIP:不是vip
  vipType: '' as any,
  /** 播放模式，默认练习模式 */
  defaultModeType: 1,
  /** 音符最多歌词次数 */
  maxLyricNum: 0,
  /** 小节dom集合 */
  vfmeasures: [] as SVGAElement[],
  /** 作曲家 */
  musicComposer: '',
  /** 作词家 */
  musicLyricist: '',
  // 加载条
  isLoading: true,
  /** 加载中的文案 */
  loadingText: '音频资源加载中，请稍后…',
  /** 是否是简单的单行谱模式页面 */
  isSimplePage: false, 
  /** xml的速度和后台设置的速度，计算出的基础音频播放倍率 */
  originAudioPlayRate: 1,  
  /** 开始播放时，记录的mp3播放倍率，用户当前设置的速度/当前小节的速度 */
  basePlayRate: 1,
  /** 引导页显示状态 */
  hasDriverPop: false,
  /** 播放倍率不等于1，或者是选段评测，APP暂时不支持保存演奏，需要给出提示 */
  noSavePopShow: true,
  /** xml里面是否有歌词 */
  xmlHasLyric: false,
  /** 生成图片的模式 */
  isCreateImg: false,
  /** 切换谱面后，作业选段是否需要刷新 */
  workSectionNeedReset: false,
  /** 旋律线开关 */
  melodyLine: false,
  /** 是否是C调，切换到唱名时，只有C调所有的谱面类型都可以播放唱名文件；其它调的只有首调可以播放唱名，因为唱名是按照C调制作的，没有其它调的唱名文件 */
  isCTone: false,
  evxmlAddPartName: false, // 妙极客的部分曲子没有part-name，需要自行添加的part-name
  /** 乐器id */
  instrumentId: null,
  /** 是否是作业模式(包含练习和评测) */
  isHomeWork: false,
  /** 顶部栏高度 */
  headTopHeight: 0,
  /** 是否是自动重播，练习模式开启自动重播时，播放前不需要再次计算播放倍率了，还是按照上次的播放倍率播放音频 */
  isAutoRePlay: false,
  /** 跟练，是否收到了录音的回调 */
  hasFollowResult: false,  
  /** 右上角速度图标，根据当前小节的速度是几分音符的动态变化 */
  speedIcon: 'speed3', // 默认取1/4拍的图片
  /** 曲目点播价格 */
  musicPrice: 0,
  /** 专辑id */
  tenantAlbumId: '',
  /** 平台或者机构 */
  providerType: "PLATFORM" as "PLATFORM" | "TENANT",
});
const browserInfo = browser();
let offset_duration = 0;
/** 自定义数据 */
export const customData = reactive({
  /** 自定义音符时值 */
  customNoteRealValue: [] as any,
  /** 自定义音符按读取到的时值 */
  customNoteCurrentTime: false,
});
/** 在渲染前后计算光标应该走到的音符 */
export const setStep = () => {
  // console.log('播放状态',state.playState)
  if (state.playState !== "play") {
    console.log("暂停播放");
    return;
  }
  let startTime = Date.now();
  requestAnimationFrame(() => {
    const endTime = Date.now();
    // 渲染时间大于16.6,就会让页面卡顿, 如果渲染时间大与16.6就下一个渲染帧去计算
    if (endTime - startTime < 16.7) {
      handlePlaying();
      setStep();
    } else {
      setTimeout(() => {
        handlePlaying();
        setStep();
      }, 16.7);
    }
  });
};
/** 开始播放 */
export const onPlay = () => {
  console.log("开始播放", '音频总时长:', getAudioDuration());
  state.playEnd = false;
  // offset_duration = browserInfo.xiaomi ? 0.2 : 0.08;
  offset_duration = 0.2;
  setStep();
};

/** 播放模式结束自动重播 */
const autoResetPlay = () => {
  if (state.modeType !== "practise") return;
  // 如果是选段需要跳转到预备小节或者选段内的第一小节，否则跳转到第一小节
  const targetIdx = state.section.length === 2 ? state.sectionFirst?.i || state.section[1].i : 0;
  skipNotePlay(targetIdx, true);
  // 没有开启自动重播, 不是练习模式
  if (!state.setting.repeatAutoPlay) return;
  offsetTop = 0;
  scrollViewNote();
  setTimeout(() => {
    // 自动播放，不需要再次计算播放倍率
    state.isAutoRePlay = true;
    togglePlay("play");
  }, 1000);
};

/** 播放完成事件 */
export const onEnded = () => {
  console.log("音频播放结束");
  // if (state.isAppPlay) {
  //     // 销毁播放器
  //     api_cloudDestroy();
  // }
  if (state.playEnd) {
    console.log('音频播放结束，无需再次执行')
    return
  }
  // 修改状态为结束
  state.playEnd = true;
  state.playState = "paused";
  // 结束播放
  audioListStart(state.playState);
  // 调用结束评测
  handleEndEvaluat(true);
  // 调用自动重复播放
  autoResetPlay();
};

// 根据当前小节动态设置，右上角展示的速度
const dynamicShowPlaySpeed = (index: number, isPlaying?: boolean) => {
  //if (!headerColumnHide.value) {
    const item: any = state.times[index];
    if (item && item.measureSpeed ) {
      // console.log('速度1',item.measureSpeed)
      const newSpeed = state.basePlayRate * item.measureSpeed
      if (state.speed !== newSpeed) {
        state.speed = newSpeed;
      }
    }
  //}

}

// 开始播放时，计算mp3的播放倍率
export const initSetPlayRate = () => {
  // 自动播放，不需要再次计算播放倍率
  if (state.isAutoRePlay) {
    state.isAutoRePlay = false
    return
  }
  // let item: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
  let item: any = state.times[state.activeNoteIndex];
  console.log('播放状态',state.playState)
  if (item && item.measureSpeed) {
    const ratio = state.speed / item.measureSpeed
    // state.audiosInstance?.setSpeed(ratio)
    state.basePlayRate = ratio || 1;
    console.log('播放倍率',state.basePlayRate)
  }
}

// 重置播放倍率
export const resetBaseRate = (idx?: number) => {
  const index = idx ? idx : 0;
  let currentItem: any = state.times[index];
  const currentSpeed = currentItem?.measureSpeed ? currentItem.measureSpeed : state.originSpeed;
  // console.log('速度2',currentSpeed)
  state.speed = currentSpeed
  //state.activeNoteIndex = 0
  state.basePlayRate = 1;
}

/**
 * 播放一直触发的事件
 */
const handlePlaying = () => {
  const currentTime = getAudioCurrentTime();
  const duration = getAudioDuration();
  state.playProgress = (currentTime / duration) * 100;
  let item = getNote(currentTime);
  if (item) {
    // 选段状态下
    if (state.sectionStatus && state.section.length === 2) {
      // 如果开启了预备拍
      const selectStartItem = state.sectionFirst ? state.sectionFirst : state.section[0];
      const selectEndItem = state.section[1];
      if (currentTime - selectEndItem.endtime >= 0) {
        console.log("选段播放结束", state.setting.repeatAutoPlay);
        // 如果为选段评测模式
        if (state.modeType === "evaluating" && state.isSelectMeasureMode) {
          onEnded();
          return;
        }
        // #8698 bug修复
        
        if (state.modeType === "practise" && state.sectionStatus) {
          // 练习作业，练习完一次需要增加练习次数
          if (query.workRecord) {
            HANDLE_WORK_ADD()
          }
          onEnded();
          // state.activeNoteIndex = state.sectionFirst ? state.sectionFirst.i : state.section[0].i
          // dynamicShowPlaySpeed(state.activeNoteIndex)
          resetPlaybackToStart();
          return;
        }
        item = selectStartItem;
        setAudioCurrentTime(selectStartItem.time, selectStartItem.i);
      }
      /**
       * #9374，反复小节的曲目播放错误, bug修复
       * 曲目：噢！苏珊娜-排箫-人音
       * 现象：重播小节为2-9，选段为4-12，当播完第9小节后会回到第2小节重播2-8，再播放10-12
       * 4-12，不符合重播规则，所以播完第9小节后，音频需要跳转到第10小节播放
       */
      if (state.repeatInfo.length) {
        const canRepeatInfo = verifyCanRepeat(state.section[0].MeasureNumberXML, state.section[1].MeasureNumberXML)
        const repeatIdx = canRepeatInfo.repeatIdx == -1 ? 0 : canRepeatInfo.repeatIdx
        if (state.modeType === "practise" && !canRepeatInfo.canRepeat && state.section[1].MeasureNumberXML > state.repeatInfo[repeatIdx].end) {
          const preItem = state.times[item.i - 1]
          if (preItem && preItem.MeasureNumberXML > item.MeasureNumberXML) {
            const skipItem = state.times.find((item: any) => item.MeasureNumberXML === preItem.MeasureNumberXML + 1)
            if (skipItem) {
              // 跳转到指定的音频位置
              setAudioCurrentTime(skipItem.time, skipItem.i);
              gotoNext(skipItem);
              return
            }
          }
        }
      }

      // if (Math.abs(selectEndItem.endtime - currentTime) < offset_duration) {
      // if (currentTime - selectEndItem.endtime > offset_duration) {
      //console.log(currentTime,selectEndItem.endtime)

    }
    gotoNext(item);
    dynamicShowPlaySpeed(item.i, true);
  }

  // 评测不播放叮咚节拍器
  // if (state.modeType !== "evaluating") {
  //   metronomeData.metro?.sound(currentTime);
  // }
  // 不需要播放节拍器的声音，因为音频带有节拍器的声音
  metronomeData.metro?.sound(currentTime);
  // 一行谱，需要滚动小节
  if (state.isSingleLine) {
    moveSmoothAnimationByPlayTime()
  }

};
/** 跳转到指定音符开始播放 */
export const skipNotePlay = async (itemIndex: number, isStart = false, handType?: string) => {
  // 纯预览模式，练习、评测作业模式，禁止手动点击跳转音符
  if (state.isPreView) return;
  // 点击或者重播的时候清除一行谱的时间信息
  state.isSingleLine && (smoothAnimationState.oldCurrentTime = 0)
  if (handType === 'manual' && (query.workRecord || query.evaluatingRecord)) return;
  const item = state.times[itemIndex];
  // 如果是选段状态，可以点击段落范围内的音符，从当前音符开始播放，如果不是段落内的音符，直接return
  if (handType === 'manual' && state.section.length === 2 && !(item.MeasureNumberXML >= state.section[0].MeasureNumberXML && item.MeasureNumberXML <= state.section[1].MeasureNumberXML)) {
    return;
  }
  console.log('点击音符')
  let itemTime = item.time;
  if (isStart) {
    itemTime = 0;
  }
  if (item) {
    // 非选段模式,点击音符，动态设置右下角的速度
    if (item.measureSpeed && state.section.length < 2) {
      // console.log('速度3')
      state.speed = state.basePlayRate * 1000000 * item.measureSpeed / 1000000
    }
    setAudioCurrentTime(itemTime, itemIndex);
    // 一行谱，点击音符，或者播放完成，需要跳转音符位置
    gotoNext(item, true);
    // 不需要播放节拍器的声音，因为音频带有节拍器的声音
    // metronomeData.metro?.sound(itemTime);
    metronomeData?.metro?.findMetronomePosition(itemTime);
    if (state.isAppPlay) {
      await api_cloudSetCurrentTime({
        currentTime: itemTime * 1000,
        songID: state.examSongId,
      })
      audioData.progress = itemTime
      state.midiSectionStart = itemTime
    }
    // 如果是跟练模式
    if (followData.start) {
      skipNotePractice()
    }
  }
};

/**
 * 切换曲谱播放状态
 * @param playState 需要切换的状态 play:播放, paused: 暂停
 */
export const togglePlay = async (playState: "play" | "paused", isForceCLoseToast?:boolean) => {
  // 如果mp3资源还在加载中，给出提示
  if (!state.isAppPlay && !state.audioDone) {
    if (!isForceCLoseToast) showToast('音频资源加载中，请稍后')
    return
  }
  // 播放之前  当为评测模式和不为MIDI时候按  是否禁用节拍器  切换音源
  if (playState === 'play' && state.modeType === "practise" && state.playMode !== "MIDI") {
    console.log("设置音源")
    changeSongSourceByBeat(metronomeData.disable)
  }
  if (playState === 'play') {
    offsetTop = 0;
    scrollViewNote();
  }
  // midi播放
  if (state.isAppPlay) {
    if (playState === "paused") {
      await api_cloudSuspend({
        songID: state.examSongId,
      })
      state.playState = 'paused'
      // 当在节拍器播放期间暂停的话 就暂停节拍器
      closeTick()
      return
    }
    skipNotePlay(state.activeNoteIndex, false);
    await api_cloudChangeSpeed({
      speed: state.modeType === "evaluating" ? state.originSpeed : state.speed,
      originalSpeed: state.originSpeed,
      songID: state.examSongId,
    });
    const cloudGetMediaStatus = await api_cloudGetMediaStatus();
    const status = cloudGetMediaStatus?.content.status === "suspend" ? "play" : "paused"
    state.playState = status
  } else {
    state.playState = playState;
  }
  if (state.playState === "play" && state.sectionStatus && state.section.length == 2 && state.playProgress === 0) {
    resetPlaybackToStart();
  }
  // 当在节拍器播放期间暂停的话 就暂停节拍器
  if (state.playState === "paused") {
    closeTick()
  }
  // 设置为开始播放时, 如果需要节拍,先播放节拍器   只有在当前播放时间不为0的时候开启节拍器
  const isOneMeasureNumberXML = state.section.length === 2 && state.section[0].MeasureNumberXML === 2 //当是选段模式 并且开始小节是第二小节 就不播节拍器(这种情况有预选小节，currentTime是0)
  if (state.playState === "play" && getAudioCurrentTime() === 0 && !isOneMeasureNumberXML && ((state.playType === "play" && state.needTick) || (state.playType === "sing" && state.needSingTick))) {
    // 如果是系统节拍器 等系统节拍器播完了再播，如果是mp3节拍器 直接播
    if ((state.playType === "play" && !state.isOpenMetronome) || (state.playType === "sing" && !state.isSingOpenMetronome)) {
      const tickend = await handleStartTick();
      // console.log("🚀 ~ tickend:", tickend)
      // 节拍器返回false, 取消播放
      if (!tickend) {
        state.playState = "paused";
        return false;
      }
    } else {
      handleStartTick()
    }
  }
  // 如果选段没有结束, 直接开始播放,清空选段状态
  if (state.playState == "play") {
    if (state.sectionStatus && state.section.length < 2) {
      clearSelection();
    }
  }
  initSetPlayRate();
  audioListStart(state.playState);
  return true;
};
/** 结束播放 */
export const handleStopPlay = () => {
  state.playState = "paused";
  audioListStart(state.playState);
};

/** 重置播放为开始 */
export const resetPlaybackToStart = () => {
  // 如果为选段状态
  if (state.sectionStatus && state.section.length === 2) {
    state.section = formateSelectMearure(state.section);
    return;
  } else {
    // 非选段状态，重播需要重置当前选中的小节为第一个小节
    metronomeData.activeMetro = metronomeData.metroMeasure[0]?.[0] || {};
  }
  skipNotePlay(0, true);
};

/** 跳转到指定音符 */
export const gotoCustomNote = (index: number) => {
  console.log('跳转位置1111',index)
  try {
    state.osmd.cursor.reset();
  } catch (error) { }
  for (let i = 0; i < index; i++) {
    state.osmd.cursor.next();
  }
};

// 找出离目标元素最近的音符
const computedDistance = (x: number, y: number) => {
  let minDistance = -1, minidx = 0;
  let a, b, c;
  state.noteCoords.forEach((note: any, idx: any) => {
    //a，b为直角三角形的两个直角边
    a = Math.abs(note.x - x)
    b = Math.abs(note.y - y)
    //c为直角三角形的斜边
    c = Math.sqrt(a * a + b * b) as 0
    c = Number(c.toFixed(0)) as 0
    if (c !== 0 && (minDistance === - 1 || c < minDistance)) {
      //min为元素中离目标元素最近元素的距离
      minDistance = c
      minidx = idx
    }
  })
  return minidx
};


const setCursorPosition = (note: any, cursor: any, flag?: string) => {
  // console.log('音符',note?.i,state.osmd.Cursor.noteGraphicalId,note.svgElement?.attrs?.id)

  if (state.musicRenderType === EnumMusicRenderType.firstTone || state.musicRenderType === EnumMusicRenderType.fixedTone) {
    /**
     * bug：#9920、#9940
     * 简谱选段模式，预备小节为休止小节时，选段播放结束，指针会重置到第一小节位置的初始位置
     */
    if (state.sectionStatus && state.playState === 'paused' && state.sectionFirst && (note.multipleRestMeasures || note.MeasureNumberXML !== state.sectionFirst?.MeasureNumberXML)) {
      return

    }
    const specialIds = ['1788850864767643649', '1788502467554750466', '1788501975122489346'];
    if (specialIds.includes(state.cbsExamSongId) && note.multipleRestMeasures === 0) {
      // console.log('音符idx',note?.i,cursor.cursorElement.style.left)
      const cursorLeft = cursor?.cursorElement?.style?.left ? parseFloat(cursor.cursorElement.style.left) : 0;
      let patchX = 0;
      if (state.cbsExamSongId == '1788502467554750466') {
        if (state.musicRenderType === EnumMusicRenderType.firstTone) {
          patchX = (note.i == 0 || note.i == 60) ? 21 : (note.i == 1 || note.i == 7 || note.i == 23 || note.i == 38 || note.i == 44 || note.i == 52 || note.i == 58) ? -6 : (note.i >= 2 || note.i <= 6) || (note.i >= 8 || note.i <= 22) || (note.i >= 24 || note.i <= 37) || (note.i >= 39 || note.i <= 43) || (note.i >= 45 || note.i <= 51) || (note.i >= 53 || note.i <= 57) || (note.i == 59) ? 6 : 0;
        }
        if (state.musicRenderType === EnumMusicRenderType.fixedTone) {
          patchX = note.i == 0 ? 31 : (note.i == 8 || note.i == 14 || note.i == 30 || note.i == 45 || note.i == 51 || note.i == 59 || note.i == 65) ? -10 : note.i == 67 ? 31 : 0;
        }
      } else if (state.cbsExamSongId == '1788501975122489346') {
        if (state.musicRenderType === EnumMusicRenderType.firstTone) {
          patchX = (note.i == 0) ? 21 : (note.i == 1 || note.i == 7 || note.i == 23 || note.i == 38 || note.i == 44 || note.i == 52 || note.i == 58) ? -6 : (note.i == 9 || note.i == 10 || note.i == 12 || note.i == 13) ? 3 : (note.i == 14 || note.i == 30 || note.i == 45 || note.i == 51 || note.i == 59) ? 6 : (note.i == 45) ? -8 : (note.i >= 15 || note.i <= 29) || (note.i >= 31 || note.i <= 36) || (note.i >= 38 || note.i <= 44) || (note.i >= 46 || note.i <= 50) || (note.i >= 52 || note.i <= 58) || (note.i >= 60 || note.i <= 64) || (note.i == 66) ? 4 : 0;
        }
        if (state.musicRenderType === EnumMusicRenderType.fixedTone) {
          patchX = note.i == 0 ? 31 : (note.i == 8 || note.i == 14 || note.i == 30 || note.i == 45 || note.i == 51 || note.i == 59 || note.i == 65) ? -10 : note.i == 67 ? 31 : 0;
        }
      }
      if (flag === 'refresh' || (flag === 'init' && !state.specialPosInit)) {
        // console.log('音符idx',note?.i,cursor.cursorElement.style.left)
        cursor.cursorElement.style.left = cursorLeft + patchX + "px";
        state.specialPosInit = true;
      }
    } else {
      nextTick(() => {
        let bbox = note.bbox;
        if (!bbox) {
          const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || {
            x: 0,
            y: 0,
          };
          const parentLeft = musicContainer.x || 0;
          const noteEle = document.querySelector(`#vf-${note.svgElement?.attrs?.id}`);
          if (noteEle) {
            const noteHead = noteEle.querySelector(".vf-numbered-note-head");
            const noteHeadBbox = noteHead?.getBoundingClientRect?.();
            if (noteHeadBbox) {
              note.bbox = {
                left: noteHeadBbox.x - parentLeft - noteHeadBbox.width / 4,
                width: noteHeadBbox.width * 1.5,
              };
              bbox = note.bbox;
            }
          }
        }
        if (!bbox) return;
        const baseW = state.platform === IPlatform.PC ? 29 : 18;
        const width = (bbox.width - baseW) / 3;
        // console.log(555555,bbox.left,width)
        cursor.cursorElement.style.left = bbox.left + "px";
        cursor.cursorElement.style.transform = `translateX(${width}px)`;
      });
    }


  }
};
/** 
 * 跳转到下一个音符
 * 一行谱，点击音符，或者播放完成，需要跳转音符位置，增加参数skipNote
 **/
export const gotoNext = (note: any, skipNote?: boolean) => {
  // console.log(33333333333,state.activeNoteIndex,note.i)
  const num = note.i;

  if (state.activeNoteIndex === note.i) {
    /* 没有光标了  这里就算加上光标也要性能优化 */
    // try {
    //   setCursorPosition(note, state.osmd.cursor, 'init');
    // } catch (error) {
    //   console.log(error);
    // }
    // 重置 或者切换演奏演唱的时候 可能出现 state.activeNoteIndex === note.i的情况 执行
    if(state.playState === "paused"){
      // fillWordColor();
    }
    if (state.isSingleLine && state.playState === "paused") {
      moveSvgDom(skipNote);
    }
    return;
  }
  const osmd = state.osmd;
  let prev = state.activeNoteIndex;
  state.activeNoteIndex = num;
  state.activeMeasureIndex = note.MeasureNumberXML;
  osmd.cursor.activeMeasureNum = note.MeasureNumberXML;
  // 赋值音符id
  osmd.cursor.noteGraphicalId = state.times[state.activeNoteIndex].id;
  // 设置光标位置
  // nextTick(() => {
  //     if (osmd.cursor.noteGraphicalId) {
  //     const { x, y } = document.getElementById(`vf-${osmd.cursor.noteGraphicalId}`)?.getBoundingClientRect() || { x: 0, y: 0}
  //     osmd.cursor.cursorElement.style.left = x + "px";
  //     // cursorElement.style.top = y + "px";
  //   }
  // })
  dynamicShowPlaySpeed(state.activeNoteIndex);
  if (prev && num - prev === 1) {
    // console.log('跳转音符',11111,osmd.cursor)
    // if (!note.id && note.multipleRestMeasures === 0) {

    // } else {
    //   osmd.cursor.next();
    // }
    osmd.cursor.next();

  } else if (prev >= 0 && num - prev > 0) {
    while (num - prev > 0) {
      prev++;
      // console.log('跳转音符',22222)
      osmd.cursor.next();
    }
  } else if (prev >= 0) {
    // gotoCustomNote(num);
    while (prev - num > 0) {
      prev--;
      // console.log('跳转音符',22222)
      osmd.cursor.previous();
    }
  }
  /* 取消光标了 */
  // try {
  //   setCursorPosition(note, state.osmd.cursor, 'refresh');
  // } catch (error) {
  //   console.log(error);
  // }
  // fillWordColor();
  // 一行谱，需要滚动小节
  if (state.isSingleLine && state.playState === "paused") {
    moveSvgDom(skipNote);
  }
  scrollViewNote();
};
/** 获取指定音符 */
export const getNote = (currentTime: number) => {
  const times = state.times;
  const len = state.times.length;
  /** 播放超过了最后一个音符的时间,直接结束, 2秒误差 */
  if (currentTime > times[len - 1].endtime + 2 && !state.isAppPlay && !state.isSimplePage) {
    // onEnded();
    return;
  }
  let _item = null as any;
  for (let i = state.activeNoteIndex; i < len; i++) {
    let item = times[i];
    const prevItem = times[i - 1];
    // if (state.isEvxml) {
    //   let diffArr: any[] = [];
    //   times.forEach((note: any, noteIdx: number) => {
    //     if (currentTime >= note.time && currentTime <= times[noteIdx+1].time) {
    //       let diffTime = times[noteIdx+1].time - currentTime;
    //       diffArr.push({
    //         diffTime,
    //         idx: noteIdx
    //       })
    //     }
    //   })
    //   diffArr.sort((a, b) => a.diffTime - b.diffTime);
    //   item = diffArr.length ? times[diffArr[0].idx] : item;
    // }
    if (currentTime >= item.time) {
      if (!prevItem || item.time != prevItem.time) {
        _item = item;
      }
    } else {
      break;
    }
  }
  // console.log("activeNoteIndex", currentTime, state.activeNoteIndex, _item.i);
  return _item;
};

/** 重播 */
export const handleResetPlay = () => {
  // 如果是midi需要重置播放进度
  if (state.isAppPlay) {
    audioData.progress = 0
  }
  // 如果是作业模式，不还原速度
  /**
   * #TODO：2024.09.14，业务需求变更，重播不还原用户设置的速度
   */
  // if (!query.workRecord) {
  //   resetBaseRate();
  // }
  resetPlaybackToStart();
  // 如果是暂停, 直接播放
  togglePlay("play");
};
/** 设置速度 */
export const handleSetSpeed = (speed: number) => {
  // setStorageSpeed(state.examSongId, speed);
  state.speed = speed;
  // 当前的音符
  // const currentItem: any = (state.sectionStatus && state.section.length === 2) ? state.section[0] : state.times[state.activeNoteIndex];
  const currentItem: any = state.times[state.activeNoteIndex];
  state.basePlayRate = currentItem?.measureSpeed ? state.speed / currentItem.measureSpeed : state.speed / state.originSpeed;
  const actualRate = state.originAudioPlayRate * state.basePlayRate;
  console.log('速度设置',speed,'小节计算的倍率',state.basePlayRate,'实际播放倍率',actualRate)
};
/** 清除选段状态 */
export const clearSelection = () => {
  state.sectionStatus = false;
  state.section = [];
  closeToast();
};

/** 开启选段 */
export const handleChangeSection = () => {
  // 如果开启了选段,再次点击取消选段
  if (state.sectionStatus) {
    togglePlay("paused");
    clearSelection();
    // 重置速度和播放倍率
    resetBaseRate(state.activeNoteIndex);
    //skipNotePlay(0, true);  取消选段的时候 不跳回开头
    state.sectionFirst = null;
    // IOS18.1.1浏览器渲染更新有问题，需要手动更新一下
    const selectionDom = document.getElementById('selectionBox')
    if (selectionDom) {
      selectionDom.style.display = 'none';
      requestAnimationFrame(() => {
        selectionDom.style.display = 'block';
      })
    }    
    return;
  }
  state.sectionStatus = true;
  // 开启
  if (state.sectionStatus) {
    togglePlay("paused");
  }
  showToast({
    message: "请选择开始小节",
    duration: 0,
    position: "top",
    className: "selectionToast",
  });
};

// 重置设置
export const resetSettings = () => {
  // 切换模式，清除选段
  metronomeData.cursorMode = state.modeType === 'follow' ? 1 : 2;
  state.noSavePopShow = true;
  metronomeData.activeIndex = null;
  clearSelection();
  skipNotePlay(0, true);
  resetBaseRate();
  resetCursorPosition();
  // midi 重置播放进度
  if (state.isAppPlay) {
    audioData.progress = 0;
  }
  showToast({
    message: "已将所有参数重置到默认状态",
    position: "top",
  });
};

/** 效验并格式化选段小节 */
const formateSelectMearure = (_list: any[]): any[] => {
  if (!_list.length) return [];
  const list = _list.sort((a, b) => a.time - b.time);
  const startXml = list[0]?.measureOpenIndex;
  const endXml = list.last()?.measureOpenIndex;
  const selectStartMeasure = state.times.filter((n: any) => startXml === n.measureOpenIndex) || [];
  const selectEndMeasure = state.times.filter((n: any) => endXml === n.measureOpenIndex) || [];
  // 没有找到选段小节
  if (!selectStartMeasure.length || !selectEndMeasure.length) {
    clearSelection();
    return [];
  }
  list[0] = selectStartMeasure[0];
  list[1] = selectEndMeasure.last();
  let startItemINdex = list[0].i;
  // 开启预备拍
  if (state.isOpenPrepare) {
    const startXmlIndex = list[0].MeasureNumberXML;
    state.sectionFirst = state.times.find((n: any) => startXmlIndex - n.MeasureNumberXML === 1);
    startItemINdex = state.sectionFirst ? state.sectionFirst.i : startItemINdex;
  }
  skipNotePlay(startItemINdex, startItemINdex === 0);
  return list;
};

/** 选择选段 */
export const handleSelection = (item: any) => {
  if (!state.sectionStatus || state.section.length > 1) return;
  if (state.section.length !== 2 && item) {
    state.section.push(item);
    if (state.section.length === 2) {
      setSection(state.section[0].MeasureNumberXML, state.section[1].MeasureNumberXML)
      //state.section = formateSelectMearure(state.section);
      closeToast();
      resetCursorPosition();
    }
  }
  if (state.section.length === 1) {
    showToast({
      message: "请选择结束小节",
      duration: 0,
      position: "top",
      className: "selectionToast",
    });
  }
};

/** 阶段练习、阶段评测设置选段小节 */
export const setSection = (start: number, end: number, userSpeed?: number) => {
  const startNotes = state.times.filter(
    (n: any) => n.noteElement.sourceMeasure.MeasureNumberXML == start
  )
  const endNotes = state.times.filter(
    (n: any) => n.noteElement.sourceMeasure.MeasureNumberXML == end
  )
  state.userChooseEndIndex = end
  const lastEndId = endNotes[endNotes.length - 1].noteId
  let lastEndNotes = endNotes.filter((n: any) => n.noteId === lastEndId)
  // 是否符合重播规则
  const canRepeatInfo = verifyCanRepeat(start, end)
  console.log('能否重播', canRepeatInfo)
  const isCanRepeat = canRepeatInfo.canRepeat
  // 如果符合重播规则，但是lastEndNotes长度为1，则需要向前找，直到找到lastEndNotes长度为2
  /**
   * 如果符合重播规则，但是lastEndNotes长度为1，则需要向前找，直到找到lastEndNotes长度为2
   * 2024.03.28 新增逻辑，如果当前选中的结束小节是跳房子，则不向前找，因为这种场景不符合重播规则（bug：#9921）
   * TODO:通过vf-volta判断是否是跳房子
   */
  let isSkipVolta = false; // 结束小节是否是跳房子小节
  if (lastEndNotes.length === 1) {
    isSkipVolta = lastEndNotes[0]?.stave?.modifiers?.some((item: any) => item.getAttribute('type') === 'Volta')
  }
  let currentEndNum: number = end
  const lastEndIndex: any = state.repeatInfo[canRepeatInfo.repeatIdx]?.end || 0
  while (isCanRepeat && lastEndNotes.length === 1 && lastEndNotes[0].MeasureNumberXML <= lastEndIndex && !isSkipVolta) {
    currentEndNum = currentEndNum - 1
    const newEndNotes = state.times.filter(
      (n: any) => n.noteElement.sourceMeasure.MeasureNumberXML == currentEndNum
    )
    const newLastEndId = newEndNotes[newEndNotes.length - 1].noteId
    lastEndNotes = newEndNotes.filter((n: any) => n.noteId === newLastEndId)
  }

  const endIdx: any = (isCanRepeat && canRepeatInfo.repeatIdx == state.repeatInfo.length - 1) ? lastEndNotes.length - 1 : 0
  const startNote = startNotes[0]
  // const endNote = endNotes[endNotes.length - 1]
  const endNote = lastEndNotes[endIdx]

  if (startNote && endNote) {
    state.isSelectMeasureMode = true;
    // 设置小节
    hanldeDirectSelection([startNote, endNote]);

    // 评测作业，练习作业的场景，需要用老师布置的速度，设置播放速度
    if (userSpeed) {
      handleSetSpeed(userSpeed);
    }
  }
}


/** 直接设置选段 */
export const hanldeDirectSelection = (list: any[]) => {
  if (!Array.isArray(list) || list.length !== 2) return;
  state.sectionStatus = true;
  setTimeout(() => {
    state.section = formateSelectMearure(list);
    // 选段完成后，需要根据预报小节的速度，设置右下角显示的速度
    const currentItem: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
    if (currentItem.measureSpeed && query.workRecord === undefined && query.evaluatingRecord === undefined) {
      handleSetSpeed(currentItem.measureSpeed);
    }
    console.log('选段小节', state.section)
  }, 0);
};
let offsetTop = 0, musicScrollTop = 0;
/**
 * 窗口内滚动到音符的区域
 * @param isScroll 可选: 强制滚动到顶部, 默认: false
 * @returns void
 */
export const scrollViewNote = (resetTop?: boolean) => {
  // const cursorElement = document.getElementById("cursorImg-0")!;
  if (state.isSingleLine) {
    return;
  }
  const noteId = state.times[state.activeNoteIndex].id;
  // if (!noteId) {
  //   return;
  // }
  const domId = "vf" + noteId;
  // 合并休止小节没有音符，取小节的位置，否则取音符指针位置
  const cursorElement: any = !noteId && state.times[state.activeNoteIndex]?.totalMultipleRestMeasures ? document.querySelector(`.measureIndex_${state.activeMeasureIndex}`) : document.querySelector(`[data-vf=${domId}]`)?.parentElement;
  const musicAndSelection = document.getElementById(state.scrollContainer)!;
  if (!state.headTopHeight) {
    state.headTopHeight = document.querySelector('.headHeight')?.getBoundingClientRect()?.height || 100;
  }
  // console.log('滑动',musicAndSelection?.scrollTop, musicScrollTop)
  if (!cursorElement || !musicAndSelection) {
    return
  }
  if (Math.abs(musicAndSelection?.scrollTop - musicScrollTop) > 30) {
    // 手动滑动谱面，重新播放需要滚动到对应位置
  } else {
    if (offsetTop === cursorElement.offsetTop || Math.abs(offsetTop - cursorElement.offsetTop) < 30) return;
  }
  offsetTop = cursorElement.offsetTop;
  if (offsetTop > (state.headTopHeight + 30)) {
    musicScrollTop = (offsetTop - state.headTopHeight - 30) * state.musicZoom
    musicAndSelection.scrollTo({
      top: (offsetTop - state.headTopHeight - 30) * state.musicZoom,
      behavior: "auto",
    });
  } else {
    musicScrollTop = 0
    musicAndSelection.scrollTo({
      top: 0,
      behavior: "auto",
    });
  }
};

/** 检测是否是节奏练习 */
export const isRhythmicExercises = () => {
  return state.examSongName.indexOf("节奏练习") > -1;
};

/** 重置状态 */
export const handleRessetState = () => {
  // 切换模式，清除选段
  state.noSavePopShow = true;
  clearSelection();
  skipNotePlay(0, true);
  resetBaseRate();
  // midi 重置播放进度
  if (state.isAppPlay) {
    audioData.progress = 0;
  }
  if (state.modeType === "evaluating") {
    handleStartEvaluat();
  } else if (state.modeType === "practise") {
    togglePlay("paused", true);
  } else if (state.modeType === "follow") {
    toggleFollow(false);
  }
};
export default state;

/** 初始化评测音频 */
export const evaluatCreateMusicPlayer = () => {
  return api_createMusicPlayer({
    musicSrc: state.accompany || state.music, // 曲谱音频url
    // tuneSrc: "https://oss.dayaedu.com/cloud-coach/1686725501654check_music1_(1).mp3", //效音音频url
    tuneSrc: "https://oss.dayaedu.com/MECMP/1722593665681.mp3", //效音音频url
    checkFrequence: 496,
  });
};



/** 获取内容平台的接口详情并初始化state信息 */
export const getMusicDetail = async (id: string, type?: string, musicId?: string) => {
  const res = await getMusicSheetDetail(id, type, musicId);
  if (res?.code === 200) {
    await getMusicInfo(res)
  }
};


const getMusicInfo = async (res: any) => {
  // 是否支持总谱
  state.isScoreRender = res.data?.isScoreRender
  // 是否默认显示总谱
  state.defaultScoreRender = res.data?.defaultScoreRender
  // 是否显示节拍器  (管乐迷 默认显示节拍器)
  //state.isMixBeat = res.data?.isMixBeat  
  /* 设置partIndex */
  let partIndexs = query["part-index"] ? query["part-index"].split(",") : ["-1"] // -1为partIndex没有值的时候
  partIndexs = partIndexs.map((indexStr:string) => {
    return parseInt(indexStr)
  }).sort((a, b) => a - b);
  let partIndex = partIndexs[0]
  // 当partIndexs 大于1个的时候，代表用户自己选择了多个声部，用总谱渲染的逻辑
  if(partIndexs.length > 1){
    partIndex = 999
    state.combinePartIndexs = partIndexs
  }
  // 如果是评测报告，会有默认的分轨index
  if (state.isEvaluatReport) {
    partIndex = state.partIndex;
  }
  // 布置作业 取作业的乐器id
  let workRecordInstrumentId:undefined | string
  // multiTracksSelection 返回为空，默认代表全部分轨
  state.canSelectTracks = res.data.multiTracksSelection === "null" || res.data.multiTracksSelection === "" || res.data.multiTracksSelection === null ? [] : res.data.multiTracksSelection?.split(',');
  state.canSelectTracks = state.canSelectTracks.map((item: any)=>item.trim())
  /* 获取声轨列表 */
  let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
  xmlString = xmlAddPartName(xmlString);
  downloadXmlStr.value = xmlString //给musice-score 赋值xmlString 以免加载2次
  const tracks = xmlToTracks(xmlString) //获取声轨列表
  // 如果是多个分轨合并显示的，需要记录下所选分轨的第一个分轨的名字，渲染计算音符位置的时候需要根据第一个分轨找到对应音符的位置
  if (state.combinePartIndexs.length) {
    (window as any).DYFirstTrackName = tracks[state.combinePartIndexs[0]] || '';
  } else {
    (window as any).DYFirstTrackName = '';
  }
  // 如果是作业模式，需要默认渲染当前学生声部对应的声轨，并且默认不显示总谱
  if (state.isHomeWork && storeData.user?.instrumentId) {
    const currentTrack = res.data.musicSheetSoundList.find((item: any) => item.musicalInstrumentId === storeData.user?.instrumentId)?.track;
    if (currentTrack) {
      partIndex = tracks.findIndex(item => item === currentTrack) || partIndex
      state.defaultScoreRender = false
    }
  }
  // 设置音源  track 为当前的声轨 index为当前的
  const { track, index, musicalInstrumentId } = state.isSimplePage ? { track:tracks[0], index: state.partIndex, musicalInstrumentId: '' } : initMusicSource(res.data, tracks, partIndex, workRecordInstrumentId)
  // 这里返回的track可能和实际的对不上，所以重新筛选一下
  const realTrack = musicalInstrumentId && res.data?.musicalInstruments?.length ? res.data?.musicalInstruments.find((item: any) => item?.id == musicalInstrumentId)?.code?.split(',')?.[0] : '';
  const musicInfo = {
    ...res.data,
    track: res.data.musicSheetType === 'CONCERT' ? track : realTrack
  };
  console.log("🚀 ~ musicInfo:", musicInfo);
  setState(musicInfo, index);
};
//获取xml中的音轨数据
function xmlToTracks(xmlString: string) {
  const xmlParse = new DOMParser().parseFromString(xmlString, "text/xml");
  const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
  return partNames.reduce((arr: string[], item) => {
    const textContent = item?.textContent?.trim()
    if (textContent?.toLocaleLowerCase() === "common") {
      (window as any).HasCommonTrack = true;
    }
    if (textContent != "COMMON" && textContent != "common" && textContent) {
      arr.push(textContent)
    }
    return arr
  }, []);
}
// 设置音源
function initMusicSource(data: any, tracks: string[], partIndex: number, workRecordInstrumentId?: string) {
  let track:string,index:number, musicalInstrumentId: string
  let instrumentId = workRecordInstrumentId || query.instrumentId || storeData.user?.instrumentId
  // 打击乐特殊处理  当曲目乐器为2266（打击乐（键盘））和2267（小军鼓）时候，从打击乐分类下 进入的时候
  if(["2266", "2267"].includes(data.musicalInstrumentIds)){
    instrumentId = data.musicalInstrumentIds
  }
  state.instrumentId = instrumentId;
  let { musicSheetType, isAllSubject, musicSheetSoundList, musicSheetAccompanimentList } = data
  musicSheetSoundList || (musicSheetSoundList = [])
  musicSheetAccompanimentList || (musicSheetAccompanimentList = [])
  let musicObj, accompanyObj, fanSongObj, banSongObj
   /* 独奏 */
  if (musicSheetType === "SINGLE") {
    accompanyObj = musicSheetAccompanimentList.find((item: any) => {
      return item.audioPlayType === "PLAY"
    })
    // 如果specialInstrumentIds有多个，是打击乐的声部，打击乐声部可能有多个乐器id
    if (storeData.user?.specialInstrumentIds?.length > 1) {
      musicObj = musicSheetSoundList.find((item: any) => {
        return isAllSubject ? item.audioPlayType === "PLAY" : (item.audioPlayType === "PLAY" && storeData.user?.specialInstrumentIds?.includes(item.musicalInstrumentId))
      })
    } else {
      // 是否全声部(isAllSubject)为true 时候没有乐器只有一个原音(比如节奏练习，这个曲子全部乐器都支持);当前用户有乐器就匹配  不然取第一个原音
      musicObj = musicSheetSoundList.find((item: any) => {
        return isAllSubject ? item.audioPlayType === "PLAY" : (item.audioPlayType === "PLAY" && item.musicalInstrumentId == instrumentId)
      })
    }
    // 当没有找到原音的时候，并且instrumentId没有值的时候，取默认第一个乐器
    if(!musicObj && !instrumentId){
      musicObj = musicSheetSoundList.find((item: any) => {
        return item.audioPlayType === "PLAY"
      })
    }
    fanSongObj = musicSheetSoundList.find((item: any) => {
      return item.audioPlayType === "SING"
    })
    banSongObj = musicSheetAccompanimentList.find((item: any) => {
      return item.audioPlayType === "SING"
    })
    track = musicObj?.track   //没有原音的时候track为空 不显示指法
    index = tracks.findIndex(item => {
      return item === track
    })
    musicalInstrumentId = musicObj?.musicalInstrumentId
  } else {
    /* 合奏 */
    // 支持总谱 并且当前是总谱。partIndex是999时候，或者默认是总谱并且partIndex为-1时候  -1就是partIndex没有值
    if((state.isScoreRender && (partIndex===999 || (state.defaultScoreRender && partIndex===-1))) || state.combinePartIndexs.length > 1){
        // 总谱渲染
        state.isCombineRender = true
        banSongObj = musicSheetAccompanimentList.find((item: any) => {
          return item.audioPlayType === "SING"
        })
        // 总谱演唱模式是 范唱，取banSongObj 里面的scoreAudioFileUrl字段
        // 先取scoreAudioFileUrl的值 如果 没有就是空
        if(banSongObj){
          fanSongObj = {
            audioFileUrl: banSongObj.scoreAudioFileUrl,
            audioBeatMixUrl: banSongObj.scoreAudioBeatMixUrl
          }
        }
        // 总谱 需要播放各个声部的音频
        if(state.combinePartIndexs.length) {
          // 当选择多个分轨时候
          state.combinePartIndexs.map( partI => {
            const musicSheetSound = musicSheetSoundList.find((item:any)=>{
              return item.track?.toLowerCase().trim() === tracks[partI]?.toLowerCase().trim()
            })
            musicSheetSound?.audioFileUrl && (audioData.combineMusics[partI] = musicSheetSound.audioFileUrl)
          })
        }else{
          tracks.map((itemTrack:any, partI:number) => {
            const musicSheetSound = musicSheetSoundList.find((item:any)=>{
              return item.track?.toLowerCase().trim() === itemTrack?.toLowerCase().trim()
            })
            musicSheetSound?.audioFileUrl && (audioData.combineMusics[partI] = musicSheetSound.audioFileUrl)
          })
        }        
        // 总谱演奏模式是 伴奏
        accompanyObj = musicSheetAccompanimentList.find((item: any) => {
          return item.audioPlayType === "PLAY"
        })
        track = "总谱"
        index = 999
        musicalInstrumentId = ''
    }else{
      // 合奏只显示一个声轨   当为-1时候，取tracks中 后端勾选了的第一个值
      track =  partIndex === -1 ? tracks.find(value => state.canSelectTracks.includes(value))! : tracks[partIndex]
      // 根据当前的声轨 取数据
      musicObj = musicSheetSoundList.find((item: any) => {
        return item.audioPlayType === "PLAY" && item.track === track
      })
      fanSongObj = musicSheetSoundList.find((item: any) => {
        return item.audioPlayType === "SING" && item.track === track
      })
      banSongObj = musicSheetAccompanimentList.find((item: any) => {
        return item.audioPlayType === "SING"
      })
      accompanyObj = musicSheetAccompanimentList.find((item: any) => {
        return item.audioPlayType === "PLAY"
      })
      index = tracks.findIndex(item => {
        return item === track
      })
      musicalInstrumentId = musicObj?.musicalInstrumentId
    }
    state.partListNames = tracks
  }
  /*  目前 管乐迷没有演唱模式 所以先排除掉演唱模式的数据 */
  if(fanSongObj?.audioFileUrl || banSongObj?.audioFileUrl || fanSongObj?.solmizationFileUrl || fanSongObj?.femaleSolmizationFileUrl){
    fanSongObj?.audioFileUrl && (fanSongObj.audioFileUrl = null)
    banSongObj?.audioFileUrl && (banSongObj.audioFileUrl = null)
    fanSongObj?.solmizationFileUrl && (fanSongObj.solmizationFileUrl = null)
    fanSongObj?.femaleSolmizationFileUrl && (fanSongObj.femaleSolmizationFileUrl = null)
  }
  // 当没有任何曲目的时候报错
  if (!musicObj?.audioFileUrl && !accompanyObj?.audioFileUrl && !fanSongObj?.audioFileUrl && !banSongObj?.audioFileUrl && !fanSongObj?.solmizationFileUrl && !fanSongObj?.femaleSolmizationFileUrl) {
    state.noMusicSource = !data.midiFileUrl ? true : false // 没有音源文件
    // 独奏的时候
    if(musicSheetType === "SINGLE"){
      // 并且是midi没有midi文件的时候
      if(data.playMode === "MIDI" && !data.midiFileUrl) {
        // 是预览的时候 不报错
        if(!query.isPreView){
          throw new Error("该曲目无任何音源");
        }
      }
    }
  }
  Object.assign(state, {
    music: musicObj?.audioFileUrl,
    accompany: accompanyObj?.audioFileUrl,
    fanSong: fanSongObj?.audioFileUrl,
    banSong: banSongObj?.audioFileUrl,
  })
  // 如果没有男唱名
  if(!fanSongObj?.solmizationFileUrl){
    state.mingSong = fanSongObj?.femaleSolmizationFileUrl
  }else{
    state.mingSong = fanSongObj?.solmizationFileUrl
    state.mingSongGirl = fanSongObj?.femaleSolmizationFileUrl
  }
   /*  目前 管乐迷没有用到 后台生成的节拍器 */
  // 当使用节拍器的时候才加载节拍器音频
  if(state.isMixBeat && false) {
    Object.assign(state.beatSong, {
      music: musicObj?.audioBeatMixUrl,
      accompany: accompanyObj?.audioBeatMixUrl,
      fanSong: fanSongObj?.audioBeatMixUrl,
      banSong: banSongObj?.audioBeatMixUrl
    })
    // 如果没有男唱名
    if(!fanSongObj?.solmizationBeatUrl){
      state.beatSong.mingSong = fanSongObj?.femaleSolmizationBeatUrl
    }else{
      state.beatSong.mingSong = fanSongObj?.solmizationBeatUrl
      state.beatSong.mingSongGirl = fanSongObj?.femaleSolmizationBeatUrl
    }
  }
  return {
    index,
    track,
    musicalInstrumentId
  }
}
const setState = (data: any, index: number) => {
  // 获取当前模式 声部切换用
  const localStoragePlayType = localStorage.getItem("musicScorePlayType")
  if(localStoragePlayType) {
    localStorage.removeItem("musicScorePlayType")
    const fields = localStoragePlayType.split(',')
    state.playType = fields[0] as any
    state.playSource = fields[1] as any
  }
  // 根据当前文件有没有 设置当前的播放模式
  const playObj = {
    "play_music":"music",
    "play_background":"accompany",
    "sing_music":"fanSong",
    "sing_background":"banSong",
    "sing_mingSong":"mingSong",
  } as any
  // 当前缓存 有值的时候 用这个，没有的时候 走筛选
  // @ts-ignore
  if(!state[playObj[`${state.playType}_${state.playSource}`]]){
    if(state.playType === "play"){
      if(state.music){
        state.playSource = "music"
      }else if(state.accompany){
        state.playSource = "background"
      }else{
        if(state.fanSong){
          state.playType = "sing"
          state.playSource = "music"
        }else if(state.banSong){
          state.playType = "sing"
          state.playSource = "background"
        }else if(state.mingSong){
          state.playType = "sing"
          state.playSource = "mingSong"
        }
      }
    }else{
      if(state.fanSong){
        state.playSource = "music"
      }else if(state.banSong){
        state.playSource = "background"
      }else if(state.mingSong){
        state.playSource = "mingSong"
      }else{
        if(state.music){
          state.playType = "play"
          state.playSource = "music"
        }else if(state.accompany){
          state.playType = "play"
          state.playSource = "background"
        }
      }
    }
  }
  state.appName = "COLEXIU";
  state.detailId = data.bizId;
  state.musicPrice = data.musicPrice || 0;
  state.xmlUrl = data.xmlFileUrl;
  // 曲子付费类型
  state.paymentType = data.paymentType;
  // 是否已经购买
  state.musicBuyInfo = {
    id: data.bizId,
    musicPrice: data.musicPrice,
    paymentType: data.paymentType,
    buyed: data.buyed
  }
  state.partIndex = index >= 0 ? index : 0;
  state.trackId = data.track;
  state.subjectId = data.subjectIds ? data.subjectIds.split(',')?.[0] : 0;
  // 声部code
  const subjectCode = data.subjectCodes ? data.subjectCodes.split(',')?.[0] : '';
  // 打击乐列表查询用
  state.specialPercussionFlag = data.specialPercussionFlag
  state.musicalInstrumentIds = data.musicalInstrumentIds || ""
  // 乐器code
  // let musicalCode = data.musicalInstrumentIdCodes ? data.musicalInstrumentIdCodes.split(',')?.[0] : '';
  /**
   * 单曲，指法根据用户当前的乐器来显示，如果没有则取musicSheetSoundList第一个track
   */
  // const currentInstrumentId = query.instrumentId || storeData.user?.instrumentId;
  // let musicalCode = !currentInstrumentId ? data.musicSheetSoundList?.find((item:any)=>{ return item.audioPlayType === "PLAY" })?.track || '' : data.musicSheetSoundList?.find((item: any) => item?.musicalInstrumentId == currentInstrumentId && item.audioPlayType === "PLAY")?.track || '';
  // const pitchSubject = musicalInstrumentCodeInfo.find((n) => n.code.toLocaleLowerCase() === subjectCode.toLocaleLowerCase())
  // const pitchMusical = musicalInstrumentCodeInfo.find((n) => n.code.toLocaleLowerCase() === musicalCode.toLocaleLowerCase())
  // state.subjectCodeId = pitchSubject ? pitchSubject.id : 0
  // state.musicalCodeId = pitchMusical ? pitchMusical.id : 0
  state.categoriesId = data.musicCategoryId;
  state.categoriesName = data.musicTagNames;
  // state.enableEvaluation = data.isEvaluated ? true : false;
  state.examSongId = data.bizId + "";
  state.cbsExamSongId = data.id + "";
  state.examSongName = data.name;
  state.coverImg = data.musicCover ?? "";
  // 如果是simple页面，只显示单轨
  if (state.isSimplePage) {
    state.isCombineRender = false;
  }
  setCustom(state.isCombineRender ? data.musicSheetSoundList?.length : 0);
  // 解析扩展字段
  if (data.extConfigJson) {
    try {
      state.extConfigJson = JSON.parse(data.extConfigJson as string);
    } catch (error) {
      console.error("解析扩展字段错误:", error);
    }
  }
  state.gradualTimes = state.extConfigJson.gradualTimes;
  state.repeatedBeats = state.extConfigJson.repeatedBeats || 0;
  state.isEvxml = state.extConfigJson.isEvxml == 1 ? true : false;
  // 是否开启节拍器
  state.needTick = !!data.isPlayBeat
  state.needSingTick = !!data.isPlaySingBeat
  // state.isOpenMetronome = data.isUseSystemBeat ? false : true;
  // 演奏模式是否播mp3节拍器
  state.isOpenMetronome = data.isPlayBeat && !data.isUseSystemBeat ? true : false
  // 演唱模式是否播mp3节拍器
  state.isSingOpenMetronome = data.isPlaySingBeat && !data.isUseSingSystemBeat ? true : false
  state.isShowFingering = data.isShowFingering ? true : false;
  // 设置曲谱的播放模式, APP播放(midi音频是app播放) | h5播放
  state.isAppPlay = data.playMode === 'MIDI';
  if (state.isAppPlay) {
    state.isOpenMetronome = false
    state.isSingOpenMetronome = false
  }
  state.midiUrl = data.midiFileUrl;
  state.parentCategoriesId = data.musicTag;
  state.musicSheetCategoriesId = data.musicCategoryId;
  state.bizMusicCategoryId = data.bizMusicCategoryId
  state.playMode = data.playMode === "MP3" ? "MP3" : "MIDI";
  // 设置速度节拍
  state.speedBeatUnit = data.speedBeatUnit || "1/4"
  // 这里把后台设置的速度 转换为1/4拍的速度 
  state.originSpeed = state.speed = speedBeatTo({unit: data.speedBeatUnit || "1/4",speed: parseFloat(data.playSpeed) || 0}, `1/4`);
  const track = data.code || data.track;
  state.track = track ? track.replace(/ /g, "").toLocaleLowerCase() : "";
  // 能否评测，根据当前声轨有无伴奏判断
  if (state.isAppPlay) {
    state.enableEvaluation = state.midiUrl ? true : false
  } else {
    state.enableEvaluation = state.accompany || state.music ? true : false
  }
  state.isConcert = data.musicSheetType === "CONCERT" ? true : false;
  // 开启预备小节
  state.isOpenPrepare = true;
  state.extStyleConfigJson = data.extStyleConfigJson || {}
  state.extJianStyleConfigJson = data.extJianStyleConfigJson || {}
  // console.log("🚀 ~ state.subjectId:", state.subjectId, state.track as any , state.subjectId)
  // 是否打击乐
  /**
   * 是否打击乐：AMPLITUDE & 节奏练习：DECIBELS
   * evaluationStandard：("评测标准 节奏 AMPLITUDE 音准 FREQUENCY 分贝 DECIBELS")
   * 打击乐&节奏练习，没有跟练模式
   */
  // state.isPercussion = isRhythmicExercises();
  state.isPercussion = data.evaluationStandard === "AMPLITUDE" || data.evaluationStandard === "DECIBELS";
  state.evaluationStandard = data.evaluationStandard?.toLocaleLowerCase() || ''
  // 设置是否特殊曲谱, 是特殊曲谱取反(不理解之前的思考逻辑), 使用后台设置的速度
  state.isSpecialBookCategory = !classids.includes(Number(data.musicCategoryId));

  // 设置指法
  // const code = state.isConcert ? mappingVoicePart(state.trackId, "ENSEMBLE") : mappingVoicePart(state.subjectId, "INSTRUMENT");
  /**
   * 各平台的乐器声部id不统一，为了兼容处理老的数据，加上乐器code码，此码唯一
   * 获取指法code
   */
  // const code = state.isConcert ? matchVoicePart(state.trackId, "CONCERT") : matchVoicePart(state.musicalCodeId, "SINGLE");
  const code = matchVoicePart(state.trackId, "CONCERT")
  state.fingeringInfo = subjectFingering(code);
  console.log("🚀 ~ state.fingeringInfo:", code, state.fingeringInfo, state.trackId, state.track);
  state.musicalCodeId = state.fingeringInfo?.id || 0
  state.musicalCode = musicalInstrumentCodeInfo.find(item => item.id === state.musicalCodeId)?.code || state.trackId
    ; (window as any).DYSubjectId = state.musicalCodeId
    // 开启自定义每行显示的小节数
    ; (window as any).customSectionAmount = true
    // 标识是课堂乐器，用于移调时进行区分处理
    ; (window as any).DYProjectName = 'musicScore';
  // 如果切换的声轨没有指法，择指法开关置灰并且不可点击
  if (!state.fingeringInfo.name && state.setting.displayFingering) {
    state.setting.displayFingering = false
  }

  // 如果是PC端,放大曲谱
  state.platform = query.platform?.toLocaleUpperCase() || "";
  if (state.platform === IPlatform.PC || state.systemType === 'web') {
    // pc端，谱面默认初始大小设置为1.5
    if (state.platform === IPlatform.PC) {
      state.zoom = 1.5;
    }
    if (query.zoom <= 1) {
      state.zoom = query.zoom ? Number(query.zoom) : state.zoom;
    } else {
      state.zoom = localStorage.getItem('scoreZoom') ? Number(localStorage.getItem('scoreZoom')) : state.zoom
    }
  }
  if (storeData.isApp && !state.isPreView) {
    state.zoom = localStorage.getItem('scoreZoom') ? Number(localStorage.getItem('scoreZoom')) : state.zoom
  }
  // 详情预览页面，曲谱比例
  if (state.isPreView) {
    state.zoom = query.zoom ? Number(query.zoom) : state.zoom;
  }
  // 评测报告取默认谱面大小
  if (state.isEvaluatReport) {
    state.zoom = 0.8
  }
  // 非APP打开的页面，禁止评测
  if (!storeData.isApp) {
    // state.enableEvaluation = false;
  }
  /**
   * 默认渲染什么谱面类型 & 能否转谱逻辑
   * 渲染类型：首先取url参数musicRenderType，没有该参数则取musicalInstruments字段匹配的当前分轨的defaultScore，没有匹配到则取默认值（'firstTone'）
   * 能否转谱：先取isConvertibleScore字段，如果isConvertibleScore为true，则取musicalInstruments字段匹配的当前分轨的transferFlag，都为true则可以转谱
   * 
   */
  // let pitchTrack = null
  // if (state.isConcert) {
  //   musicalCode = musicalInstrumentCodeInfo.find((item: any) => item.id === state.musicalCodeId)?.code
  //   pitchTrack = data.musicalInstruments?.find((item: any) => item.code?.split(',')[0] === musicalCode)
  // } else {
  //   pitchTrack = data.musicalInstruments?.find((item: any) => item.code?.split(',')[0] === musicalCode)
  // }
  let musicalRenderType = ''
  // if (pitchTrack?.defaultScore) {
  //   musicalRenderType = pitchTrack?.defaultScore === 'STAVE' ? 'staff' : pitchTrack?.defaultScore === 'JIAN' ? 'fixedTone' : pitchTrack?.defaultScore === 'FIRST' ? 'firstTone' : ''
  // }
  // state.musicRenderType = query.musicRenderType || musicalRenderType || EnumMusicRenderType.firstTone;
  /**
   * 2024.7.30，使用新的字段
   * 谱面类型，scoreType
   * STAVE("五线谱"),JIAN("固定调"),FIRST("首调"),
   */
  musicalRenderType = data.scoreType === 'STAVE' ? 'staff' : data.scoreType === 'JIAN' ? 'fixedTone' : data.scoreType === 'FIRST' ? '' : 'firstTone';
  if (!state.isEvaluatReport) {
    state.musicRenderType = query.musicRenderType || musicalRenderType || EnumMusicRenderType.firstTone;  
  }
  state.musicRenderType = query.musicRenderType || musicalRenderType || EnumMusicRenderType.firstTone;  
  /**
   * TODO:摇篮曲特殊处理
   */
  if (['1788501975122489346', '1788502467554750466', '1789839575249596417'].includes(state.cbsExamSongId)) {
    if (state.musicRenderType === 'fixedTone') {
      state.musicRenderType = EnumMusicRenderType.firstTone;
    }
  }
  /**
   * 2024.7.30，使用新的字段
   * 能否转谱，isConvertibleScore
   * true：能转谱,false：不能转谱
   * 额外的逻辑：后台设置不能转谱时，如果默认谱面不是五线谱，需要显示转谱按钮，但是只能转首调和固定调
   */  
  // state.enableNotation = pitchTrack ? data.isConvertibleScore && pitchTrack.transferFlag : data.isConvertibleScore
  state.enableNotation = data.isConvertibleScore
  state.specialShowNotation = !data.isConvertibleScore && data.scoreType !== 'STAVE';
  console.log("state对象", state);
  // 评测基准频率
  state.baseFrequency = data.evaluationFrequency ? data.evaluationFrequency.split(",")[0] : 440
  state.baseFrequency = Number(state.baseFrequency)
  // 用户上次的频率和基准频率误差超过10，则重置
  if (Math.abs(state.setting.frequency - state.baseFrequency) > 10) {
    state.setting.frequency = state.baseFrequency >= 0 ? state.baseFrequency : 440
  } else {
    state.setting.frequency = state.setting.frequency || state.baseFrequency
  }
  state.playBtnDirection = query.imagePos === 'left' ? 'left' : 'right';
  state.isAttendClass = (query.imagePos === 'left' || query.imagePos === 'right') ? true : false;
};

// 多分轨合并显示标示
const setCustom = (trackNum?: number) => {
  if (trackNum || state.extConfigJson.multitrack) {
    state.multitrack = trackNum || 0
    setGlobalData("multitrack", trackNum || state.extConfigJson.multitrack);
  }
};

/** 跟练模式播放节拍器（叮咚） */
export const followBeatPaly = () => {
  let metroTimer: any = null;
  if (!followData.start) {
    clearTimeout(metroTimer)
    metroTimer = null
    return;
  }
  const time = state.measureTime * 1000 / metronomeData.totalNumerator / state.basePlayRate;
  requestAnimationFrame(() => {
    const endTime = Date.now();
    if (endTime - state.beatStartTime < time) {
      followBeatPaly();
    } else {
      // metroTimer = setTimeout(() => {
      //   metronomeData.metro?.simulatePlayAudio()
      //   startTime = Date.now();
      //   followBeatPaly();
      // }, time);
      metronomeData.metro?.simulatePlayAudio()
      state.beatStartTime = Date.now();
      followBeatPaly();
    }
  });
};

// 音符添加bbox
export const addNoteBBox = (list: any[]) => {
  const musicContainer = document.getElementById("musicAndSelection")?.getBoundingClientRect() || {
    x: 0,
    y: 0,
  };
  const parentLeft = musicContainer.x || 0;
  let voicesBBox: any = null;
  for (let i = 0; i < list.length; i++) {
    const note = list[i];
    const { svgElement, multipleRestMeasures, totalMultipleRestMeasures, stave } = note;
    /**
     * 兼容合并休止小节没有音符的情况，将合并的小节宽度等分，音符位置就在等分的位置
     */
    let bbox: any = null;
    if (svgElement?.attrs.id) {
      // @ts-ignore
      bbox = document.getElementById(`vf-${svgElement?.attrs?.id}`)?.getBBox();
      const noteBbox = document.getElementById(`vf-${svgElement?.attrs?.id}`)?.getBoundingClientRect?.() || { x: 0, width: 0 };
      bbox = {
        left: noteBbox.x - parentLeft - noteBbox.width / 4, // 用于简谱模式，跳动音符时，设置光标的位置（五线谱：osmd自动设置光标位置，简谱：需要手动设置光标位置）
        x: bbox?.x * state.zoom,
        y: bbox?.y * state.zoom,
        width: bbox?.width * state.zoom,
        height: bbox?.height * state.zoom,
      }
    } else {
      // @ts-ignore
      let currentVoicesBBox: any = document.getElementById(`${stave?.attrs?.id}`)?.nextSibling?.getBBox();
      const svgBodyBBox: any = document.getElementById('musicAndSelection')?.getBoundingClientRect();
      if (!currentVoicesBBox && multipleRestMeasures <= totalMultipleRestMeasures) {
        currentVoicesBBox = voicesBBox;
      }
      let nextIndex = i + 1;
      while (!list[nextIndex]?.id && nextIndex < list.length) {
        nextIndex += 1;
      }
      // 休止小节的开头和下一个音符之间的间距
      let multipleWidth: any = currentVoicesBBox?.width * state.zoom;
      if (list[nextIndex]?.id) {
        // @ts-ignore
        multipleWidth = document.getElementById(`${list[nextIndex]?.stave?.attrs?.id}`)?.getBBox()?.x * state.zoom - currentVoicesBBox?.x * state.zoom;
      }
      const ratioWidth = multipleWidth / totalMultipleRestMeasures || 0;
      bbox = currentVoicesBBox ? {
        bottom: currentVoicesBBox.bottom,
        height: 30,
        left: currentVoicesBBox.x * state.zoom + ratioWidth * (multipleRestMeasures - 1),
        right: currentVoicesBBox.y,
        top: currentVoicesBBox.top,
        width: 1,
        x: currentVoicesBBox.x * state.zoom + ratioWidth * (multipleRestMeasures - 1),
        y: currentVoicesBBox.y,
        svgBodyLeft: svgBodyBBox?.x,
      } : null;
      voicesBBox = currentVoicesBBox;
    }
    note.bbox = bbox;
  }
}

// 给歌词和音符添加动态颜色
let prevActiveNoteIndex = -1 // 上一个激活的
export const fillWordColor = () => {
  // console.log('当前音符',state.activeNoteIndex,prevActiveNoteIndex)
  if(prevActiveNoteIndex !== -1) {
    const prevActiveNoteId = state.times[prevActiveNoteIndex]?.svgElement?.attrs?.id
    const svgEl = document.getElementById(`vf-${prevActiveNoteId}`)
    const stemEl = document.getElementById(`vf-${prevActiveNoteId}-stem`)
    const stemLine = document.getElementById(`vf-${prevActiveNoteId}-lines`)
    svgEl?.classList.remove('noteActive')
    stemEl?.classList.remove('noteActive')
    // stemLine?.classList.remove('noteActive')
    svgEl?.parentElement?.classList.remove('voiceActive')
    const si = state.times[prevActiveNoteIndex].si || 0;
    svgEl?.parentElement?.querySelectorAll('rect')?.forEach((item: any) => {
      item?.classList.remove('rectActive');
    })
    // svgEl?.parentElement?.querySelectorAll('rect')?.[si]?.classList.remove('rectActive');
  }
  const activeNoteId = state.times[state.activeNoteIndex]?.svgElement?.attrs?.id
  const svgEl = document.getElementById(`vf-${activeNoteId}`)
  const stemEl = document.getElementById(`vf-${activeNoteId}-stem`)
  const stemLine = document.getElementById(`vf-${activeNoteId}-lines`)
  svgEl?.classList.add('noteActive')
  stemEl?.classList.add('noteActive')
  // stemLine?.classList.add('noteActive')
  // 如果是简谱、固定调，需要把音符后面跟着的"-"也添加颜色
  if (state.musicRenderType === EnumMusicRenderType.firstTone || state.musicRenderType === EnumMusicRenderType.fixedTone) {
    // const parentVoice = svgEl?.parentElement;
    // 简谱模式下，二分音符和全音符才显示音符右侧的"-"
    if (state.times[state.activeNoteIndex].noteElement?.length?.realValue >= 0.5) {
      // 如果是二分音符，只亮该音符后面那个"-"，本小节其它的"-"不亮
      if (state.times[state.activeNoteIndex].noteElement?.length?.realValue === 0.5) {
        const si = state.times[state.activeNoteIndex].si || 0;
        const halfNotes = state.times[state.activeNoteIndex].measures.filter((item: any) => item?.noteElement?.length?.realValue === 0.5) || [];
        const sIdx = halfNotes?.findIndex((item: any) => item?.noteElement === state.times[state.activeNoteIndex]?.noteElement);
        const filterRects = svgEl?.parentElement?.querySelectorAll('rect')?.length ? Array.from(svgEl?.parentElement?.querySelectorAll('rect')).filter(item => item.parentElement === svgEl?.parentElement) : [];
        filterRects?.[sIdx]?.classList.add('rectActive');
      } else {
        svgEl?.parentElement?.classList.add('voiceActive');
      }
    }
  }
  prevActiveNoteIndex = state.activeNoteIndex

  // 给当前匹配到的歌词添加颜色
  const currentNote = state.times[state.activeNoteIndex];
  const lyrics: SVGAElement[] = Array.from(document.querySelectorAll(".vf-lyric"));
  lyrics.forEach((lyric) => {
    lyric?.classList.remove('lyricActive')
  })
  const currentLyrics: SVGAElement[] = Array.from(document.querySelectorAll(`.lyric${currentNote?.noteId}`));
  currentLyrics.forEach((lyric, index) => {
    const lyricIndex = lyric.getAttribute('lyricIndex');
    // bug：#10942，如果需要反复唱的小节，只有一遍歌词，反复唱的时候，歌词都需要高亮
    const onlyOneLyric = currentNote.measures?.every((item: any) => item?.formatLyricsEntries?.length <= 1);
    if ((index === currentNote.repeatIdx && currentNote.repeatIdx + 1 == lyricIndex) || (currentNote.repeatIdx != index && !onlyOneLyric && currentNote.repeatIdx + 1 == lyricIndex) || (currentNote.repeatIdx > 0 && currentNote.formatLyricsEntries?.length === 1 && onlyOneLyric)) {
      lyric?.classList.add('lyricActive')
    } 
    // bug: #11189，兼容处理需要唱4遍，但是只打了2遍歌词的情况，1、3唱一样的歌词，2、4唱一样的歌词
    if ( currentNote.formatLyricsEntries.length == 2 && currentNote.repeatIdx >= 2 && index === (currentNote.repeatIdx - 2) ) {
      lyric?.classList.add('lyricActive')
    }
    // if ((index === currentNote.repeatIdx && currentNote.repeatIdx + 1 == lyricIndex)) {
    //   lyric?.classList.add('lyricActive')
    // }
  })
}

/** 跳动svgdom */
export const moveSvgDom = (skipNote?: boolean) => {
  /**
   * 计算需要移动的距离
   * 当前选中的音符和第一个音符之间的间距
   */
  if (skipNote) {
    // 点击 清空translateXNum
    smoothAnimationState.translateXNum = 0
    moveTranslateXNum(0)
    // 移动小鸟的位置
    moveSmoothAnimation(0, state.activeNoteIndex, false)
    // 移动谱面当当前音符的位置
    const distance = state.times[state.activeNoteIndex].bbox?.x - state.times[0].bbox?.x
    smoothAnimationState.osdmScrollDom!.scrollTo({
      left: distance,
      behavior: "smooth",
    });
  }
}


// 暂停的时候  恢复一行谱偏移位置信息
watch(
  () => state.playState,
  () => {
    if (state.isSingleLine) {
      // 当在播放中暂停 执行这个方法
      if (!state.playEnd && state.playState === "paused") {
        moveTranslateXNum(0)
        // 因为safari浏览器scrollWidth的值一直变化，scrollLeft + smoothAnimationState.translateXNum 为最大宽度的时候，实际上scrollLeft滚不到最大宽度，所以在下一帧处理滚动，能滚动到最大滚动位置
        requestAnimationFrame(() => {
          const scrollLeft = smoothAnimationState.osdmScrollDom!.scrollLeft
          smoothAnimationState.osdmScrollDom!.scrollLeft = scrollLeft + smoothAnimationState.translateXNum
          smoothAnimationState.translateXNum = 0
        });
      }
    }
  }
)


/* 根据当前的小节获取前面有多少需要去除的合并小节 */
function getNeedReduceMultipleRestNum(currMeasureIndex: number) {
  let needReduceMultipleRestNum = 0;
  for (let noteIndex = 0; noteIndex < state.times.length; noteIndex++) {
    const note = state.times[noteIndex];
    if (note.MeasureNumberXML > currMeasureIndex) {
      break;
    }
    if (note.multipleRestMeasures && note.multipleRestMeasures > 1) {
      needReduceMultipleRestNum += 1;
    }
  }
  return needReduceMultipleRestNum
}

watch(
  () => state.activeMeasureIndex,
  () => {
    // 监听音符小节的变化，取对应的小节速度图片
    const currentNote = state.times[state.activeNoteIndex]
    state.speedIcon = unitImgs[currentNote.speedBeatUnit]
    
    // 需要减去的合并小节数
    // const needReduceMultipleRestNum = getNeedReduceMultipleRestNum(state.activeMeasureIndex)
    // const matchMeasureNum = state.activeMeasureIndex - needReduceMultipleRestNum - 1
    // console.log('选中的小节',matchMeasureNum,'需要减去的小节',needReduceMultipleRestNum,'当前的小节',state.activeMeasureIndex)
    state.vfmeasures.forEach((item: any, idx: number) => {
      const dataNum = item.getAttribute('data-num')  // 值可能为字符串类型的undefined
      let measureNum = (dataNum && dataNum !== "undefined") ? Number(dataNum) : -1;
      let nextDataNum = state.vfmeasures[idx+1]?.getAttribute('data-num')
      // 当有换行小节，下个小节的nextDataNum是undefined，所以这里需要往后找一个
      if(!(nextDataNum && nextDataNum !== "undefined")){
        nextDataNum = state.vfmeasures[idx + 2]?.getAttribute('data-num')
      }
      const nextMeasureNum = Number(nextDataNum)
      // 当measureNum 为undefined 则是下一个小节的换行小节，所以这里等于下一个小节
      if(measureNum === -1) {
        measureNum = nextMeasureNum
      }
      if (measureNum >= 0 && (measureNum === state.activeMeasureIndex || (measureNum < state.activeMeasureIndex && nextMeasureNum > state.activeMeasureIndex))) {
        item.querySelector('.vf-custom-bg')?.setAttribute("fill", state.isSimplePage ? "rgba(45, 199, 170, 0.3)" : "rgba(45, 199, 170, 0.2)")
        // 预备小节
        if(state.sectionFirst && measureNum === state.sectionFirst.MeasureNumberXML && state.section.length === 2){
          item?.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(255, 193, 48, 0.15)")
        }
        // 如果是选段，超出选段时，不添加背景色
        if (state.section.length === 2 && state.activeMeasureIndex > state.section[1].MeasureNumberXML) {
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", "transparent")
        }
      } else {
        // 有选段只清除选段处的
        if (state.section.length === 2) {
          // 把中间置灰
          let leftMeasureNumberXML = state.section[0].MeasureNumberXML
          let rightMeasureNumberXML = state.section[1].MeasureNumberXML
          if (leftMeasureNumberXML > rightMeasureNumberXML) {
            leftMeasureNumberXML = state.section[1].MeasureNumberXML
            rightMeasureNumberXML = state.section[0].MeasureNumberXML
          }
          if(measureNum >= leftMeasureNumberXML && measureNum <= rightMeasureNumberXML){
            item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(45, 199, 170, 0.15)")
          }
          // 预备小节
          if(state.sectionFirst && measureNum === state.sectionFirst.MeasureNumberXML){
            item?.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(255, 193, 48, 0.15)")
          }
        } else {
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", "transparent")
        }
      }
    })
  }
);



watch(
  () => state.section,
  () => {
    if (state.section.length === 2) {
      // 把前面和 后面置灰
      let leftMeasureNumberXML = state.section[0].MeasureNumberXML
      let rightMeasureNumberXML = state.section[1].MeasureNumberXML
      if (leftMeasureNumberXML > rightMeasureNumberXML) {
        leftMeasureNumberXML = state.section[1].MeasureNumberXML
        rightMeasureNumberXML = state.section[0].MeasureNumberXML
      }
      state.vfmeasures.forEach((item: any, idx: number) => {
        const dataNum = item.getAttribute('data-num')  // 值可能为字符串类型的undefined
        let measureNum = (dataNum && dataNum !== "undefined") ? Number(dataNum) : -1;
        let nextDataNum = state.vfmeasures[idx+1]?.getAttribute('data-num')
        // 当有换行小节，下个小节的nextDataNum是undefined，所以这里需要往后找一个
        if(!(nextDataNum && nextDataNum !== "undefined")){
          nextDataNum = state.vfmeasures[idx + 2]?.getAttribute('data-num')
        }
        const nextMeasureNum = Number(nextDataNum)
        // 当measureNum 为undefined 则是下一个小节的换行小节，所以这里等于下一个小节
        if(measureNum === -1) {
          measureNum = nextMeasureNum
        }
        // 小于选中置灰
        if (measureNum < leftMeasureNumberXML) {
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", 'transparent')
        }
        // 大于选中置灰
        if(measureNum > rightMeasureNumberXML){
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", "transparent")
        }
        if (measureNum >= leftMeasureNumberXML && measureNum <= rightMeasureNumberXML) {
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(45, 199, 170, 0.15)")
        }
        // 预备小节
        if(state.sectionFirst && measureNum === state.sectionFirst.MeasureNumberXML){
          item?.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(255, 193, 48, 0.15)")
        }        
      })
    }else{
      // 恢复选段前
      state.vfmeasures.forEach((item: any, idx: number) => {
        const dataNum = item.getAttribute('data-num')  // 值可能为字符串类型的undefined
        let measureNum = (dataNum && dataNum !== "undefined") ? Number(dataNum) : -1;
        let nextDataNum = state.vfmeasures[idx+1]?.getAttribute('data-num')
        // 当有换行小节，下个小节的nextDataNum是undefined，所以这里需要往后找一个
        if(!(nextDataNum && nextDataNum !== "undefined")){
          nextDataNum = state.vfmeasures[idx + 2]?.getAttribute('data-num')
        }
        const nextMeasureNum = Number(nextDataNum)
        // 当measureNum 为undefined 则是下一个小节的换行小节，所以这里等于下一个小节
        if(measureNum === -1) {
          measureNum = nextMeasureNum
        }
        if (measureNum >= 0 && (measureNum === state.activeMeasureIndex || (measureNum < state.activeMeasureIndex && nextMeasureNum > state.activeMeasureIndex)) ) {
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(45, 199, 170, 0.15)")
        } else {
          item.querySelector('.vf-custom-bg')?.setAttribute("fill", "transparent")
        }
      })
    }
  }
)

// 后台编辑谱面模式，切换谱面时，如果有操作没有保存，需要给出提示
export const checkMoveNoSave = async () => {
  return new Promise((resolve, reject) => {
    if (query.isMove) {
      if (moveData.open && undoData.undoList.length) {
        showConfirmDialog({
          className: "noSaveModal",
          title: "温馨提示",
          message: "您有新的修改还未保存，切换谱面后本次编辑的内容将不会保存",
        }).then(() => {
          moveData.open = false
          resolve(true)
        }).catch(() => {
          return;
        });
      } else {
        moveData.open = false
        undoData.undoList = []
        resolve(true)
      }
    } else {
      resolve(true)
    }
  });


}


/** 刷新谱面 */
export const refreshMusicSvg = () => {
  moveData.noteCoords = []
  moveData.modelList = []
  clearSelection();
  resetBaseRate();
  state.activeMeasureIndex = -1;
  metronomeData.activeIndex = null;
  if (query.workRecord || query.evaluatingRecord) {
    state.workSectionNeedReset = true;
  }
  // 销毁旋律线
  destroySmoothAnimation()
  musicScoreRef.value?.refreshMusicScore()
}

// 指法改变显示的时候 osdmScrollDomWith 宽度会变化 所以指法改变的时候这个宽度重新计算
watch(
  () => state.setting.displayFingering,
  () => {
    // 有字符 并且是竖向指法 并且是一行谱
    if(state.fingeringInfo?.name && state.fingeringInfo.direction === "vertical" && state.isSingleLine){
      nextTick(() => {
        calcClientWidth()
      })
    }
    // 如果有指法，并且是竖向指法时，切换指法时，谱面宽度变化，需要重新渲染谱面
    if (state.fingeringInfo?.name && state.fingeringInfo.direction === "vertical" && !state.isSingleLine) {
      headTopData.settingMode = false;
      refreshMusicSvg();
    }
  }
)

// 完成拖动弹窗引导页
export const handleGuide = async () => {
    const guideInfoStore = localStorage.getItem('guideInfo') ? JSON.parse(localStorage.getItem('guideInfo')) : {};
    guideInfoStore.teacherDrag = true;
    try {
      localStorage.setItem("guideInfo", JSON.stringify(guideInfoStore));
      state.guideInfo = guideInfoStore
    } catch (e) {
      console.log(e);
    }
};

// 手动设置音符指针位置
export const resetCursorPosition = () => {
  nextTick(() => {
    if (metronomeData.cursorMode === 1) {
      const currentActives: HTMLElement[] = Array.from(document.querySelectorAll(".dotActive"));
      currentActives.forEach((currentActive) => {
        currentActive?.classList.remove("dotActive");
      })
      // const currentDot = document.querySelector(`.noteIndex_${state.activeNoteIndex}`)
      const noteId = state.times[state.activeNoteIndex].id;
      const domId = "vf" + noteId;
      const currentDot = document.querySelector(`[data-vf=${domId}]`)?.parentElement
      currentDot?.classList.add('dotActive')
    } else {
      const currentActives: HTMLElement[] = Array.from(document.querySelectorAll(".dotActive"));
      currentActives.forEach((currentActive) => {
        currentActive?.classList.remove("dotActive");
      })
    }
    // metronomeData?.metro?.findMetronomePosition(0);
  })
}


watch(
  () => state.activeNoteIndex,
  () => {
    resetCursorPosition();
  }
);