/** * 播放过程中必要的context,效果与state一致仅用于操作,但是包含方法,好处是可以在任意地方调用,操作播放状态 */ import { Toast } from 'vant' import { reactive, watchEffect } from 'vue' import store from 'store' import { constant, throttle, debounce } from 'lodash' import { getActtiveNoteByTimes, getDuration, getIndex, getNoteBySlursStart, setStepIndex, getSlursNote, getVoicePartInfo, } from './helpers' import SectionHint from '/src/helpers/section-hint' import { browser, formatTime, getPlatform, getRequestHostname } from '/src/helpers/utils' import request from '/src/helpers/request' // import * as Tone from 'tone' import router from '/src/router' import appState from '/src/state' import detailState from './state' import SettingState from '/src/pages/detail/setting-state' // import fixtimeRela from '/src/constant/fixtime' import TickPlayer, { getTickTime } from '/src/components/tick/player' // import { tickByNumerator } from '/src/components/tick/constant' import { postMessage, listenerMessage, promisefiyPostMessage } from '/src/helpers/native-message' import EventEmitter from 'eventemitter3' import { useClientType, useOriginSearch } from '/src/subpages/colexiu/uses' import { evaluatPlayerStop } from '/src/subpages/colexiu/buttons/evaluating' export const event = new EventEmitter() const browserInfo = browser() const initBehaviorId = '' + new Date().valueOf() const getLinkId = (): string => { const seearchid = useOriginSearch().id as string return location.hash.split('?')[0].split('/').pop() || seearchid || '' } export const getFixtimeRelaVal = () => { const route: any = router.currentRoute.value const linkId = location.hash.split('?')[0].split('/').pop() return 0 //(fixtimeRela as any)[(route.params.id || linkId)] || 0 } export const getFixTime = (speed: number) => { const duration: any = getDuration(state.osmd) let numerator = duration.numerator || 0 let denominator = duration.denominator || 4 if (detailState.repeatedBeats) { // 音频制作问题仅2拍不重复 numerator = numerator === 2 ? 4 : numerator } // 音频制作问题仅2拍不重复 // numerator = numerator === 2 ? 4 : numerator const time = !detailState.needTick && !detailState.skipTick ? ((denominator * 60) / speed / denominator) * numerator : 0//5 + getFixtimeRelaVal() return time } let prevIndex: number = 0 export type IPlayState = 'init' | 'play' | 'pause' | 'suspend' export type IMode = 'background' | 'music' export type ISonges = { background?: string music?: string } const state = reactive({ /** 曲目信息 */ songs: {} as ISonges, /** 播放状态 */ playState: 'init' as IPlayState, /** 当前播放背景 */ sectionHint: new SectionHint(), /** 音频播放器实例 */ audiosInstance: null as any, /** 原声伴奏 */ mode: 'music' as IMode, /** 是否是第一次播放 */ isFirstPlay: true, metro: null as any, metroing: false, /** 时长 */ duration: '0:00', durationNum: 0, /** 当前时间 */ currentTime: '0:00', currentTimeNum: 0, loading: false, /** 速度 */ speed: 90, browser: browser(), /** 调速是否可见 */ speedShow: false, /** 播放进度进度是否可见 */ progressShow: false, touched: false, /** opendisplaymusicdisplay 实例 */ osmd: null as any, /** 节拍器实例 */ tickPlayer: null as any, /** 评测状态 */ evaluatingStatus: false, /** 评测提示 */ evaluatingTips: false, clickTime: 0, evaluatingFixTime: 0, /** 摄像头状态 */ cameraStatus: false, /** 录制状态 */ captureStatus: false, ticking: false, /** 第几分轨 */ partIndex: 0, /** 当前音符index */ activeIndex: 0 }) const syncStepIndex = (i: number) => { // console.log("🚀 ~ i", i) if (state.osmd.hidden !== false) { state.osmd.cursor.show() } prevIndex = i setStepIndex(state.osmd, i) refreshIndex(detailState.times[i]?.time) } watchEffect(() => { detailState.maskStatus = state.playState === 'play' }) const syncPlayState = async () => { if (detailState.activeDetail.isAppPlay) { const cloudGetMediaStatus = await promisefiyPostMessage({ api: 'cloudGetMediaStatus', }) const status = cloudGetMediaStatus?.content.status state.playState = status } else { state.playState = state.audiosInstance.getStatus() } } export default state export const setCurrentTime = (time: number) => { console.log('setCurrentTime', time) const fixt = time // - 5 // console.log('set', fixt) detailState.fixedKey = 0 state.currentTimeNum = fixt state.currentTime = formatTime(fixt) if (detailState.activeDetail.isAppPlay) { promisefiyPostMessage({ api: 'cloudSetCurrentTime', content: { currentTime: time * 1000, songID: detailState.activeDetail.examSongId, }, }) } else { state.audiosInstance.setCurrentTime(fixt) } refreshView() syncPlayState() const index = getIndex(detailState.times, state.currentTimeNum) syncStepIndex(index) // changeMode(state.mode) } export const onTimeChanged = (num: number) => { const time = Math.min(num, state.durationNum) const index = getIndex(detailState.times, state.currentTimeNum) setCurrentTime(time) syncStepIndex(index) // console.log('onTimeChanged', time) // changeMode(state.mode) } /** 获取当前MidiId */ export const getActiveMidiId = () => { return state.osmd?.sheet?.instruments?.[0]?.subInstruments?.[0]?.midiInstrumentID ?? 0 } /** * 修改原音或伴奏 * @param val IMode */ export const changeMode = async (val: IMode) => { const cm: IMode = val === 'background' ? 'music' : 'background' console.log(!state.songs[val], val, cm) if (detailState.activeDetail.isAppPlay) { const data = new Map() for (const name of detailState.partListNames) { data.set(name, 60) } for (const name of getVoicePartInfo().partListNames) { data.set(name, cm === 'background' ? 100 : 0) } promisefiyPostMessage({ api: 'cloudVolume', content: { activeMidiId: getActiveMidiId(), activeMidiVolume: cm === 'background' ? 100 : 0, parts: Array.from(data.keys()).map((item) => ({ name: item, volume: data.get(item), })), }, }) // state.mode = val // promisefiyPostMessage({ // api: 'cloudSwitch', // content: { // songID: detailState.activeDetail.examSongId, // parts: cm === 'background' ? [] : getVoicePartInfo().partListNames, // } // }) } // if (!state.songs[val]) { // return // } state.mode = val state.audiosInstance?.setMute(true, state.songs[cm]) state.audiosInstance?.setMute(false, state.songs[val]) } export const changeAllMode = () => { if (detailState.activeDetail?.isAppPlay) { const data = new Map() for (const name of detailState.partListNames) { data.set(name, 1) } promisefiyPostMessage({ api: 'cloudVolume', content: { activeMidiId: getActiveMidiId(), activeMidiVolume: 100, parts: Array.from(data.keys()).map((item) => ({ name: item, volume: data.get(item), })), }, }) } else { state.mode = 'background' state.audiosInstance?.setMute(true) } } export const changeSpeed = (speed: number, isSave: boolean = true) => { // console.log(speed) // const route: any = router.currentRoute.value const speeds = store.get('speeds') || {} if (isSave){ speeds[getLinkId()] = speed store.set('speeds', speeds) } state.speed = speed if (!detailState.activeDetail) return state.audiosInstance?.setSpeed(speed / detailState.baseSpeed) promisefiyPostMessage({ api: 'cloudChangeSpeed', content: { speed: speed, originalSpeed: detailState.activeDetail.originalSpeed, songID: detailState.activeDetail.examSongId, }, }) // changeMode(state.mode) if (state.playState === 'play') { syncStepIndex(getIndex(detailState.times, state.currentTimeNum)) } } let nextLook: boolean = false let syncTimed: boolean = false export const resetCursor = () => { if (state.osmd) { if(state.osmd.product){ state.osmd.cursor.setPosition({...detailState.times[0].cursorBox}) } else { state.osmd.cursor.reset() } state.osmd.cursor.hide() detailState.fixedKey = 0 } } export const refreshIndexBase = (index: number) => { if (index < 0) return const { osmd }: any = state if (osmd) { if (detailState.times[index]) { if (!detailState.sectionStatus) { state.sectionHint.show() } if (detailState.times[index] && detailState.times[index].noteElement) { state.sectionHint.showForElement(detailState.times[index]) } if (!osmd.product){ if (osmd.cursor.hidden !== false) { osmd.cursor.reset() osmd.cursor.show() detailState.fixedKey = 0 } } if (prevIndex !== index) { setStepIndex(state.osmd, detailState.times[index].i, prevIndex) prevIndex = index } detailState.fixedKey = detailState.times[index].realKey detailState.activeNote = detailState.times[index] } } } export const refreshIndex = (ctime?: number) => { const { osmd }: any = state if (osmd && (ctime || state.audiosInstance.audio)) { const currentTimeNum = ctime || (state.audiosInstance.audio as HTMLAudioElement).currentTime const index = getIndex(detailState.times, currentTimeNum) state.activeIndex = index removeRepateBackground(index) // console.log(currentTimeNum, index, detailState.times[detailState.times.length - 1]?.endtime) const lastNote = detailState.times[detailState.times.length - 1] const endtime = lastNote?.sourceEndTime || lastNote?.endtime if (currentTimeNum > endtime) { state.osmd.cursor.hide() state.sectionHint.destroy() } else { if (detailState.times[index]) { refreshIndexBase(index) } } } } /** 重复中移除重复的背景 */ export const removeRepateBackground = (index: number) => { if (state.evaluatingStatus && index) { const activeNote = detailState.times[index] const note = detailState.times[index + 1] || activeNote const number = note?.noteElement?.sourceMeasure?.measureListIndex // 0 比较特殊重置等操作会导致误操作所以跳过第0个 if (note && detailState.evaluatings[number] && index > 0) { detailState.evaluatings = { ...detailState.evaluatings, [number]: undefined, } } } } /** * 刷新播放的状态 * @param ctime 当前时间 * @returns */ export const refreshPlayer = async (ctime?: number) => { // if (!state.durationNum) { // loadedmetadata() // } // const status: IPlayState = state.audiosInstance.getStatus() const { osmd }: any = state if (osmd && (ctime || state.audiosInstance.audio)) { // state.playState = status const currentTimeNum = ctime || (state.audiosInstance.audio as HTMLAudioElement).currentTime // console.log('refreshPlayer', currentTimeNum) const mintime = 0 //detailState.times[0].time if (currentTimeNum + 1 < mintime) { setCurrentTime(mintime) return } else { syncTimed = false } const nextTime = () => { if (detailState.sectionStatus && detailState.section.length === 2) { // console.log("state.currentTimeNum >= detailState.section[0].time", state.currentTimeNum, detailState.section[0].time) if (currentTimeNum >= detailState.section[0].time) { detailState.sectionFlash = false } const nextNote = detailState.times[detailState.section[1].i + 1] const time = nextNote ? nextNote.halfTone === 0 ? detailState.section[1].endtime : nextNote.time : state.durationNum // console.table({ // currentTimeNum, // isNext: (browserInfo.xiaomi ? 0.2 : 0.08) >= time, // time, // nextNote // }) return currentTimeNum + (browserInfo.xiaomi ? 0.2 : 0.08) >= time } return false } const isNext = nextTime() if (isNext) { // console.log("isNext", detailState.section[1], detailState.section[1].endtime, currentTimeNum) state.audiosInstance.setMute(true) state.osmd.cursor.hide() // resetPlayStatus() if (detailState.activeDetail?.isAppPlay) { pause() } else { await state.audiosInstance.pause() } // setCurrentTime(detailState.section[0].sourceStartTime || detailState.section[0].time) setSectionModeCurrentTime() clearAccelerateRefreshPlayer() setTimeout(() => setPlayState(), 1000) state.loading = false return } // 当MIDI进度超过最后一个音符时间触发结束事件 if(detailState.activeDetail?.isAppPlay && state.durationNum + 3 < currentTimeNum) { if (state.evaluatingStatus) { pause() event.emit('ended', new Event('ended')) } else { if (SettingState.sett.loop) { await pause() await setCurrentTime(0) await play() } else { pause() } } } } } /** * 重置播放状态 * @param notStop 是否停止播放 */ export const resetPlayStatus = async (notStop?: boolean) => { try { prevIndex = 0 state.osmd.cursor.reset() state.osmd.cursor.hide() detailState.fixedKey = 0 detailState.sectionFlash = false if (state.sectionHint) { state.sectionHint.destroy() } if (!notStop) { if (detailState.activeDetail.isAppPlay) { await promisefiyPostMessage({ api: 'cloudSuspend', content: { songID: detailState.activeDetail.examSongId, }, }) endCapture() } else { console.log('resetPlayStatus调用暂停') state.audiosInstance.pause() } } syncPlayState() } catch (error) { console.log('resetPlayStatus错误', error) } } export const play = async () => { if (state.isFirstPlay) { resetPlayStatus() detailState.fixedKey = 0 } if (detailState.activeDetail.isAppPlay) { await syncPlayState() promisefiyPostMessage({ api: 'cloudSuspend', content: { songID: detailState.activeDetail.examSongId, }, }) startCapture() } else { state.playState = state.audiosInstance.getStatus() clearAccelerateRefreshPlayer() accelerateRefreshPlayer() startCapture() } } const setDelayTime = async (time: number) => { return new Promise((resolve) => { setTimeout(() => { resolve(time) }, time) }) } /** * 暂停播放 */ export const pause = async () => { if (detailState.sectionStatus) { state.osmd.cursor.hide() } if (detailState.activeDetail.isAppPlay) { await syncPlayState() await promisefiyPostMessage({ api: 'cloudSuspend', }) await setDelayTime(200) endCapture() } else { state.playState = state.audiosInstance.getStatus() clearAccelerateRefreshPlayer() state.audiosInstance.pause() setTimeout(() => { // 确保播放完毕,延迟200ms endCapture() }, 200) } } export const waiting = () => { state.loading = true } export const playing = () => { state.loading = false } export const ended = debounce(async (evt: Event) => { resetPlayStatus() detailState.fixedKey = 0 if (!state.evaluatingStatus) { refreshPlayer(0) if (SettingState.sett.loop) { await setPlayState() } } setCurrentTime(0) event.emit('ended', evt) }, 300, { 'leading': true, 'trailing': false }) let timer: any = null let now = 0 let nowTime = 0 let prevTime: number = 0 const accelerateRefreshPlayer = () => { if (timer || !state.audiosInstance) { return } timer = setInterval(() => { requestAnimationFrame(() => { refreshPlayer() if (state.audiosInstance.getStatus() === 'play') { refreshIndex() } }) }, 16.7) } const clearAccelerateRefreshPlayer = () => { clearInterval(timer) timer = null now = 0 } /** * 选择段落 */ export const sectionChange = () => { detailState.sectionStatus = !detailState.sectionStatus clearAccelerateRefreshPlayer() resetPlayStatus() if (!detailState.sectionStatus) { setCurrentTime(0) detailState.fixedKey = 0 } if (detailState.sectionStatus && detailState.section.length != 2) { resetCursor() } } export const clearSectionStatus = () => { detailState.section = [] detailState.sectionBoundingBoxs = [] detailState.sectionStatus = false } export const getFirsrNoteByMeasureListIndex = (index: number, tie = true) => { for (const note of detailState.times) { if (note?.noteElement?.sourceMeasure?.measureListIndex === index) { let noteTies: any = null for (const item of note.measures) { if (getSlursNote(item)) { noteTies = getSlursNote(item) } } if (noteTies) { if (noteTies.sourceMeasure?.measureListIndex !== index) { for (const n of detailState.times) { if (n.noteElement.NoteToGraphicalNoteObjectId === noteTies.NoteToGraphicalNoteObjectId) { return n } } } } return note } } return null } export const setSectionModeCurrentTime = () => { console.log(detailState.needTick, 'setSectionModeCurrentTime') if (detailState.needTick) { setCurrentTime(detailState.section[0].sourceStartTime || detailState.section[0].time) } else { const measureListIndex = detailState.section[0].noteElement?.sourceMeasure?.measureListIndex if (measureListIndex > 0) { setCurrentTime(getFirsrNoteByMeasureListIndex(measureListIndex - 1).time) detailState.sectionFlash = true } else { setCurrentTime(0) } } } export const setPlayerView = () => { console.log(detailState.sectionStatus, 'detailState.sectionStatus') console.log(detailState.needTick) if (detailState.sectionStatus) { syncStepIndex(getIndex(detailState.times, state.currentTimeNum)) if (detailState.section.length === 2) { // setCurrentTime(detailState.section[0].sourceStartTime || detailState.section[0].time) setSectionModeCurrentTime() } else { detailState.section = [] detailState.sectionBoundingBoxs = [] detailState.sectionStatus = false Toast.clear() } } } const cloudToggleState = async () => { const cloudGetMediaStatus = await promisefiyPostMessage({ api: 'cloudGetMediaStatus', }) const status = cloudGetMediaStatus?.content.status if (status === 'init') { return } if (status === 'suspend') { await promisefiyPostMessage({ api: 'cloudPlay', content: { songID: detailState.activeDetail.examSongId, startTime: state.currentTimeNum * 1000, originalSpeed: detailState.activeDetail.originalSpeed, speed: state.speed, hertz: 440//SettingState.sett.hertz, }, }) startCapture() } else { await promisefiyPostMessage({ api: 'cloudSuspend', }) endCapture() } const cloudGetMediaStatused = await promisefiyPostMessage({ api: 'cloudGetMediaStatus', }) state.playState = cloudGetMediaStatused?.content.status console.log(cloudGetMediaStatused, 'cloudGetMediaStatused') } export const toggleState = async (delay?: number) => { if (detailState.activeDetail.isAppPlay) { await cloudToggleState() } else { console.log(detailState.activeDetail) console.log('delay', delay) state.isFirstPlay = false setPlayerView() await state.audiosInstance.togglePlay(delay) if (!state.evaluatingStatus) { changeMode(state.mode) } state.playState = state.audiosInstance.getStatus() } } const setActiveKey = (index: number) => { detailState.activeTick = index } const setTickStop = () => { console.log('节拍器结束', new Date().getTime() - state.clickTime) detailState.activeTick = -1 detailState.activeTickRepeat = 1 toggleState(getTickTime(state.speed / detailState.baseSpeed)) } let timeliner: any = -1 export const startIntervalTimeline = (maxTime: number, end: () => void) => { const nowTimeline = new Date().getTime() let currenttTime = 0 const throttleIndex = throttle(() => { if (currenttTime) { refreshView() } }, 1200) const start = () => { requestAnimationFrame(() => { currenttTime = (new Date().getTime() - nowTimeline) / 1000 refreshIndex(currenttTime) if (maxTime && currenttTime >= maxTime) { clearIntervalTimeline() end() } throttleIndex() }) } start() timeliner = setInterval(() => { start() }, 16.7) } export const clearIntervalTimeline = () => { clearInterval(timeliner) } const onTickDestroy = () => { event.emit('tickDestroy') } export const setTick = (stop: () => void, speed?: number) => { // 节拍时间是固定的无需调整 console.log('ticking') const mixStop = () => { stop() event.emit('tickEnd') } if (detailState.needTick) { let { numerator, denominator } = getDuration(state.osmd) if (state.osmd.numerator && state.osmd.denominator){ numerator = state.osmd.numerator denominator = state.osmd.denominator } if (detailState.activeDetail.isAppPlay) { state.ticking = true postMessage( { api: 'cloudMetronome', content: { // 少量情况下需要重复 repeat: numerator === 2 ? 2 : 1, denominator, numerator, }, }, (res) => { state.ticking = false if (res?.content.status === 'finish') { mixStop() } else if (res?.content.status === 'cancel') { event.emit('tickDestroy') } } ) } else { const activeTickRepeat = numerator === 2 ? 2 : 1 detailState.activeTickRepeat = activeTickRepeat console.log('ticking') state.tickPlayer = new TickPlayer(numerator, (speed || state.speed) / 90) state.tickPlayer?.start(numerator, (speed || state.speed) / 90, activeTickRepeat) state.tickPlayer?.event.off('tick', setActiveKey) state.tickPlayer?.event.off('stop', mixStop) state.tickPlayer?.event.off('destroy', onTickDestroy) state.tickPlayer?.event.on('tick', setActiveKey) state.tickPlayer?.event.on('stop', mixStop) state.tickPlayer?.event.on('destroy', onTickDestroy) } } else { mixStop() } } export const setPlayState = async () => { // console.log('click play', detailState.activeTick, state.audiosInstance.getStatus()) if (detailState.activeTick > -1 || state.ticking) { return } await syncPlayState() console.table({ playState: state.playState, currentTime: state.currentTimeNum, }) if (state.playState !== 'pause' && state.playState !== 'suspend') { await toggleState() return } console.log('设置播放') setPlayerView() setTick(setTickStop) } export const stopTick = () => { if (state.tickPlayer) { state.tickPlayer.destroy() } event.emit('stopTick') detailState.activeTickRepeat = 1 detailState.activeTick = -1 } export const windowResize = () => { const index = getIndex(detailState.times, state.currentTimeNum) setTimeout(() => { state.sectionHint?.showForElement(detailState.times[index]?.noteElement) }, 200) } export const loadedmetadata = () => { state.duration = formatTime(state.audiosInstance.duration) state.durationNum = state.audiosInstance.duration } let prevDiff: number = 0 let viewing: boolean = false export const refreshView = () => { let cursorEl : any = undefined let containerEl : any = undefined if (state?.osmd?.product){ cursorEl = state.osmd.cursor.img containerEl = document.querySelector('#colexiu-detail-music-sheet') } const top = Math.max(parseFloat((cursorEl || state.osmd.cursor.cursorElement).style.top), 0) if (Math.abs(prevDiff - top) > 10 && !viewing) { viewing = true setTimeout(() => { viewing = false const scrollElement = containerEl ? containerEl : appState.clintNmae === 'colexiu' ? state.osmd.container.parentElement.parentElement : state.osmd.container.parentElement scrollElement.scrollTo({ top: top, left: 0, behavior: 'smooth', }) prevDiff = top }, 100) } } const updatePlayTime = async (time: number) => { const search = useOriginSearch() const behaviorId = sessionStorage.getItem('behaviorId') || search.behaviorId || initBehaviorId const prefix = getRequestHostname() const clientType = useClientType() if (!state.evaluatingStatus) { // 如果是后台不需要统计时长 if (clientType === 'web') return try { const res = await request.post('/musicPracticeRecord/save', { prefix: prefix, requestType: 'json', data: { musicSheetId: getLinkId(), sysMusicScoreId: getLinkId(), feature: search.feature, playTime: time, deviceType: getPlatform(), behaviorId, }, }) event.emit('updatePlayTimeSuccess', res.data) } catch (error) {} } //课后训练 if (search.lessonTrainingId) { try { console.log(prefix) const res = await request.post('/studentLessonTraining/lessonTrainingRecord', { prefix: prefix, requestType: 'json', data: { materialType: 'SONG', record: { clientType: (clientType as string)?.toLocaleUpperCase(), feature: search.feature, deviceType: browserInfo.android ? 'ANDROID' : browserInfo.isApp ? 'IOS' : 'WEB', behaviorId, playTime: time, musicSheetId: getLinkId(), }, courseScheduleId: search.courseScheduleId, lessonTrainingId: search.lessonTrainingId, materialId: search.materialId, }, }) } catch (error) {} } } export const setAudioInit = () => { state.audiosInstance.event.on('loadedmetadata', loadedmetadata) state.audiosInstance.event.on('waiting', waiting) state.audiosInstance.event.on('playing', playing) state.audiosInstance.event.on('play', play, false) state.audiosInstance.event.on('pause', pause, false) state.audiosInstance.event.on('ended', ended, false) state.audiosInstance.event.on('updatePlayTime', updatePlayTime, false) listenerMessage('cloudplayed', async () => { await syncPlayState() state.currentTimeNum = 0 state.currentTime = '00:00' state.audiosInstance.event.emit('ended', new Event('ended')) }) listenerMessage('cloudTimeUpdae', (res) => { // console.log('cloudTimeUpdae', res?.content.currentTime) const time = res?.content.currentTime / 1000 // requestAnimationFrame(async () => { // const cloudGetMediaStatus = await promisefiyPostMessage({ // api: 'cloudGetMediaStatus', // content: { // songID: detailState.activeDetail.examSongId, // } // }) // const status = cloudGetMediaStatus?.content.status if (state.playState === 'play') { state.currentTimeNum = time state.currentTime = formatTime(time) refreshPlayer(time) refreshIndex(time) } refreshView() // }) }) state.audiosInstance.event.on('timeupdate', () => { state.currentTimeNum = state.audiosInstance.currentTime state.currentTime = formatTime(state.audiosInstance.currentTime) requestAnimationFrame(() => { if (state.audiosInstance.getStatus() === 'play') { refreshPlayer() } refreshView() }) }) window.addEventListener('resize', windowResize) } export const back = () => { if (state.browser.isApp) { postMessage({ api: 'back', }) } else { // (this as any).$router.replace({ // path: '/', // }) } } export const setStepView = (activeNote: any, time?: number) => { prevIndex = Math.max(activeNote.i, 0) syncStepIndex(activeNote.i) if (time) { // console.log(time) refreshPlayer(time) } refreshView() } export const noteClick = (evt: MouseEvent) => { if (state.isFirstPlay) { Toast('开始播放后才能调整进度') return } let activeNote = getNoteBySlursStart(getActtiveNoteByTimes(evt)) if (activeNote) { const time = activeNote.sourceStartTime || activeNote.time setCurrentTime(time) setStepView(activeNote.i, time) detailState.fixedKey = activeNote.realKey detailState.activeNote = activeNote } } let playStartTime: number = 0 export const startCapture = async () => { if (detailState.activeDetail?.isAppPlay) { playStartTime = new Date().getTime() console.log('startCapture', playStartTime) } // console.log('startCapture:',SettingState.sett.camera, browserInfo.isApp, !state.captureStatus, state.evaluatingStatus, SettingState.eva.save) if ( SettingState.sett.camera && browserInfo.isApp && !state.captureStatus && state.evaluatingStatus && SettingState.eva.save ) { state.captureStatus = true postMessage({ api: 'startCapture', }, () => { postMessage({ api: 'setCaptureMode', content: { mode: 'evaluating', }, }) }) } } export const endCapture = async () => { if (detailState.activeDetail?.isAppPlay) { const playTime = new Date().getTime() - playStartTime if (playStartTime !== 0 && playTime > 0) { state.audiosInstance?.event.emit('updatePlayTime', playTime / 1000) playStartTime = 0 } } if (browserInfo.isApp && state.evaluatingStatus && SettingState.sett.camera && state.captureStatus) { postMessage( { api: 'endCapture', }, () => { state.captureStatus = false } ) evaluatPlayerStop() } } export const setCaptureMode = async () => { if (browserInfo.isApp && SettingState.sett.camera) { postMessage({ api: 'setCaptureMode', content: { mode: state.evaluatingStatus ? 'evaluating' : 'practice', }, }) } }